diff --git a/modules/store/src/test/scala/docspell/store/DatabaseTest.scala b/modules/store/src/test/scala/docspell/store/DatabaseTest.scala index dcd03557..e2e9363e 100644 --- a/modules/store/src/test/scala/docspell/store/DatabaseTest.scala +++ b/modules/store/src/test/scala/docspell/store/DatabaseTest.scala @@ -50,7 +50,7 @@ trait DatabaseTest lazy val h2DataSource = ResourceSuiteLocalFixture( "h2DataSource", { - val jdbc = StoreFixture.memoryDB("test") + val jdbc = StoreFixture.memoryDB(UUID.randomUUID().toString) StoreFixture.dataSource(jdbc).map(ds => (jdbc, ds)) } ) diff --git a/modules/webapp/src/main/elm/Comp/ItemCardList.elm b/modules/webapp/src/main/elm/Comp/ItemCardList.elm index a5577054..f630e00f 100644 --- a/modules/webapp/src/main/elm/Comp/ItemCardList.elm +++ b/modules/webapp/src/main/elm/Comp/ItemCardList.elm @@ -12,6 +12,7 @@ module Comp.ItemCardList exposing , init , nextItem , prevItem + , size , update , updateDrag , view @@ -71,6 +72,11 @@ prevItem model id = |> Util.List.findPrev (\i -> i.id == id) +size : Model -> Int +size model = + Data.Items.length model.results + + --- Update diff --git a/modules/webapp/src/main/elm/Messages/Page/Share.elm b/modules/webapp/src/main/elm/Messages/Page/Share.elm index 7fb86f2f..59acb64d 100644 --- a/modules/webapp/src/main/elm/Messages/Page/Share.elm +++ b/modules/webapp/src/main/elm/Messages/Page/Share.elm @@ -33,6 +33,8 @@ type alias Texts = , listView : String , tileView : String , downloadAllLabel : String + , loadMore : String + , thatsAll : String } @@ -53,6 +55,8 @@ gb tz = , listView = "List view" , tileView = "Tile view" , downloadAllLabel = "Download all" + , loadMore = "Load more…" + , thatsAll = "That's all" } @@ -73,6 +77,8 @@ de tz = , listView = "Listenansicht" , tileView = "Kachelansicht" , downloadAllLabel = "Alles herunterladen" + , loadMore = "Mehr laden…" + , thatsAll = "Mehr gibt es nicht" } @@ -93,4 +99,6 @@ fr tz = , listView = "Affichage liste" , tileView = "Affichage tuile" , downloadAllLabel = "Télécharger tout" + , loadMore = "Charger plus..." + , thatsAll = "C'est tout !" } diff --git a/modules/webapp/src/main/elm/Page/Share/Data.elm b/modules/webapp/src/main/elm/Page/Share/Data.elm index 80aa3457..eae35abc 100644 --- a/modules/webapp/src/main/elm/Page/Share/Data.elm +++ b/modules/webapp/src/main/elm/Page/Share/Data.elm @@ -14,6 +14,7 @@ module Page.Share.Data exposing , TopContentModel(..) , init , initCmd + , pageSizes ) import Api @@ -75,6 +76,9 @@ type alias Model = , showGroups : Bool , arrange : ItemArrange , rowsOpen : Set String + , pageSizeMenuOpen : Bool + , pageSize : Int + , offset : Int } , topContent : TopContentModel } @@ -99,6 +103,9 @@ emptyModel flags = , showGroups = True , arrange = Data.ItemArrange.Cards , rowsOpen = Set.empty + , pageSizeMenuOpen = False + , pageSize = pageSizes flags |> List.head |> Maybe.withDefault 20 + , offset = 0 } , topContent = TopContentHidden } @@ -126,6 +133,7 @@ initCmd shareId flags = type Msg = VerifyResp (Result Http.Error ShareVerifyResult) | SearchResp (Result Http.Error ItemLightList) + | AddSearchResp (Result Http.Error ItemLightList) | StatsResp Bool (Result Http.Error SearchStats) | UiSettingsResp (Result Http.Error UiSettings) | PasswordMsg Comp.SharePasswordForm.Msg @@ -141,3 +149,32 @@ type Msg | ToggleShowGroups | DownloadAllMsg Comp.DownloadAll.Msg | ToggleDownloadAll + | TogglePageSizeMenu + | SetPageSize Int + | LoadNextPage + + +pageSizes : Flags -> List Int +pageSizes flags = + let + maxSize = + flags.config.maxPageSize + + high = + min maxSize 60 // 10 * 10 + + low = + 20 + + gen n res = + if n > high then + res + + else + gen (n + low) (n :: res) + in + if maxSize <= low then + [ maxSize ] + + else + List.reverse (gen low []) diff --git a/modules/webapp/src/main/elm/Page/Share/LoadMore.elm b/modules/webapp/src/main/elm/Page/Share/LoadMore.elm new file mode 100644 index 00000000..5132116d --- /dev/null +++ b/modules/webapp/src/main/elm/Page/Share/LoadMore.elm @@ -0,0 +1,55 @@ +{- + Copyright 2020 Eike K. & Contributors + + SPDX-License-Identifier: AGPL-3.0-or-later +-} + + +module Page.Share.LoadMore exposing (view) + +import Comp.Basic as B +import Comp.ItemCardList +import Html exposing (Html, div) +import Html.Attributes exposing (class, href) +import Html.Events exposing (onClick) +import Messages.Page.Share exposing (Texts) +import Page.Share.Data exposing (Model, Msg(..)) + + +view : Texts -> Model -> Html Msg +view texts model = + let + noMore = + requestedResultSize model > currentResultSize model + in + div [ class "py-8 flex flex-row items-center justify-center" ] + [ B.secondaryBasicButton + { label = + if noMore then + texts.thatsAll + + else + texts.loadMore + , icon = + if model.searchInProgress then + "fa fa-circle-notch animate-spin" + + else + "fa fa-angle-double-down" + , disabled = noMore + , handler = onClick LoadNextPage + , attrs = + [ href "#" + ] + } + ] + + +requestedResultSize : Model -> Int +requestedResultSize model = + model.viewMode.offset + model.viewMode.pageSize + + +currentResultSize : Model -> Int +currentResultSize model = + Comp.ItemCardList.size model.itemListModel diff --git a/modules/webapp/src/main/elm/Page/Share/Menubar.elm b/modules/webapp/src/main/elm/Page/Share/Menubar.elm index d99b6026..74462224 100644 --- a/modules/webapp/src/main/elm/Page/Share/Menubar.elm +++ b/modules/webapp/src/main/elm/Page/Share/Menubar.elm @@ -16,7 +16,7 @@ import Html exposing (..) import Html.Attributes exposing (..) import Html.Events exposing (onClick, onInput) import Messages.Page.Share exposing (Texts) -import Page.Share.Data exposing (Model, Msg(..), SearchBarMode(..)) +import Page.Share.Data exposing (Model, Msg(..), SearchBarMode(..), pageSizes) import Styles as S import Util.Html @@ -95,7 +95,29 @@ view texts flags model = } ] , end = - [ MB.CustomElement <| + [ MB.Dropdown + { linkIcon = "fa fa-caret-down" + , label = String.fromInt model.viewMode.pageSize + , linkClass = + [ ( S.secondaryBasicButton, True ) + ] + , toggleMenu = TogglePageSizeMenu + , menuOpen = model.viewMode.pageSizeMenuOpen + , items = + List.map + (\n -> + { icon = i [] [] + , label = String.fromInt n + , disabled = False + , attrs = + [ href "#" + , onClick (SetPageSize n) + ] + } + ) + (pageSizes flags) + } + , MB.CustomElement <| B.secondaryBasicButton { label = "" , icon = diff --git a/modules/webapp/src/main/elm/Page/Share/Update.elm b/modules/webapp/src/main/elm/Page/Share/Update.elm index c1cc4eaa..12765501 100644 --- a/modules/webapp/src/main/elm/Page/Share/Update.elm +++ b/modules/webapp/src/main/elm/Page/Share/Update.elm @@ -23,7 +23,6 @@ import Page.Share.Data exposing (..) import Process import Set import Task -import Time import Util.Html import Util.Maybe import Util.Update @@ -49,7 +48,7 @@ update flags settings shareId msg model = , searchInProgress = True } , Cmd.batch - [ makeSearchCmd flags True model + [ makeSearchCmd flags True False model , Api.clientSettingsShare flags res.token UiSettingsResp ] ) @@ -82,6 +81,16 @@ update flags settings shareId msg model = SearchResp (Err err) -> noSub ( { model | pageError = PageErrorHttp err, searchInProgress = False }, Cmd.none ) + AddSearchResp (Ok list) -> + update flags + settings + shareId + (ItemListMsg (Comp.ItemCardList.AddResults list)) + { model | searchInProgress = False, pageError = PageErrorNone } + + AddSearchResp (Err err) -> + noSub ( { model | pageError = PageErrorHttp err, searchInProgress = False }, Cmd.none ) + StatsResp doInit (Ok stats) -> let lm = @@ -121,18 +130,27 @@ update flags settings shareId msg model = res = Comp.SearchMenu.update flags settings lm model.searchMenuModel - nextModel = + vm = + model.viewMode + + nextVm = + { vm | offset = 0 } + + nextModel1 = { model | searchMenuModel = res.model } - ( initSearch, searchCmd ) = + nextModelSearch = + { nextModel1 | viewMode = nextVm } + + ( initSearch, searchCmd, model_ ) = if res.stateChange && not model.searchInProgress then - ( True, makeSearchCmd flags False nextModel ) + ( True, makeSearchCmd flags False False nextModelSearch, nextModelSearch ) else - ( False, Cmd.none ) + ( False, Cmd.none, nextModel1 ) in noSub - ( { nextModel | searchInProgress = initSearch } + ( { model_ | searchInProgress = initSearch } , Cmd.batch [ Cmd.map SearchMenuMsg res.cmd, searchCmd ] ) @@ -141,8 +159,14 @@ update flags settings shareId msg model = res = Comp.PowerSearchInput.update lm model.powerSearchInput + vm = + model.viewMode + + nextVm = + { vm | offset = 0 } + nextModel = - { model | powerSearchInput = res.model } + { model | powerSearchInput = res.model, viewMode = nextVm } ( initSearch, searchCmd ) = case res.action of @@ -150,7 +174,7 @@ update flags settings shareId msg model = ( False, Cmd.none ) Comp.PowerSearchInput.SubmitSearch -> - ( True, makeSearchCmd flags False nextModel ) + ( True, makeSearchCmd flags False False nextModel ) in { model = { nextModel | searchInProgress = initSearch } , cmd = Cmd.batch [ Cmd.map PowerSearchMsg res.cmd, searchCmd ] @@ -159,11 +183,18 @@ update flags settings shareId msg model = ResetSearch -> let + vm = + model.viewMode + + nextVm = + { vm | offset = 0 } + nm = { model | powerSearchInput = Comp.PowerSearchInput.init , contentSearch = Nothing , pageError = PageErrorNone + , viewMode = nextVm } in update flags settings shareId (SearchMenuMsg Comp.SearchMenu.ResetForm) nm @@ -215,7 +246,14 @@ update flags settings shareId msg model = noSub ( { model | contentSearch = Util.Maybe.fromString q }, Cmd.none ) ContentSearchKey (Just Util.Html.Enter) -> - noSub ( model, makeSearchCmd flags False model ) + let + vm = + model.viewMode + + nextVm = + { vm | offset = 0 } + in + noSub ( { model | viewMode = nextVm, searchInProgress = True }, makeSearchCmd flags False False model ) ContentSearchKey _ -> noSub ( model, Cmd.none ) @@ -240,6 +278,16 @@ update flags settings shareId msg model = in noSub ( { model | viewMode = next }, Cmd.none ) + TogglePageSizeMenu -> + let + vm = + model.viewMode + + next = + { vm | pageSizeMenuOpen = not vm.pageSizeMenuOpen } + in + noSub ( { model | viewMode = next }, Cmd.none ) + ToggleArrange am -> let vm = @@ -318,6 +366,29 @@ update flags settings shareId msg model = TopContentDownload _ -> noSub ( { model | topContent = TopContentHidden, viewMode = nextVm }, Cmd.none ) + SetPageSize n -> + let + vm = + model.viewMode + + next = + { vm | pageSize = n, pageSizeMenuOpen = False } + in + noSub ( { model | viewMode = next }, Cmd.none ) + + LoadNextPage -> + let + vm = + model.viewMode + + nextVm = + { vm | offset = vm.offset + vm.pageSize } + + nextModel = + { model | viewMode = nextVm, searchInProgress = True } + in + noSub ( nextModel, makeSearchCmd flags False True nextModel ) + noSub : ( Model, Cmd Msg ) -> UpdateResult noSub ( m, c ) = @@ -342,22 +413,30 @@ createQuery flags model = ] -makeSearchCmd : Flags -> Bool -> Model -> Cmd Msg -makeSearchCmd flags doInit model = +makeSearchCmd : Flags -> Bool -> Bool -> Model -> Cmd Msg +makeSearchCmd flags doInit addResults model = let xq = createQuery flags model request mq = - { offset = Nothing - , limit = Nothing + { offset = Just model.viewMode.offset + , limit = Just model.viewMode.pageSize , withDetails = Just True , query = Q.renderMaybe mq , searchMode = Just (Data.SearchMode.asString Data.SearchMode.Normal) } searchCmd = - Api.searchShare flags model.verifyResult.token (request xq) SearchResp + Api.searchShare flags + model.verifyResult.token + (request xq) + (if addResults then + AddSearchResp + + else + SearchResp + ) statsCmd = Api.searchShareStats flags model.verifyResult.token (request xq) (StatsResp doInit) diff --git a/modules/webapp/src/main/elm/Page/Share/View.elm b/modules/webapp/src/main/elm/Page/Share/View.elm index b9b8f238..04d15be5 100644 --- a/modules/webapp/src/main/elm/Page/Share/View.elm +++ b/modules/webapp/src/main/elm/Page/Share/View.elm @@ -16,6 +16,7 @@ import Html exposing (..) import Html.Attributes exposing (..) import Messages.Page.Share exposing (Texts) import Page.Share.Data exposing (..) +import Page.Share.LoadMore as LoadMore import Page.Share.Menubar as Menubar import Page.Share.Results as Results import Page.Share.Sidebar as Sidebar @@ -83,6 +84,7 @@ mainContent texts flags shareId model = , errorMessage texts model , TopContent.view texts flags model , Results.view texts model.uiSettings flags shareId model + , LoadMore.view texts model ]