Add a load-more button to item list

This commit is contained in:
Eike Kettner 2020-06-06 02:08:20 +02:00
parent e5b90eff34
commit b150269528
9 changed files with 251 additions and 16 deletions

View File

@ -57,7 +57,7 @@ init key url flags =
, key = key , key = key
, page = page , page = page
, version = Api.Model.VersionInfo.empty , version = Api.Model.VersionInfo.empty
, homeModel = Page.Home.Data.emptyModel , homeModel = Page.Home.Data.init flags
, loginModel = Page.Login.Data.emptyModel , loginModel = Page.Login.Data.emptyModel
, manageDataModel = Page.ManageData.Data.emptyModel , manageDataModel = Page.ManageData.Data.emptyModel
, collSettingsModel = Page.CollectiveSettings.Data.emptyModel , collSettingsModel = Page.CollectiveSettings.Data.emptyModel

View File

@ -14,9 +14,11 @@ import Api.Model.ItemLightList exposing (ItemLightList)
import Data.Direction import Data.Direction
import Data.Flags exposing (Flags) import Data.Flags exposing (Flags)
import Data.Icons as Icons import Data.Icons as Icons
import Data.Items
import Html exposing (..) import Html exposing (..)
import Html.Attributes exposing (..) import Html.Attributes exposing (..)
import Html.Events exposing (onClick) import Html.Events exposing (onClick)
import Ports
import Util.List import Util.List
import Util.String import Util.String
import Util.Time import Util.Time
@ -29,6 +31,7 @@ type alias Model =
type Msg type Msg
= SetResults ItemLightList = SetResults ItemLightList
| AddResults ItemLightList
| SelectItem ItemLight | SelectItem ItemLight
@ -64,6 +67,28 @@ update _ msg model =
in in
( newModel, Cmd.none, Nothing ) ( 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 -> SelectItem item ->
( model, Cmd.none, Just item ) ( model, Cmd.none, Just item )
@ -123,6 +148,7 @@ viewItem item =
[ ( "ui fluid card", True ) [ ( "ui fluid card", True )
, ( newColor, not isConfirmed ) , ( newColor, not isConfirmed )
] ]
, id item.id
, href "#" , href "#"
, onClick (SelectItem item) , onClick (SelectItem item)
] ]

View File

@ -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

View File

@ -2,13 +2,19 @@ module Page.Home.Data exposing
( Model ( Model
, Msg(..) , Msg(..)
, ViewMode(..) , ViewMode(..)
, emptyModel , doSearchCmd
, init
, itemNav , itemNav
, resultsBelowLimit
, searchLimit
) )
import Api
import Api.Model.ItemLightList exposing (ItemLightList) import Api.Model.ItemLightList exposing (ItemLightList)
import Comp.ItemCardList import Comp.ItemCardList
import Comp.SearchMenu import Comp.SearchMenu
import Data.Flags exposing (Flags)
import Data.Items
import Http import Http
@ -18,16 +24,22 @@ type alias Model =
, searchInProgress : Bool , searchInProgress : Bool
, viewMode : ViewMode , viewMode : ViewMode
, menuCollapsed : Bool , menuCollapsed : Bool
, searchOffset : Int
, moreAvailable : Bool
, moreInProgress : Bool
} }
emptyModel : Model init : Flags -> Model
emptyModel = init _ =
{ searchMenuModel = Comp.SearchMenu.emptyModel { searchMenuModel = Comp.SearchMenu.emptyModel
, itemListModel = Comp.ItemCardList.init , itemListModel = Comp.ItemCardList.init
, searchInProgress = False , searchInProgress = False
, viewMode = Listing , viewMode = Listing
, menuCollapsed = False , menuCollapsed = False
, searchOffset = 0
, moreAvailable = True
, moreInProgress = False
} }
@ -39,6 +51,7 @@ type Msg
| ItemSearchResp (Result Http.Error ItemLightList) | ItemSearchResp (Result Http.Error ItemLightList)
| DoSearch | DoSearch
| ToggleSearchMenu | ToggleSearchMenu
| LoadMore
type ViewMode type ViewMode
@ -58,3 +71,32 @@ itemNav id model =
{ prev = Maybe.map .id prev { prev = Maybe.map .id prev
, next = Maybe.map .id next , 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

View File

@ -1,6 +1,5 @@
module Page.Home.Update exposing (update) module Page.Home.Update exposing (update)
import Api
import Browser.Navigation as Nav import Browser.Navigation as Nav
import Comp.ItemCardList import Comp.ItemCardList
import Comp.SearchMenu import Comp.SearchMenu
@ -21,7 +20,11 @@ update key flags msg model =
model model
ResetSearch -> 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 -> SearchMenuMsg m ->
let let
@ -57,32 +60,71 @@ update key flags msg model =
ItemSearchResp (Ok list) -> ItemSearchResp (Ok list) ->
let let
noff =
model.searchOffset + searchLimit
m = m =
{ model | searchInProgress = False, viewMode = Listing } { model
| searchInProgress = False
, moreInProgress = False
, searchOffset = noff
, viewMode = Listing
, moreAvailable = list.groups /= []
}
in in
if list.groups == [] then
( m, Cmd.none )
else if model.searchOffset == 0 then
update key flags (ItemCardListMsg (Comp.ItemCardList.SetResults list)) m update key flags (ItemCardListMsg (Comp.ItemCardList.SetResults list)) m
else
update key flags (ItemCardListMsg (Comp.ItemCardList.AddResults list)) m
ItemSearchResp (Err _) -> ItemSearchResp (Err _) ->
( { model | searchInProgress = False }, Cmd.none ) ( { model
| searchInProgress = False
}
, Cmd.none
)
DoSearch -> DoSearch ->
doSearch flags model let
nm =
{ model | searchOffset = 0 }
in
doSearch flags nm
ToggleSearchMenu -> ToggleSearchMenu ->
( { model | menuCollapsed = not model.menuCollapsed } ( { model | menuCollapsed = not model.menuCollapsed }
, Cmd.none , Cmd.none
) )
LoadMore ->
if model.moreAvailable then
doSearchMore flags model
else
( model, Cmd.none )
doSearch : Flags -> Model -> ( Model, Cmd Msg ) doSearch : Flags -> Model -> ( Model, Cmd Msg )
doSearch flags model = doSearch flags model =
let let
smask = cmd =
Comp.SearchMenu.getItemSearch model.searchMenuModel doSearchCmd flags model.searchOffset model.searchMenuModel
mask =
{ smask | limit = 100 }
in in
( { model | searchInProgress = True, viewMode = Listing } ( { 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
) )

View File

@ -61,6 +61,7 @@ view model =
, not model.menuCollapsed , not model.menuCollapsed
) )
, ( "sixteen wide column", model.menuCollapsed ) , ( "sixteen wide column", model.menuCollapsed )
, ( "item-card-list", True )
] ]
] ]
[ div [ div
@ -90,6 +91,36 @@ view model =
Detail -> Detail ->
div [] [] 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"
]
]
]
] ]

View File

@ -1,5 +1,6 @@
port module Ports exposing port module Ports exposing
( removeAccount ( removeAccount
, scrollToElem
, setAccount , setAccount
, setAllProgress , setAllProgress
, setProgress , setProgress
@ -18,3 +19,6 @@ port setProgress : ( String, Int ) -> Cmd msg
port setAllProgress : ( String, Int ) -> Cmd msg port setAllProgress : ( String, Int ) -> Cmd msg
port scrollToElem : String -> Cmd msg

View File

@ -1,5 +1,6 @@
module Util.List exposing module Util.List exposing
( distinct ( distinct
, dropRight
, find , find
, findIndexed , findIndexed
, findNext , findNext
@ -80,3 +81,10 @@ findNext pred list =
|> Maybe.map Tuple.second |> Maybe.map Tuple.second
|> Maybe.map (\i -> i + 1) |> Maybe.map (\i -> i + 1)
|> Maybe.andThen (get list) |> Maybe.andThen (get list)
dropRight : Int -> List a -> List a
dropRight n list =
List.reverse list
|> List.drop n
|> List.reverse

View File

@ -30,3 +30,18 @@ elmApp.ports.setAllProgress.subscribe(function(input) {
$("."+id).progress({percent: percent}); $("."+id).progress({percent: percent});
}, 100); }, 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);
}
});