From ff42a2f8b59287707042461aa1ca1347fb8250eb Mon Sep 17 00:00:00 2001 From: Chris Waldon Date: Tue, 14 Sep 2021 15:05:46 -0400 Subject: [PATCH] list: update Loader to return if more elements This commit changes the signature of the Loader hook so that it also returns whether there are more elements in a given direction. This greatly simplifies some of the state management logic, and actually eliminates several spurious invocations of the Loader hook. Signed-off-by: Chris Waldon --- list/async.go | 5 +++-- list/async_test.go | 21 +++++++++++++++------ list/element.go | 11 ++++++----- list/manager.go | 4 ++-- list/manager_test.go | 11 +++++------ 5 files changed, 31 insertions(+), 21 deletions(-) diff --git a/list/async.go b/list/async.go index 863a4dd..8cd3dd0 100644 --- a/list/async.go +++ b/list/async.go @@ -125,10 +125,11 @@ func asyncProcess(maxSize int, hooks Hooks) (chan<- interface{}, chan viewport, loadSerial = synthesis.SerialAt(len(synthesis.Source) - 1) } // Load new elements. - newElems = append(newElems, hooks.Loader(req.Direction, loadSerial)...) + var more bool + newElems, more = hooks.Loader(req.Direction, loadSerial) // Track whether all new elements in a given direction have been // exhausted. - if len(newElems) == 0 { + if len(newElems) == 0 || !more { ignore.Add(req.Direction) } else { ignore = NoDirection diff --git a/list/async_test.go b/list/async_test.go index d847f01..7d74897 100644 --- a/list/async_test.go +++ b/list/async_test.go @@ -20,14 +20,15 @@ var testElements = func() []Element { func TestAsyncProcess(t *testing.T) { var nextLoad []Element + var more bool var loadInvoked bool hooks := Hooks{ Invalidator: func() {}, Comparator: testComparator, Synthesizer: testSynthesizer, - Loader: func(dir Direction, rt Serial) []Element { + Loader: func(dir Direction, rt Serial) ([]Element, bool) { loadInvoked = true - return nextLoad + return nextLoad, more }, } size := 6 @@ -40,6 +41,9 @@ func TestAsyncProcess(t *testing.T) { input loadRequest // the data that will be returned by the data request (if the loader is executed) load []Element + // whether the async logic should expect additional content in the direction of + // the load. + loadMore bool // should the testcase block waiting for an update on the update channel skipUpdate bool // the update to expect on the update channel @@ -60,7 +64,8 @@ func TestAsyncProcess(t *testing.T) { }, Direction: Before, }, - load: testElements[7:], + load: testElements[7:], + loadMore: true, expected: stateUpdate{ Synthesis: Synthesis{ Elements: testElements[7:], @@ -119,7 +124,8 @@ func TestAsyncProcess(t *testing.T) { }, Direction: Before, }, - load: testElements[4:7], + load: testElements[4:7], + loadMore: true, expected: stateUpdate{ Synthesis: Synthesis{ Elements: testElements[4:], @@ -172,7 +178,8 @@ func TestAsyncProcess(t *testing.T) { }, Direction: Before, }, - load: testElements[1:4], + load: testElements[1:4], + loadMore: true, expected: stateUpdate{ Synthesis: Synthesis{ Elements: testElements[3:9], @@ -201,7 +208,8 @@ func TestAsyncProcess(t *testing.T) { }, Direction: Before, }, - load: testElements[:3], + load: testElements[:3], + loadMore: true, expected: stateUpdate{ Synthesis: Synthesis{ Elements: testElements[2:8], @@ -267,6 +275,7 @@ func TestAsyncProcess(t *testing.T) { // ensure that the next invocation of the loader will load this // testcase's data payload. nextLoad = tc.load + more = tc.loadMore // request a load reqs <- tc.input diff --git a/list/element.go b/list/element.go index 8648d62..732a221 100644 --- a/list/element.go +++ b/list/element.go @@ -58,14 +58,15 @@ type Synthesizer func(previous, current, next Element) []Element type Comparator func(a, b Element) bool // Loader is a function that can fulfill load requests. If it returns -// a response with no elements in a given direction, the manager will not +// a response with no elements in a given direction or false as its +// second return value, the manager will not // invoke the loader in that direction again until the manager loads // data from the other end of the list or another manger state update // occurs. // // Loader implements pull modifications. When the manager wants more data it // will invoke the Loader hook to get more. -type Loader func(direction Direction, relativeTo Serial) []Element +type Loader func(direction Direction, relativeTo Serial) (elems []Element, more bool) // Presenter is a function that can transform the data for an Element // into a widget to be laid out in the user interface. It must not return @@ -121,11 +122,11 @@ func DefaultHooks(w *app.Window, th *material.Theme) Hooks { Comparator: func(a, b Element) bool { return string(a.Serial()) < string(b.Serial()) }, - Loader: func(dir Direction, relativeTo Serial) []Element { + Loader: func(dir Direction, relativeTo Serial) ([]Element, bool) { if relativeTo == NoSerial { - return newDefaultElements() + return newDefaultElements(), false } - return nil + return nil, false }, Presenter: func(elem Element, state interface{}) layout.Widget { return material.H4(th, "Implement list.Hooks to change me.").Layout diff --git a/list/manager.go b/list/manager.go index a0be92a..7180ea2 100644 --- a/list/manager.go +++ b/list/manager.go @@ -333,8 +333,8 @@ func (m *Manager) UpdatedLen(list *layout.List) int { // the list's position. firstElementVisible := list.Position.First == 0 lastElementVisible := list.Position.First+list.Position.Count == len(m.elements.Elements) - stickToEnd := lastElementVisible && m.Stickiness.Contains(After) && m.ignoring.Contains(After) - stickToBeginning := firstElementVisible && m.Stickiness.Contains(Before) && m.ignoring.Contains(Before) + stickToEnd := lastElementVisible && m.Stickiness.Contains(After) && (m.ignoring.Contains(After) || su.Type == push) + stickToBeginning := firstElementVisible && m.Stickiness.Contains(Before) && (m.ignoring.Contains(Before) || su.Type == push) if !stickToBeginning { // Update the list position to match the new set of elements. diff --git a/list/manager_test.go b/list/manager_test.go index f54c235..c7ca282 100644 --- a/list/manager_test.go +++ b/list/manager_test.go @@ -67,8 +67,8 @@ func TestManager(t *testing.T) { Height: unit.Dp(5), }.Layout }, - Loader: func(dir Direction, relativeTo Serial) []Element { - return nil + Loader: func(dir Direction, relativeTo Serial) ([]Element, bool) { + return nil, false }, Invalidator: func() {}, Comparator: func(a, b Element) bool { return true }, @@ -381,7 +381,6 @@ func TestManagerPrefetch(t *testing.T) { } default: } - }) } } @@ -420,7 +419,7 @@ func TestManagerViewportOnRemoval(t *testing.T) { Height: unit.Dp(5), }.Layout }, - Loader: func(dir Direction, relativeTo Serial) []Element { return nil }, + Loader: func(dir Direction, relativeTo Serial) ([]Element, bool) { return nil, false }, Invalidator: func() {}, Comparator: func(a, b Element) bool { return true }, Synthesizer: synth, @@ -559,8 +558,8 @@ var testHooks = Hooks{ Height: unit.Dp(5), }.Layout }, - Loader: func(dir Direction, relativeTo Serial) []Element { - return nil + Loader: func(dir Direction, relativeTo Serial) ([]Element, bool) { + return nil, false }, Invalidator: func() {}, Comparator: func(a, b Element) bool { return true }, -- 2.34.2