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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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