From b8a24ba57c09e2cef33929a7edea5e1bb4994faf Mon Sep 17 00:00:00 2001 From: trett Date: Wed, 25 Feb 2026 19:05:05 +0100 Subject: [PATCH 1/5] fix(client): hide 'More News' button when no more news available --- client/src/main/scala/client/Home.scala | 11 ++++++++++- client/src/main/scala/client/Models.scala | 2 ++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/client/src/main/scala/client/Home.scala b/client/src/main/scala/client/Home.scala index d88ca4d..9ae29f7 100644 --- a/client/src/main/scala/client/Home.scala +++ b/client/src/main/scala/client/Home.scala @@ -47,6 +47,10 @@ object Home: private val feedsObserver = feedVar.updater[FeedItemList]((xs1, xs2) => (xs1 ++: xs2).distinctBy(_.link)) + private val hasMoreObserver = Observer[FeedItemList] { xs => + hasMoreVar.set(xs.size == pageLimit) + } + private val unreadCountObserver = Observer[Try[Int]] { case Success(count) => unreadCountVar.set(count) case Failure(err) => handleError(err) @@ -62,6 +66,7 @@ object Home: val data = response.collectSuccess val errors = response.collectFailure data.addObserver(feedsObserver)(ctx.owner) + data.addObserver(hasMoreObserver)(ctx.owner) errors.addObserver(errorObserver)(ctx.owner) } ), @@ -96,7 +101,10 @@ object Home: _.icon := IconName.download, "More News", onClick.mapTo(feedVar.now().size / pageLimit + 1) --> Home.refreshFeedsBus, - hidden <-- feedVar.signal.map(xs => xs.isEmpty) + hidden <-- Signal.combine(feedVar.signal, hasMoreVar.signal).map { + (xs, hasMore) => + xs.isEmpty || !hasMore + } ) ) ) @@ -110,6 +118,7 @@ object Home: _.noDataText := "Nothing to read", children <-- feedSignal.split(_.link)(renderItem), data --> feedsObserver, + data --> hasMoreObserver, errors --> errorObserver, unreadCountResponse --> unreadCountObserver ) diff --git a/client/src/main/scala/client/Models.scala b/client/src/main/scala/client/Models.scala index f980498..5c27414 100644 --- a/client/src/main/scala/client/Models.scala +++ b/client/src/main/scala/client/Models.scala @@ -47,7 +47,9 @@ final class Model: val channelVar: Var[ChannelList] = Var(List()) val settingsVar: Var[Option[UserSettings]] = Var(Option.empty) val unreadCountVar: Var[Int] = Var(0) + val hasMoreVar: Var[Boolean] = Var(true) val feedSignal: StrictSignal[FeedItemList] = feedVar.signal val channelSignal: StrictSignal[ChannelList] = channelVar.signal val settingsSignal: StrictSignal[Option[UserSettings]] = settingsVar.signal val unreadCountSignal: StrictSignal[Int] = unreadCountVar.signal + val hasMoreSignal: StrictSignal[Boolean] = hasMoreVar.signal From 1d6127164d48e2394414109ba272d7852e9cf1ec Mon Sep 17 00:00:00 2001 From: trett Date: Wed, 25 Feb 2026 19:17:18 +0100 Subject: [PATCH 2/5] refactor(client): use signals and combineWith for 'More News' button visibility --- client/src/main/scala/client/Home.scala | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/client/src/main/scala/client/Home.scala b/client/src/main/scala/client/Home.scala index 9ae29f7..694eb84 100644 --- a/client/src/main/scala/client/Home.scala +++ b/client/src/main/scala/client/Home.scala @@ -101,9 +101,8 @@ object Home: _.icon := IconName.download, "More News", onClick.mapTo(feedVar.now().size / pageLimit + 1) --> Home.refreshFeedsBus, - hidden <-- Signal.combine(feedVar.signal, hasMoreVar.signal).map { - (xs, hasMore) => - xs.isEmpty || !hasMore + hidden <-- feedSignal.combineWith(hasMoreSignal).map { case (feeds, hasMore) => + feeds.isEmpty || !hasMore } ) ) From 16a8523b9ba411cd6faccb41ea583cc7f46f5cae Mon Sep 17 00:00:00 2001 From: trett Date: Wed, 25 Feb 2026 19:50:50 +0100 Subject: [PATCH 3/5] fix(client): improve pagination logic to avoid infinite loops --- client/src/main/scala/client/Home.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/src/main/scala/client/Home.scala b/client/src/main/scala/client/Home.scala index 694eb84..da842fc 100644 --- a/client/src/main/scala/client/Home.scala +++ b/client/src/main/scala/client/Home.scala @@ -100,7 +100,9 @@ object Home: _.design := ButtonDesign.Transparent, _.icon := IconName.download, "More News", - onClick.mapTo(feedVar.now().size / pageLimit + 1) --> Home.refreshFeedsBus, + onClick.mapTo( + (feedVar.now().size + pageLimit - 1) / pageLimit + 1 + ) --> Home.refreshFeedsBus, hidden <-- feedSignal.combineWith(hasMoreSignal).map { case (feeds, hasMore) => feeds.isEmpty || !hasMore } From 30f03ac51365afcbf62416493550c2ed174a9dfa Mon Sep 17 00:00:00 2001 From: trett Date: Wed, 25 Feb 2026 19:58:20 +0100 Subject: [PATCH 4/5] refactor(client): extract feed observation logic to helper method --- client/src/main/scala/client/Home.scala | 26 ++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/client/src/main/scala/client/Home.scala b/client/src/main/scala/client/Home.scala index da842fc..964974a 100644 --- a/client/src/main/scala/client/Home.scala +++ b/client/src/main/scala/client/Home.scala @@ -56,18 +56,20 @@ object Home: case Failure(err) => handleError(err) } + private def bindFeeds(stream: EventStream[Try[FeedItemList]], owner: Owner): Unit = + val data = stream.collectSuccess + val errors = stream.collectFailure + data.addObserver(feedsObserver)(owner) + data.addObserver(hasMoreObserver)(owner) + errors.addObserver(errorObserver)(owner) + def render: Element = div( - cls := "cards main-content", + cls := "main-content", div( onMountBind(ctx => refreshFeedsBus --> { page => - val response = getChannelsAndFeedsRequest(page) - val data = response.collectSuccess - val errors = response.collectFailure - data.addObserver(feedsObserver)(ctx.owner) - data.addObserver(hasMoreObserver)(ctx.owner) - errors.addObserver(errorObserver)(ctx.owner) + bindFeeds(getChannelsAndFeedsRequest(page), ctx.owner) } ), div( @@ -111,16 +113,14 @@ object Home: ) private def feeds(): Element = - val response = getChannelsAndFeedsRequest(1) - val data = response.collectSuccess - val errors = response.collectFailure + val stream = getChannelsAndFeedsRequest(1) val unreadCountResponse = getUnreadCountRequest() UList( + onMountBind(ctx => + stream --> (tryFeeds => bindFeeds(EventStream.fromValue(tryFeeds), ctx.owner)) + ), _.noDataText := "Nothing to read", children <-- feedSignal.split(_.link)(renderItem), - data --> feedsObserver, - data --> hasMoreObserver, - errors --> errorObserver, unreadCountResponse --> unreadCountObserver ) From 8f87ebad380c168d6d7ebc2c0bf37f4a67bb9668 Mon Sep 17 00:00:00 2001 From: trett Date: Wed, 25 Feb 2026 20:05:21 +0100 Subject: [PATCH 5/5] refactor(client): simplify component mount logic in feeds method --- client/src/main/scala/client/Home.scala | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/client/src/main/scala/client/Home.scala b/client/src/main/scala/client/Home.scala index 964974a..e917f00 100644 --- a/client/src/main/scala/client/Home.scala +++ b/client/src/main/scala/client/Home.scala @@ -116,9 +116,7 @@ object Home: val stream = getChannelsAndFeedsRequest(1) val unreadCountResponse = getUnreadCountRequest() UList( - onMountBind(ctx => - stream --> (tryFeeds => bindFeeds(EventStream.fromValue(tryFeeds), ctx.owner)) - ), + onMountCallback(ctx => bindFeeds(stream, ctx.owner)), _.noDataText := "Nothing to read", children <-- feedSignal.split(_.link)(renderItem), unreadCountResponse --> unreadCountObserver