mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-06-02 13:32:51 +00:00
Add a load-more button to item list
This commit is contained in:
parent
e5b90eff34
commit
b150269528
@ -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
|
||||
|
@ -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)
|
||||
]
|
||||
|
67
modules/webapp/src/main/elm/Data/Items.elm
Normal file
67
modules/webapp/src/main/elm/Data/Items.elm
Normal 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
|
@ -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
|
||||
|
@ -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
|
||||
)
|
||||
|
@ -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"
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user