From c76644511f0d4c4b8f115bd9d3c9c70373d7eabf Mon Sep 17 00:00:00 2001
From: eikek <eike.kettner@posteo.de>
Date: Sat, 11 Jun 2022 00:34:55 +0200
Subject: [PATCH 1/2] Add paging to share view

---
 .../webapp/src/main/elm/Comp/ItemCardList.elm |   6 +
 .../src/main/elm/Messages/Page/Share.elm      |   8 ++
 .../webapp/src/main/elm/Page/Share/Data.elm   |  37 ++++++
 .../src/main/elm/Page/Share/LoadMore.elm      |  55 +++++++++
 .../src/main/elm/Page/Share/Menubar.elm       |  26 ++++-
 .../webapp/src/main/elm/Page/Share/Update.elm | 109 +++++++++++++++---
 .../webapp/src/main/elm/Page/Share/View.elm   |   2 +
 7 files changed, 226 insertions(+), 17 deletions(-)
 create mode 100644 modules/webapp/src/main/elm/Page/Share/LoadMore.elm

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
         ]
 
 

From 4bca6031aff14cd2599d888fc25246c97296f185 Mon Sep 17 00:00:00 2001
From: eikek <eike.kettner@posteo.de>
Date: Sat, 11 Jun 2022 01:02:22 +0200
Subject: [PATCH 2/2] Fix flaky db test

---
 modules/store/src/test/scala/docspell/store/DatabaseTest.scala | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

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))
     }
   )