From b1502695281b85690b8c3a3dad70fe53673309f4 Mon Sep 17 00:00:00 2001 From: Eike Kettner Date: Sat, 6 Jun 2020 02:08:20 +0200 Subject: [PATCH] Add a load-more button to item list --- modules/webapp/src/main/elm/App/Data.elm | 2 +- .../webapp/src/main/elm/Comp/ItemCardList.elm | 26 +++++++ modules/webapp/src/main/elm/Data/Items.elm | 67 +++++++++++++++++++ .../webapp/src/main/elm/Page/Home/Data.elm | 48 ++++++++++++- .../webapp/src/main/elm/Page/Home/Update.elm | 66 ++++++++++++++---- .../webapp/src/main/elm/Page/Home/View.elm | 31 +++++++++ modules/webapp/src/main/elm/Ports.elm | 4 ++ modules/webapp/src/main/elm/Util/List.elm | 8 +++ modules/webapp/src/main/webjar/docspell.js | 15 +++++ 9 files changed, 251 insertions(+), 16 deletions(-) create mode 100644 modules/webapp/src/main/elm/Data/Items.elm diff --git a/modules/webapp/src/main/elm/App/Data.elm b/modules/webapp/src/main/elm/App/Data.elm index d57fdef5..01aacda2 100644 --- a/modules/webapp/src/main/elm/App/Data.elm +++ b/modules/webapp/src/main/elm/App/Data.elm @@ -57,7 +57,7 @@ init key url flags = , key = key , page = page , version = Api.Model.VersionInfo.empty - , homeModel = Page.Home.Data.emptyModel + , homeModel = Page.Home.Data.init flags , loginModel = Page.Login.Data.emptyModel , manageDataModel = Page.ManageData.Data.emptyModel , collSettingsModel = Page.CollectiveSettings.Data.emptyModel diff --git a/modules/webapp/src/main/elm/Comp/ItemCardList.elm b/modules/webapp/src/main/elm/Comp/ItemCardList.elm index 5518b645..fbbbece4 100644 --- a/modules/webapp/src/main/elm/Comp/ItemCardList.elm +++ b/modules/webapp/src/main/elm/Comp/ItemCardList.elm @@ -14,9 +14,11 @@ import Api.Model.ItemLightList exposing (ItemLightList) import Data.Direction import Data.Flags exposing (Flags) import Data.Icons as Icons +import Data.Items import Html exposing (..) import Html.Attributes exposing (..) import Html.Events exposing (onClick) +import Ports import Util.List import Util.String import Util.Time @@ -29,6 +31,7 @@ type alias Model = type Msg = SetResults ItemLightList + | AddResults ItemLightList | SelectItem ItemLight @@ -64,6 +67,28 @@ update _ msg model = in ( newModel, Cmd.none, Nothing ) + AddResults list -> + if list.groups == [] then + ( model, Cmd.none, Nothing ) + + else + let + firstNew = + Data.Items.first list + + scrollCmd = + case firstNew of + Just item -> + Ports.scrollToElem item.id + + Nothing -> + Cmd.none + + newModel = + { model | results = Data.Items.concat model.results list } + in + ( newModel, scrollCmd, Nothing ) + SelectItem item -> ( model, Cmd.none, Just item ) @@ -123,6 +148,7 @@ viewItem item = [ ( "ui fluid card", True ) , ( newColor, not isConfirmed ) ] + , id item.id , href "#" , onClick (SelectItem item) ] diff --git a/modules/webapp/src/main/elm/Data/Items.elm b/modules/webapp/src/main/elm/Data/Items.elm new file mode 100644 index 00000000..04b9aaad --- /dev/null +++ b/modules/webapp/src/main/elm/Data/Items.elm @@ -0,0 +1,67 @@ +module Data.Items exposing + ( concat + , first + , length + ) + +import Api.Model.ItemLight exposing (ItemLight) +import Api.Model.ItemLightGroup exposing (ItemLightGroup) +import Api.Model.ItemLightList exposing (ItemLightList) +import Util.List + + +concat : ItemLightList -> ItemLightList -> ItemLightList +concat l0 l1 = + let + lastOld = + lastGroup l0 + + firstNew = + List.head l1.groups + in + case ( lastOld, firstNew ) of + ( Nothing, Nothing ) -> + l0 + + ( Just _, Nothing ) -> + l0 + + ( Nothing, Just _ ) -> + l1 + + ( Just o, Just n ) -> + if o.name == n.name then + let + ng = + ItemLightGroup o.name (o.items ++ n.items) + + prev = + Util.List.dropRight 1 l0.groups + + suff = + List.drop 1 l1.groups + in + ItemLightList (prev ++ [ ng ] ++ suff) + + else + ItemLightList (l0.groups ++ l1.groups) + + +first : ItemLightList -> Maybe ItemLight +first list = + List.head list.groups + |> Maybe.map .items + |> Maybe.withDefault [] + |> List.head + + +length : ItemLightList -> Int +length list = + List.map (\g -> List.length g.items) list.groups + |> List.sum + + +lastGroup : ItemLightList -> Maybe ItemLightGroup +lastGroup list = + List.reverse list.groups + |> List.head diff --git a/modules/webapp/src/main/elm/Page/Home/Data.elm b/modules/webapp/src/main/elm/Page/Home/Data.elm index 2ca78dbb..baab5e9a 100644 --- a/modules/webapp/src/main/elm/Page/Home/Data.elm +++ b/modules/webapp/src/main/elm/Page/Home/Data.elm @@ -2,13 +2,19 @@ module Page.Home.Data exposing ( Model , Msg(..) , ViewMode(..) - , emptyModel + , doSearchCmd + , init , itemNav + , resultsBelowLimit + , searchLimit ) +import Api import Api.Model.ItemLightList exposing (ItemLightList) import Comp.ItemCardList import Comp.SearchMenu +import Data.Flags exposing (Flags) +import Data.Items import Http @@ -18,16 +24,22 @@ type alias Model = , searchInProgress : Bool , viewMode : ViewMode , menuCollapsed : Bool + , searchOffset : Int + , moreAvailable : Bool + , moreInProgress : Bool } -emptyModel : Model -emptyModel = +init : Flags -> Model +init _ = { searchMenuModel = Comp.SearchMenu.emptyModel , itemListModel = Comp.ItemCardList.init , searchInProgress = False , viewMode = Listing , menuCollapsed = False + , searchOffset = 0 + , moreAvailable = True + , moreInProgress = False } @@ -39,6 +51,7 @@ type Msg | ItemSearchResp (Result Http.Error ItemLightList) | DoSearch | ToggleSearchMenu + | LoadMore type ViewMode @@ -58,3 +71,32 @@ itemNav id model = { prev = Maybe.map .id prev , next = Maybe.map .id next } + + +searchLimit : Int +searchLimit = + 90 + + +doSearchCmd : Flags -> Int -> Comp.SearchMenu.Model -> Cmd Msg +doSearchCmd flags offset model = + let + smask = + Comp.SearchMenu.getItemSearch model + + mask = + { smask + | limit = searchLimit + , offset = offset + } + in + Api.itemSearch flags mask ItemSearchResp + + +resultsBelowLimit : Model -> Bool +resultsBelowLimit model = + let + len = + Data.Items.length model.itemListModel.results + in + len < searchLimit diff --git a/modules/webapp/src/main/elm/Page/Home/Update.elm b/modules/webapp/src/main/elm/Page/Home/Update.elm index 05c8d850..92a5d600 100644 --- a/modules/webapp/src/main/elm/Page/Home/Update.elm +++ b/modules/webapp/src/main/elm/Page/Home/Update.elm @@ -1,6 +1,5 @@ module Page.Home.Update exposing (update) -import Api import Browser.Navigation as Nav import Comp.ItemCardList import Comp.SearchMenu @@ -21,7 +20,11 @@ update key flags msg model = model ResetSearch -> - update key flags (SearchMenuMsg Comp.SearchMenu.ResetForm) model + let + nm = + { model | searchOffset = 0 } + in + update key flags (SearchMenuMsg Comp.SearchMenu.ResetForm) nm SearchMenuMsg m -> let @@ -57,32 +60,71 @@ update key flags msg model = ItemSearchResp (Ok list) -> let + noff = + model.searchOffset + searchLimit + m = - { model | searchInProgress = False, viewMode = Listing } + { model + | searchInProgress = False + , moreInProgress = False + , searchOffset = noff + , viewMode = Listing + , moreAvailable = list.groups /= [] + } in - update key flags (ItemCardListMsg (Comp.ItemCardList.SetResults list)) m + if list.groups == [] then + ( m, Cmd.none ) + + else if model.searchOffset == 0 then + update key flags (ItemCardListMsg (Comp.ItemCardList.SetResults list)) m + + else + update key flags (ItemCardListMsg (Comp.ItemCardList.AddResults list)) m ItemSearchResp (Err _) -> - ( { model | searchInProgress = False }, Cmd.none ) + ( { model + | searchInProgress = False + } + , Cmd.none + ) DoSearch -> - doSearch flags model + let + nm = + { model | searchOffset = 0 } + in + doSearch flags nm ToggleSearchMenu -> ( { model | menuCollapsed = not model.menuCollapsed } , Cmd.none ) + LoadMore -> + if model.moreAvailable then + doSearchMore flags model + + else + ( model, Cmd.none ) + doSearch : Flags -> Model -> ( Model, Cmd Msg ) doSearch flags model = let - smask = - Comp.SearchMenu.getItemSearch model.searchMenuModel - - mask = - { smask | limit = 100 } + cmd = + doSearchCmd flags model.searchOffset model.searchMenuModel in ( { model | searchInProgress = True, viewMode = Listing } - , Api.itemSearch flags mask ItemSearchResp + , cmd + ) + + +doSearchMore : Flags -> Model -> ( Model, Cmd Msg ) +doSearchMore flags model = + let + cmd = + doSearchCmd flags model.searchOffset model.searchMenuModel + in + ( { model | moreInProgress = True, viewMode = Listing } + , cmd ) diff --git a/modules/webapp/src/main/elm/Page/Home/View.elm b/modules/webapp/src/main/elm/Page/Home/View.elm index dbc89548..29fe81ff 100644 --- a/modules/webapp/src/main/elm/Page/Home/View.elm +++ b/modules/webapp/src/main/elm/Page/Home/View.elm @@ -61,6 +61,7 @@ view model = , not model.menuCollapsed ) , ( "sixteen wide column", model.menuCollapsed ) + , ( "item-card-list", True ) ] ] [ div @@ -90,6 +91,36 @@ view model = Detail -> div [] [] ] + , div + [ classList + [ ( "sixteen wide column", True ) + ] + ] + [ div [ class "ui basic center aligned segment" ] + [ button + [ classList + [ ( "ui basic tiny button", True ) + , ( "disabled", not model.moreAvailable ) + , ( "hidden invisible", resultsBelowLimit model ) + ] + , disabled (not model.moreAvailable || model.moreInProgress || model.searchInProgress) + , title "Load more items" + , href "#" + , onClick LoadMore + ] + [ if model.moreInProgress then + i [ class "loading spinner icon" ] [] + + else + i [ class "angle double down icon" ] [] + , if model.moreAvailable then + text "Load more…" + + else + text "That's all" + ] + ] + ] ] diff --git a/modules/webapp/src/main/elm/Ports.elm b/modules/webapp/src/main/elm/Ports.elm index 94b3b8e1..100852c3 100644 --- a/modules/webapp/src/main/elm/Ports.elm +++ b/modules/webapp/src/main/elm/Ports.elm @@ -1,5 +1,6 @@ port module Ports exposing ( removeAccount + , scrollToElem , setAccount , setAllProgress , setProgress @@ -18,3 +19,6 @@ port setProgress : ( String, Int ) -> Cmd msg port setAllProgress : ( String, Int ) -> Cmd msg + + +port scrollToElem : String -> Cmd msg diff --git a/modules/webapp/src/main/elm/Util/List.elm b/modules/webapp/src/main/elm/Util/List.elm index fca6efce..c7df91ca 100644 --- a/modules/webapp/src/main/elm/Util/List.elm +++ b/modules/webapp/src/main/elm/Util/List.elm @@ -1,5 +1,6 @@ module Util.List exposing ( distinct + , dropRight , find , findIndexed , findNext @@ -80,3 +81,10 @@ findNext pred list = |> Maybe.map Tuple.second |> Maybe.map (\i -> i + 1) |> Maybe.andThen (get list) + + +dropRight : Int -> List a -> List a +dropRight n list = + List.reverse list + |> List.drop n + |> List.reverse diff --git a/modules/webapp/src/main/webjar/docspell.js b/modules/webapp/src/main/webjar/docspell.js index 9bea1f26..162c5b36 100644 --- a/modules/webapp/src/main/webjar/docspell.js +++ b/modules/webapp/src/main/webjar/docspell.js @@ -30,3 +30,18 @@ elmApp.ports.setAllProgress.subscribe(function(input) { $("."+id).progress({percent: percent}); }, 100); }); + +elmApp.ports.scrollToElem.subscribe(function(id) { + if (id && id != "") { + window.setTimeout(function() { + var el = document.getElementById(id); + if (el) { + if (el["scrollIntoViewIfNeeded"]) { + el.scrollIntoViewIfNeeded(); + } else { + el.scrollIntoView(); + } + } + }, 20); + } +});