feat: Extract Card interface and CardFeedState to commons (B1)

- Create commons/ui/notifications/CardFeedState.kt with:
  - Card interface (createdAt, id)
  - CardFeedState sealed class (Loading, Loaded, Empty, FeedError)
  - DefaultCardComparator for sorting

- Update Android imports to use commons Card/CardFeedState:
  - DefaultFeedOrder.kt
  - AccountViewModel.kt
  - CardFeedContentState.kt
  - CardFeedState.kt (now only contains concrete card types)
  - CardFeedView.kt

Part of Phase B (Notifications Feed Extraction) for #1648

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
nrobi144
2026-01-12 06:34:14 +02:00
co-authored by Claude Opus 4.5
parent 45207857c6
commit ec629ac3fe
6 changed files with 85 additions and 28 deletions
@@ -20,8 +20,8 @@
*/
package com.vitorpamplona.amethyst.ui.dal
import com.vitorpamplona.amethyst.commons.ui.notifications.Card
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.Card
val DefaultFeedOrder: Comparator<Note> =
compareByDescending<Note> { it.createdAt() }.thenBy { it.idHex }
@@ -42,6 +42,7 @@ import com.vitorpamplona.amethyst.commons.compose.GenericBaseCache
import com.vitorpamplona.amethyst.commons.compose.GenericBaseCacheAsync
import com.vitorpamplona.amethyst.commons.model.LiveHiddenUsers
import com.vitorpamplona.amethyst.commons.ui.feeds.FeedState
import com.vitorpamplona.amethyst.commons.ui.notifications.CardFeedState
import com.vitorpamplona.amethyst.logTime
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.AccountSettings
@@ -82,7 +83,6 @@ import com.vitorpamplona.amethyst.ui.note.ZapraiserStatus
import com.vitorpamplona.amethyst.ui.note.showAmount
import com.vitorpamplona.amethyst.ui.note.showAmountInteger
import com.vitorpamplona.amethyst.ui.screen.UiSettingsState
import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.CardFeedState
import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.CombinedZap
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.tor.TorSettingsFlow
@@ -26,6 +26,8 @@ import androidx.compose.runtime.Stable
import androidx.compose.runtime.mutableStateOf
import com.vitorpamplona.amethyst.commons.ui.feeds.InvalidatableContent
import com.vitorpamplona.amethyst.commons.ui.feeds.LoadedFeedState
import com.vitorpamplona.amethyst.commons.ui.notifications.Card
import com.vitorpamplona.amethyst.commons.ui.notifications.CardFeedState
import com.vitorpamplona.amethyst.logTime
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.LocalCache
@@ -21,8 +21,7 @@
package com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable
import com.vitorpamplona.amethyst.commons.ui.feeds.LoadedFeedState
import com.vitorpamplona.amethyst.commons.ui.notifications.Card
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.service.firstFullCharOrEmoji
@@ -30,14 +29,6 @@ import com.vitorpamplona.quartz.nip01Core.core.ImmutableListOfLists
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
import kotlinx.collections.immutable.toImmutableMap
import kotlinx.coroutines.flow.MutableStateFlow
@Immutable
interface Card {
fun createdAt(): Long
fun id(): String
}
@Immutable
class BadgeCard(
@@ -113,19 +104,3 @@ class MessageSetCard(
override fun id() = note.idHex
}
@Immutable
sealed class CardFeedState {
@Immutable object Loading : CardFeedState()
@Stable
class Loaded(
val feed: MutableStateFlow<LoadedFeedState<Card>>,
) : CardFeedState()
@Immutable object Empty : CardFeedState()
@Immutable class FeedError(
val errorMessage: String,
) : CardFeedState()
}
@@ -43,6 +43,8 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.commons.ui.notifications.Card
import com.vitorpamplona.amethyst.commons.ui.notifications.CardFeedState
import com.vitorpamplona.amethyst.logTime
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
import com.vitorpamplona.amethyst.ui.feeds.FeedError
@@ -0,0 +1,78 @@
/**
* Copyright (c) 2025 Vitor Pamplona
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
* Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.amethyst.commons.ui.notifications
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable
import com.vitorpamplona.amethyst.commons.ui.feeds.LoadedFeedState
import kotlinx.coroutines.flow.MutableStateFlow
/**
* Base interface for notification cards.
*
* Cards represent grouped or individual notification items in the feed.
* Platform implementations provide concrete card types (NoteCard, MultiSetCard, etc.).
*/
@Immutable
interface Card {
/**
* Returns the creation timestamp of this card.
* Used for sorting cards in chronological order.
*/
fun createdAt(): Long
/**
* Returns a unique identifier for this card.
* Used for list diffing and deduplication.
*/
fun id(): String
}
/**
* Feed state for notification cards.
*
* Mirrors FeedState but specifically typed for Card content.
*/
@Stable
sealed class CardFeedState {
@Immutable
object Loading : CardFeedState()
@Stable
class Loaded(
val feed: MutableStateFlow<LoadedFeedState<Card>>,
) : CardFeedState()
@Immutable
object Empty : CardFeedState()
@Immutable
class FeedError(
val errorMessage: String,
) : CardFeedState()
}
/**
* Comparator for sorting cards by creation time (newest first).
*/
val DefaultCardComparator: Comparator<Card> =
compareByDescending<Card> { it.createdAt() }
.thenByDescending { it.id() }