Merge pull request #3109 from vitorpamplona/claude/great-knuth-sJVYl

Reorganize commons packages: NIP-64 chess, NIP-AC WebRTC, and model caches
This commit is contained in:
Vitor Pamplona
2026-05-30 15:55:59 -04:00
committed by GitHub
127 changed files with 807 additions and 240 deletions
+6 -1
View File
@@ -76,7 +76,12 @@ amethyst/
**Sharing Philosophy:**
- `quartz/` = Nostr business logic, protocol, data (no UI)
- `commons/` = Shared UI components, icons, composables, flows and ViewModels
- `commons/` = Shared code for every front end (Android, Desktop, iOS, and the
headless `cli`): domain models, state holders, ViewModels, the relay client,
shared services, **and** the Compose UI that ≥1 GUI front end renders. The
package taxonomy, the CLI-safe / UI boundary, and a "where does my code go?"
guide are documented in **`commons/ARCHITECTURE.md`** — read it before adding
a new package or dropping code into `commons`.
- `quic/` = Transport library (QUIC + HTTP/3 + WebTransport); reusable for any
KMP project that needs MoQ. Has no Android-framework dependencies.
- `nestsClient/` = MoQ + audio-rooms client; takes `:quic` as transport,
@@ -26,7 +26,7 @@ import coil3.disk.DiskCache
import coil3.memory.MemoryCache
import com.vitorpamplona.amethyst.commons.model.NoteState
import com.vitorpamplona.amethyst.commons.robohash.CachedRobohash
import com.vitorpamplona.amethyst.commons.services.lnurl.OkHttpLnurlEndpointResolver
import com.vitorpamplona.amethyst.commons.service.lnurl.OkHttpLnurlEndpointResolver
import com.vitorpamplona.amethyst.commons.tor.TorSettings
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.LocalCache
@@ -39,7 +39,7 @@ import com.vitorpamplona.amethyst.commons.model.observables.NewEventMatchingFilt
import com.vitorpamplona.amethyst.commons.model.observables.NoteListMatchingFilter
import com.vitorpamplona.amethyst.commons.model.observables.Observable
import com.vitorpamplona.amethyst.commons.model.privateChats.ChatroomList
import com.vitorpamplona.amethyst.commons.services.nwc.NwcPaymentTracker
import com.vitorpamplona.amethyst.commons.service.nwc.NwcPaymentTracker
import com.vitorpamplona.amethyst.isDebug
import com.vitorpamplona.amethyst.model.LocalCache.observeEvents
import com.vitorpamplona.amethyst.model.nip51Lists.HiddenUsersState
@@ -35,7 +35,7 @@ import androidx.core.app.NotificationCompat
import androidx.core.app.ServiceCompat
import androidx.core.content.ContextCompat
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.commons.call.CallState
import com.vitorpamplona.amethyst.commons.nipACWebRtcCalls.CallState
import com.vitorpamplona.amethyst.ui.call.CallActivity
import com.vitorpamplona.quartz.utils.Log
import kotlinx.coroutines.runBlocking
@@ -20,7 +20,7 @@
*/
package com.vitorpamplona.amethyst.service.call
import com.vitorpamplona.amethyst.commons.call.CallManager
import com.vitorpamplona.amethyst.commons.nipACWebRtcCalls.CallManager
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
/**
@@ -20,17 +20,17 @@
*/
package com.vitorpamplona.amethyst.service.call
import com.vitorpamplona.amethyst.commons.call.IceCandidateData
import com.vitorpamplona.amethyst.commons.call.PeerSession
import com.vitorpamplona.amethyst.commons.call.SdpType
import com.vitorpamplona.amethyst.commons.call.SignalingState
import com.vitorpamplona.amethyst.commons.nipACWebRtcCalls.IceCandidateData
import com.vitorpamplona.amethyst.commons.nipACWebRtcCalls.PeerSession
import com.vitorpamplona.amethyst.commons.nipACWebRtcCalls.SdpType
import com.vitorpamplona.amethyst.commons.nipACWebRtcCalls.SignalingState
import org.webrtc.IceCandidate
import org.webrtc.PeerConnection
import org.webrtc.SessionDescription
/**
* Adapts a [WebRtcCallSession] to the platform-independent [PeerSession] interface,
* allowing [com.vitorpamplona.amethyst.commons.call.PeerSessionManager] to manage
* allowing [com.vitorpamplona.amethyst.commons.nipACWebRtcCalls.PeerSessionManager] to manage
* ICE buffering, answer routing, and renegotiation glare without depending on org.webrtc.
*/
class WebRtcPeerSessionAdapter(
@@ -32,7 +32,7 @@ import coil3.request.allowHardware
import com.vitorpamplona.amethyst.Amethyst
import com.vitorpamplona.amethyst.LocalPreferences
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.commons.call.CallManager
import com.vitorpamplona.amethyst.commons.nipACWebRtcCalls.CallManager
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note
@@ -23,7 +23,7 @@ package com.vitorpamplona.amethyst.service.playback.composable.mediaitem
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import com.vitorpamplona.amethyst.commons.compose.produceCachedState
import com.vitorpamplona.amethyst.commons.ui.state.produceCachedState
import com.vitorpamplona.amethyst.service.playback.composable.WaveformData
import com.vitorpamplona.quartz.nip94FileMetadata.tags.DimensionTag
@@ -25,7 +25,7 @@ import androidx.core.net.toUri
import androidx.media3.common.MediaItem
import androidx.media3.common.MediaMetadata
import androidx.media3.common.MimeTypes
import com.vitorpamplona.amethyst.commons.compose.GenericBaseCache
import com.vitorpamplona.amethyst.commons.ui.state.GenericBaseCache
import com.vitorpamplona.quartz.utils.Log
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@@ -30,9 +30,9 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.text.input.TextFieldValue
import androidx.lifecycle.ViewModel
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.commons.compose.currentWord
import com.vitorpamplona.amethyst.commons.compose.insertUrlAtCursor
import com.vitorpamplona.amethyst.commons.richtext.RichTextParser
import com.vitorpamplona.amethyst.commons.ui.text.currentWord
import com.vitorpamplona.amethyst.commons.ui.text.insertUrlAtCursor
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.User
@@ -40,7 +40,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.core.content.ContextCompat
import androidx.lifecycle.lifecycleScope
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.commons.call.CallState
import com.vitorpamplona.amethyst.commons.nipACWebRtcCalls.CallState
import com.vitorpamplona.amethyst.service.call.CallSessionBridge
import com.vitorpamplona.amethyst.service.call.notification.CallNotifier
import com.vitorpamplona.amethyst.service.relayClient.authCommand.compose.RelayAuthSubscription
@@ -53,10 +53,10 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.commons.call.CallManager
import com.vitorpamplona.amethyst.commons.call.CallState
import com.vitorpamplona.amethyst.commons.icons.symbols.Icon
import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
import com.vitorpamplona.amethyst.commons.nipACWebRtcCalls.CallManager
import com.vitorpamplona.amethyst.commons.nipACWebRtcCalls.CallState
import com.vitorpamplona.amethyst.ui.call.session.CallSession
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.stringRes
@@ -56,9 +56,9 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.commons.call.CallState
import com.vitorpamplona.amethyst.commons.icons.symbols.Icon
import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
import com.vitorpamplona.amethyst.commons.nipACWebRtcCalls.CallState
import com.vitorpamplona.amethyst.service.call.AudioRoute
import com.vitorpamplona.amethyst.ui.call.session.CallSession
import com.vitorpamplona.amethyst.ui.note.creators.userSuggestions.ShowUserSuggestionList
@@ -42,7 +42,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.vitorpamplona.amethyst.commons.call.CallState
import com.vitorpamplona.amethyst.commons.nipACWebRtcCalls.CallState
import com.vitorpamplona.amethyst.ui.call.session.CallSession
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.quartz.utils.TimeUtils
@@ -27,14 +27,14 @@ import android.net.Network
import android.net.NetworkCapabilities
import android.net.NetworkRequest
import androidx.compose.runtime.Stable
import com.vitorpamplona.amethyst.commons.call.AnswerRouteAction
import com.vitorpamplona.amethyst.commons.call.CallManager
import com.vitorpamplona.amethyst.commons.call.CallState
import com.vitorpamplona.amethyst.commons.call.IceCandidateData
import com.vitorpamplona.amethyst.commons.call.PeerSession
import com.vitorpamplona.amethyst.commons.call.PeerSessionManager
import com.vitorpamplona.amethyst.commons.call.SdpType
import com.vitorpamplona.amethyst.commons.call.SignalingState
import com.vitorpamplona.amethyst.commons.nipACWebRtcCalls.AnswerRouteAction
import com.vitorpamplona.amethyst.commons.nipACWebRtcCalls.CallManager
import com.vitorpamplona.amethyst.commons.nipACWebRtcCalls.CallState
import com.vitorpamplona.amethyst.commons.nipACWebRtcCalls.IceCandidateData
import com.vitorpamplona.amethyst.commons.nipACWebRtcCalls.PeerSession
import com.vitorpamplona.amethyst.commons.nipACWebRtcCalls.PeerSessionManager
import com.vitorpamplona.amethyst.commons.nipACWebRtcCalls.SdpType
import com.vitorpamplona.amethyst.commons.nipACWebRtcCalls.SignalingState
import com.vitorpamplona.amethyst.service.call.AudioRoute
import com.vitorpamplona.amethyst.service.call.CallAudioManager
import com.vitorpamplona.amethyst.service.call.CallForegroundService
@@ -148,23 +148,23 @@ class CallSession(
callManager.sessionEvents.collect { event ->
if (closed) return@collect
when (event) {
is com.vitorpamplona.amethyst.commons.call.CallSessionEvent.AnswerReceived -> {
is com.vitorpamplona.amethyst.commons.nipACWebRtcCalls.CallSessionEvent.AnswerReceived -> {
onCallAnswerReceived(event.event.pubKey, event.event.sdpAnswer())
}
is com.vitorpamplona.amethyst.commons.call.CallSessionEvent.IceCandidateReceived -> {
is com.vitorpamplona.amethyst.commons.nipACWebRtcCalls.CallSessionEvent.IceCandidateReceived -> {
onIceCandidateReceived(event.event)
}
is com.vitorpamplona.amethyst.commons.call.CallSessionEvent.NewPeerInGroupCall -> {
is com.vitorpamplona.amethyst.commons.nipACWebRtcCalls.CallSessionEvent.NewPeerInGroupCall -> {
onNewPeerInGroupCall(event.peerPubKey)
}
is com.vitorpamplona.amethyst.commons.call.CallSessionEvent.MidCallOfferReceived -> {
is com.vitorpamplona.amethyst.commons.nipACWebRtcCalls.CallSessionEvent.MidCallOfferReceived -> {
onMidCallOfferReceived(event.peerPubKey, event.sdpOffer)
}
is com.vitorpamplona.amethyst.commons.call.CallSessionEvent.PeerLeft -> {
is com.vitorpamplona.amethyst.commons.nipACWebRtcCalls.CallSessionEvent.PeerLeft -> {
disposePeerSession(event.peerPubKey)
}
}
@@ -63,7 +63,6 @@ import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.em
import com.vitorpamplona.amethyst.Amethyst
import com.vitorpamplona.amethyst.commons.compose.produceCachedState
import com.vitorpamplona.amethyst.commons.emojicoder.EmojiCoder
import com.vitorpamplona.amethyst.commons.model.EmptyTagList
import com.vitorpamplona.amethyst.commons.model.ImmutableListOfLists
@@ -92,6 +91,7 @@ import com.vitorpamplona.amethyst.commons.richtext.SecretEmoji
import com.vitorpamplona.amethyst.commons.richtext.Segment
import com.vitorpamplona.amethyst.commons.richtext.VideoSegment
import com.vitorpamplona.amethyst.commons.richtext.WithdrawSegment
import com.vitorpamplona.amethyst.commons.ui.state.produceCachedState
import com.vitorpamplona.amethyst.model.HashtagIcon
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note
@@ -40,7 +40,7 @@ import androidx.core.util.Consumer
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.commons.call.CallState
import com.vitorpamplona.amethyst.commons.nipACWebRtcCalls.CallState
import com.vitorpamplona.amethyst.service.crashreports.DisplayCrashMessages
import com.vitorpamplona.amethyst.service.relayClient.notifyCommand.compose.DisplayNotifyMessages
import com.vitorpamplona.amethyst.ui.actions.NewUserMetadataScreen
@@ -57,10 +57,10 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.commons.compose.produceCachedStateAsync
import com.vitorpamplona.amethyst.commons.icons.symbols.Icon
import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
import com.vitorpamplona.amethyst.commons.model.nip28PublicChats.PublicChatChannel
import com.vitorpamplona.amethyst.commons.ui.state.produceCachedStateAsync
import com.vitorpamplona.amethyst.model.AddressableNote
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note
@@ -40,8 +40,8 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.text.style.TextOverflow
import coil3.compose.AsyncImage
import com.vitorpamplona.amethyst.commons.compose.produceCachedState
import com.vitorpamplona.amethyst.commons.richtext.RichTextParser
import com.vitorpamplona.amethyst.commons.ui.state.produceCachedState
import com.vitorpamplona.amethyst.model.UrlCachedPreviewer
import com.vitorpamplona.amethyst.service.playback.composable.VideoView
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
@@ -33,11 +33,11 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.vitorpamplona.amethyst.Amethyst
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.commons.compose.currentWord
import com.vitorpamplona.amethyst.commons.compose.insertUrlAtCursor
import com.vitorpamplona.amethyst.commons.compose.replaceCurrentWord
import com.vitorpamplona.amethyst.commons.compose.setTextAndPlaceCursorAtBeginning
import com.vitorpamplona.amethyst.commons.model.nip30CustomEmojis.EmojiPackState
import com.vitorpamplona.amethyst.commons.ui.text.currentWord
import com.vitorpamplona.amethyst.commons.ui.text.insertUrlAtCursor
import com.vitorpamplona.amethyst.commons.ui.text.replaceCurrentWord
import com.vitorpamplona.amethyst.commons.ui.text.setTextAndPlaceCursorAtBeginning
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note
@@ -44,8 +44,8 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.commons.chess.ChessChallenge
import com.vitorpamplona.amethyst.commons.chess.ChessGameViewer
import com.vitorpamplona.amethyst.commons.nip64Chess.ChessChallenge
import com.vitorpamplona.amethyst.commons.nip64Chess.ChessGameViewer
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.ui.components.SensitivityWarning
@@ -37,17 +37,17 @@ import com.vitorpamplona.amethyst.AccountInfo
import com.vitorpamplona.amethyst.Amethyst
import com.vitorpamplona.amethyst.LocalPreferences
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.commons.call.CallManager
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.model.emphChat.EphemeralChatChannel
import com.vitorpamplona.amethyst.commons.model.nip28PublicChats.PublicChatChannel
import com.vitorpamplona.amethyst.commons.model.nip53LiveActivities.LiveActivitiesChannel
import com.vitorpamplona.amethyst.commons.model.observables.CreatedAtComparator
import com.vitorpamplona.amethyst.commons.nipACWebRtcCalls.CallManager
import com.vitorpamplona.amethyst.commons.tor.TorType
import com.vitorpamplona.amethyst.commons.ui.feeds.FeedState
import com.vitorpamplona.amethyst.commons.ui.notifications.CardFeedState
import com.vitorpamplona.amethyst.commons.ui.state.GenericBaseCache
import com.vitorpamplona.amethyst.commons.ui.state.GenericBaseCacheAsync
import com.vitorpamplona.amethyst.logTime
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.AccountSettings
@@ -21,8 +21,8 @@
package com.vitorpamplona.amethyst.ui.screen.loggedIn
import com.vitorpamplona.amethyst.Amethyst
import com.vitorpamplona.amethyst.commons.call.CallManager
import com.vitorpamplona.amethyst.commons.model.privateChats.ChatroomList
import com.vitorpamplona.amethyst.commons.nipACWebRtcCalls.CallManager
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note
@@ -35,7 +35,7 @@ import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.commons.compose.nip53LiveActivities.StreamSystemCard
import com.vitorpamplona.amethyst.commons.nip53LiveActivities.ui.StreamSystemCard
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.service.playback.composable.VideoView
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
@@ -35,9 +35,9 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.commons.compose.nip53LiveActivities.StreamSystemCard
import com.vitorpamplona.amethyst.commons.icons.symbols.Icon
import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
import com.vitorpamplona.amethyst.commons.nip53LiveActivities.ui.StreamSystemCard
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
@@ -36,7 +36,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.commons.compose.nip53LiveActivities.StreamSystemCard
import com.vitorpamplona.amethyst.commons.nip53LiveActivities.ui.StreamSystemCard
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.note.CrossfadeToDisplayComment
@@ -32,10 +32,10 @@ import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.vitorpamplona.amethyst.Amethyst
import com.vitorpamplona.amethyst.commons.compose.currentWord
import com.vitorpamplona.amethyst.commons.compose.insertUrlAtCursor
import com.vitorpamplona.amethyst.commons.compose.replaceCurrentWord
import com.vitorpamplona.amethyst.commons.model.nip30CustomEmojis.EmojiPackState
import com.vitorpamplona.amethyst.commons.ui.text.currentWord
import com.vitorpamplona.amethyst.commons.ui.text.insertUrlAtCursor
import com.vitorpamplona.amethyst.commons.ui.text.replaceCurrentWord
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note
@@ -33,15 +33,15 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.vitorpamplona.amethyst.Amethyst
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.commons.compose.currentWord
import com.vitorpamplona.amethyst.commons.compose.insertUrlAtCursor
import com.vitorpamplona.amethyst.commons.compose.replaceCurrentWord
import com.vitorpamplona.amethyst.commons.model.Channel
import com.vitorpamplona.amethyst.commons.model.emphChat.EphemeralChatChannel
import com.vitorpamplona.amethyst.commons.model.nip28PublicChats.PublicChatChannel
import com.vitorpamplona.amethyst.commons.model.nip30CustomEmojis.EmojiPackState
import com.vitorpamplona.amethyst.commons.model.nip53LiveActivities.LiveActivitiesChannel
import com.vitorpamplona.amethyst.commons.richtext.UrlParser
import com.vitorpamplona.amethyst.commons.ui.text.currentWord
import com.vitorpamplona.amethyst.commons.ui.text.insertUrlAtCursor
import com.vitorpamplona.amethyst.commons.ui.text.replaceCurrentWord
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note
@@ -20,15 +20,15 @@
*/
package com.vitorpamplona.amethyst.ui.screen.loggedIn.chess
import com.vitorpamplona.amethyst.commons.chess.ChessConfig
import com.vitorpamplona.amethyst.commons.chess.ChessEventBroadcaster
import com.vitorpamplona.amethyst.commons.chess.ChessEventPublisher
import com.vitorpamplona.amethyst.commons.chess.ChessRelayFetchHelper
import com.vitorpamplona.amethyst.commons.chess.ChessRelayFetcher
import com.vitorpamplona.amethyst.commons.chess.IUserMetadataProvider
import com.vitorpamplona.amethyst.commons.chess.RelayFetchProgress
import com.vitorpamplona.amethyst.commons.chess.RelayGameSummary
import com.vitorpamplona.amethyst.commons.chess.subscription.ChessFilterBuilder
import com.vitorpamplona.amethyst.commons.nip64Chess.ChessConfig
import com.vitorpamplona.amethyst.commons.nip64Chess.ChessEventBroadcaster
import com.vitorpamplona.amethyst.commons.nip64Chess.ChessEventPublisher
import com.vitorpamplona.amethyst.commons.nip64Chess.ChessRelayFetchHelper
import com.vitorpamplona.amethyst.commons.nip64Chess.ChessRelayFetcher
import com.vitorpamplona.amethyst.commons.nip64Chess.IUserMetadataProvider
import com.vitorpamplona.amethyst.commons.nip64Chess.RelayFetchProgress
import com.vitorpamplona.amethyst.commons.nip64Chess.RelayGameSummary
import com.vitorpamplona.amethyst.commons.nip64Chess.subscription.ChessFilterBuilder
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.quartz.nip64Chess.ChessGameEnd
@@ -59,12 +59,12 @@ import androidx.compose.ui.unit.dp
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.viewmodel.compose.viewModel
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.commons.chess.ChessBroadcastBanner
import com.vitorpamplona.amethyst.commons.chess.ChessBroadcastStatus
import com.vitorpamplona.amethyst.commons.chess.ChessSyncBanner
import com.vitorpamplona.amethyst.commons.chess.LiveChessGameScreen
import com.vitorpamplona.amethyst.commons.icons.symbols.Icon
import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
import com.vitorpamplona.amethyst.commons.nip64Chess.ChessBroadcastBanner
import com.vitorpamplona.amethyst.commons.nip64Chess.ChessBroadcastStatus
import com.vitorpamplona.amethyst.commons.nip64Chess.ChessSyncBanner
import com.vitorpamplona.amethyst.commons.nip64Chess.LiveChessGameScreen
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
@@ -60,18 +60,18 @@ import androidx.compose.ui.unit.dp
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.viewmodel.compose.viewModel
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.commons.chess.ActiveGameCard
import com.vitorpamplona.amethyst.commons.chess.ChallengeCard
import com.vitorpamplona.amethyst.commons.chess.ChessChallenge
import com.vitorpamplona.amethyst.commons.chess.ChessSyncBanner
import com.vitorpamplona.amethyst.commons.chess.CompletedGameCard
import com.vitorpamplona.amethyst.commons.chess.NewChessGameDialog
import com.vitorpamplona.amethyst.commons.chess.OutgoingChallengeCard
import com.vitorpamplona.amethyst.commons.chess.OverlappingAvatars
import com.vitorpamplona.amethyst.commons.chess.PublicGameCard
import com.vitorpamplona.amethyst.commons.chess.SpectatingGameCard
import com.vitorpamplona.amethyst.commons.icons.symbols.Icon
import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
import com.vitorpamplona.amethyst.commons.nip64Chess.ActiveGameCard
import com.vitorpamplona.amethyst.commons.nip64Chess.ChallengeCard
import com.vitorpamplona.amethyst.commons.nip64Chess.ChessChallenge
import com.vitorpamplona.amethyst.commons.nip64Chess.ChessSyncBanner
import com.vitorpamplona.amethyst.commons.nip64Chess.CompletedGameCard
import com.vitorpamplona.amethyst.commons.nip64Chess.NewChessGameDialog
import com.vitorpamplona.amethyst.commons.nip64Chess.OutgoingChallengeCard
import com.vitorpamplona.amethyst.commons.nip64Chess.OverlappingAvatars
import com.vitorpamplona.amethyst.commons.nip64Chess.PublicGameCard
import com.vitorpamplona.amethyst.commons.nip64Chess.SpectatingGameCard
import com.vitorpamplona.amethyst.ui.feeds.RefresheableBox
import com.vitorpamplona.amethyst.ui.navigation.bottombars.FabBottomBarPadded
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
@@ -44,9 +44,9 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.commons.chess.ChessConfig
import com.vitorpamplona.amethyst.commons.icons.symbols.Icon
import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
import com.vitorpamplona.amethyst.commons.nip64Chess.ChessConfig
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
@@ -23,14 +23,14 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.chess
import androidx.compose.runtime.Stable
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.vitorpamplona.amethyst.commons.chess.ChessBroadcastStatus
import com.vitorpamplona.amethyst.commons.chess.ChessChallenge
import com.vitorpamplona.amethyst.commons.chess.ChessDismissedGamesStorage
import com.vitorpamplona.amethyst.commons.chess.ChessLobbyLogic
import com.vitorpamplona.amethyst.commons.chess.ChessPollingDefaults
import com.vitorpamplona.amethyst.commons.chess.ChessSyncStatus
import com.vitorpamplona.amethyst.commons.chess.CompletedGame
import com.vitorpamplona.amethyst.commons.chess.PublicGame
import com.vitorpamplona.amethyst.commons.nip64Chess.ChessBroadcastStatus
import com.vitorpamplona.amethyst.commons.nip64Chess.ChessChallenge
import com.vitorpamplona.amethyst.commons.nip64Chess.ChessDismissedGamesStorage
import com.vitorpamplona.amethyst.commons.nip64Chess.ChessLobbyLogic
import com.vitorpamplona.amethyst.commons.nip64Chess.ChessPollingDefaults
import com.vitorpamplona.amethyst.commons.nip64Chess.ChessSyncStatus
import com.vitorpamplona.amethyst.commons.nip64Chess.CompletedGame
import com.vitorpamplona.amethyst.commons.nip64Chess.PublicGame
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip64Chess.Color
@@ -20,8 +20,8 @@
*/
package com.vitorpamplona.amethyst.ui.screen.loggedIn.chess.datasource
import com.vitorpamplona.amethyst.commons.chess.subscription.ChessFilterBuilder
import com.vitorpamplona.amethyst.commons.chess.subscription.ChessSubscriptionState
import com.vitorpamplona.amethyst.commons.nip64Chess.subscription.ChessFilterBuilder
import com.vitorpamplona.amethyst.commons.nip64Chess.subscription.ChessSubscriptionState
import com.vitorpamplona.amethyst.commons.relays.SincePerRelayMap
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
@@ -34,10 +34,10 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.vitorpamplona.amethyst.Amethyst
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.commons.compose.currentWord
import com.vitorpamplona.amethyst.commons.compose.insertUrlAtCursor
import com.vitorpamplona.amethyst.commons.compose.replaceCurrentWord
import com.vitorpamplona.amethyst.commons.model.nip30CustomEmojis.EmojiPackState.EmojiMedia
import com.vitorpamplona.amethyst.commons.ui.text.currentWord
import com.vitorpamplona.amethyst.commons.ui.text.insertUrlAtCursor
import com.vitorpamplona.amethyst.commons.ui.text.replaceCurrentWord
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note
@@ -33,10 +33,10 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.vitorpamplona.amethyst.Amethyst
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.commons.compose.currentWord
import com.vitorpamplona.amethyst.commons.compose.insertUrlAtCursor
import com.vitorpamplona.amethyst.commons.compose.replaceCurrentWord
import com.vitorpamplona.amethyst.commons.model.nip30CustomEmojis.EmojiPackState
import com.vitorpamplona.amethyst.commons.ui.text.currentWord
import com.vitorpamplona.amethyst.commons.ui.text.insertUrlAtCursor
import com.vitorpamplona.amethyst.commons.ui.text.replaceCurrentWord
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note
@@ -29,9 +29,9 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.lifecycle.viewmodel.compose.viewModel
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.commons.chess.NewChessGameDialog
import com.vitorpamplona.amethyst.commons.icons.symbols.Icon
import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
import com.vitorpamplona.amethyst.commons.nip64Chess.NewChessGameDialog
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.chess.ChessViewModelFactory
@@ -35,11 +35,11 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.vitorpamplona.amethyst.Amethyst
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.commons.compose.currentWord
import com.vitorpamplona.amethyst.commons.compose.insertUrlAtCursor
import com.vitorpamplona.amethyst.commons.compose.replaceCurrentWord
import com.vitorpamplona.amethyst.commons.compose.setTextAndPlaceCursorAtBeginning
import com.vitorpamplona.amethyst.commons.model.nip30CustomEmojis.EmojiPackState.EmojiMedia
import com.vitorpamplona.amethyst.commons.ui.text.currentWord
import com.vitorpamplona.amethyst.commons.ui.text.insertUrlAtCursor
import com.vitorpamplona.amethyst.commons.ui.text.replaceCurrentWord
import com.vitorpamplona.amethyst.commons.ui.text.setTextAndPlaceCursorAtBeginning
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.BooleanType
import com.vitorpamplona.amethyst.model.LocalCache
@@ -33,12 +33,12 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.vitorpamplona.amethyst.Amethyst
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.commons.compose.currentWord
import com.vitorpamplona.amethyst.commons.compose.insertUrlAtCursor
import com.vitorpamplona.amethyst.commons.compose.replaceCurrentWord
import com.vitorpamplona.amethyst.commons.model.AddressableNote
import com.vitorpamplona.amethyst.commons.model.nip30CustomEmojis.EmojiPackState
import com.vitorpamplona.amethyst.commons.richtext.UrlParser
import com.vitorpamplona.amethyst.commons.ui.text.currentWord
import com.vitorpamplona.amethyst.commons.ui.text.insertUrlAtCursor
import com.vitorpamplona.amethyst.commons.ui.text.replaceCurrentWord
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note
@@ -27,5 +27,5 @@ fun <T> equalImmutableLists(
list1: ImmutableList<T>,
list2: ImmutableList<T>,
): Boolean =
com.vitorpamplona.amethyst.commons.utils
com.vitorpamplona.amethyst.commons.util
.equalImmutableLists(list1, list2)
@@ -33,10 +33,10 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.vitorpamplona.amethyst.Amethyst
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.commons.compose.currentWord
import com.vitorpamplona.amethyst.commons.compose.insertUrlAtCursor
import com.vitorpamplona.amethyst.commons.compose.replaceCurrentWord
import com.vitorpamplona.amethyst.commons.model.nip30CustomEmojis.EmojiPackState
import com.vitorpamplona.amethyst.commons.ui.text.currentWord
import com.vitorpamplona.amethyst.commons.ui.text.insertUrlAtCursor
import com.vitorpamplona.amethyst.commons.ui.text.replaceCurrentWord
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note
@@ -32,7 +32,7 @@ import com.vitorpamplona.amethyst.commons.actions.SearchActions
import com.vitorpamplona.amethyst.commons.actions.ZapActions
import com.vitorpamplona.amethyst.commons.defaults.DefaultNIP65RelaySet
import com.vitorpamplona.amethyst.commons.relayClient.nip17Dm.unwrapAndUnsealOrNull
import com.vitorpamplona.amethyst.commons.services.lnurl.LightningAddressResolver
import com.vitorpamplona.amethyst.commons.service.lnurl.LightningAddressResolver
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.dal.HomeNewThreadFeedFilter
import com.vitorpamplona.quartz.lightning.LnInvoiceUtil
import com.vitorpamplona.quartz.marmot.RecipientRelayFetcher
@@ -25,7 +25,7 @@ import com.vitorpamplona.amethyst.cli.Context
import com.vitorpamplona.amethyst.cli.DataDir
import com.vitorpamplona.amethyst.cli.Output
import com.vitorpamplona.amethyst.commons.actions.ZapActions
import com.vitorpamplona.amethyst.commons.services.lnurl.LightningAddressResolver
import com.vitorpamplona.amethyst.commons.service.lnurl.LightningAddressResolver
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent
+243
View File
@@ -0,0 +1,243 @@
# `commons` — Shared Module Architecture & Goals
`commons` is the **shared layer** between every Amethyst front end:
| Consumer | Kind | Uses from `commons` |
|----------------|------------------------------|----------------------------------------------|
| `amethyst` | Android app (touch-first) | everything (models, state, ViewModels, UI) |
| `desktopApp` | Desktop JVM app (mouse-first)| everything (models, state, ViewModels, UI) |
| `cli` (`amy`) | Headless JVM CLI (no UI) | **non-UI only** — models, actions, relay, services |
| iOS (future) | iOS app | everything; expected to share most UI with Android |
`commons` sits **above** `quartz` (the protocol-only Nostr KMP library) and
**below** the apps. The split between the three is:
- **`quartz/`** — Nostr protocol: events, NIPs, crypto, relay framing. No app
state, no UI, no caches of "what this user follows."
- **`commons/`** — everything an Amethyst *client* needs that isn't a
platform-native screen or navigation shell: domain models (`Note`, `User`),
in-memory state holders, ViewModels, the relay-subscription client, shared
business services, **and** the Compose UI components that more than one front
end renders.
- **`amethyst/` & `desktopApp/`** — platform-native screens, navigation
(bottom-nav vs sidebar), gestures, system integration. They assemble
`commons` pieces; they should not re-implement them.
> The goal is **"write it once in `commons`"**. Before adding a manager, cache,
> filter, ViewModel, or composable to an app module, check whether it already
> exists here or belongs here. See the "Where does my code go?" guide below.
---
## 1. The one rule that shapes the package tree: the **UI / non-UI boundary**
`commons` is a single module that contains **both** Compose UI and headless
logic. That is deliberate (it keeps a feature's model, state, and UI together —
see §3), but it creates one hard constraint, because **`cli` and any headless
consumer cannot use Compose**:
> **CLI-safe code** = does not depend on Compose UI. It may use the
> `androidx.compose.runtime` *annotations* `@Stable` / `@Immutable` (they are
> just stability tags) and snapshot state, but it must **not** import
> `androidx.compose.ui`, `androidx.compose.foundation`,
> `androidx.compose.material3`, declare `@Composable` functions, or build
> `ImageVector`s.
>
> **UI code** = anything that does. It is only usable by the GUI front ends
> (Android, Desktop, iOS), never by `cli`.
Compose is an `implementation` dependency of `commonMain`, so `cli` pulling in
`commons` does **not** force it to render anything — but a `cli` command must
only reach for CLI-safe packages. When you add code, know which side of this
line it is on, and put it in a package that matches (§2).
This boundary is **not** a top-level `ui/` vs `logic/` partition of the whole
module (we chose to stay feature-oriented, §3). It is a property of each file
that you keep track of via package placement and the table in §2.
---
## 2. Package taxonomy
Top-level packages under
`commonMain/.../commons/`, grouped by concern. **UI?** marks whether the
package contains Compose UI (and is therefore *not* CLI-safe).
### Domain models & data
| Package | UI? | Purpose |
|----------------|-----|---------|
| `model` | no¹ | Core domain types (`Note`, `User`, `Channel`), thread assembly, and per-NIP event model extensions in `model/nipNN…` subpackages. `model/cache` holds the in-memory event-store interfaces + `UserMetadataCache`. `model/account`, `model/observables`. The largest package; keep it organized by NIP. |
| `defaults` | no | Static bootstrap data (default relays, channels). |
¹ `model` uses only the `@Stable`/`@Immutable` runtime annotations — CLI-safe.
### Protocol-adjacent business logic (CLI-safe)
| Package | UI? | Purpose |
|----------------|-----|---------|
| `actions` | no | Event builders for user actions (follow, zap…). The canonical entry point for non-UI callers. |
| `account` | no | New-account bootstrap events. |
| `onchain` | no | On-chain zap splitting/broadcasting. |
| `marmot` | no | MLS group-chat event processing. |
| `nip53LiveActivities` | mixed | Live-activity zapper aggregation (logic) + the stream card in `nip53LiveActivities/ui`. |
| `search` | no | Event search filtering/ranking, kind registry. |
| `preview` | no | OpenGraph / meta-tag link-preview parsing. |
| `emojicoder` | no | Variation-selector emoji encode/decode. |
| `richtext` | no | URL/media/pattern parsing for rich text. |
| `blurhash` | no | BlurHash encode/decode (pure math; platform image bridge in platform sets). |
| `thumbhash` | no | ThumbHash encode/decode. |
| `nipACWebRtcCalls` | no | NIP-AC WebRTC **call state machine** + peer-session abstraction. See `nipACWebRtcCalls/ARCHITECTURE.md`. (Mirrors `quartz/.../nipACWebRtcCalls`.) |
### State holders & ViewModels
| Package | UI? | Purpose |
|----------------|-----|---------|
| `state` | no² | Small feature `StateFlow` machines (`FollowState`, `UserMetadataState`, `LoadingState`). |
| `viewmodels` | no² | Larger list/feed-backed ViewModels (`androidx.lifecycle.ViewModel`). Shared by all GUI front ends; `cli` usually drives the layers below instead. |
| `feeds` | no | `FeedDefinitionRepository` — custom-feed definitions & ordering. |
| `profile` | mixed | `ProfileBroadcastStatus` (state) + `EditProfileFields` at the root; the `ProfileBroadcastBanner` composable lives in `profile/ui`. |
² may touch `compose.runtime`/`foundation` state types (e.g. `LazyListState`);
they are shared across the GUI apps. Treat as GUI-shared, not strictly headless.
### Relay client
| Package | UI? | Purpose |
|----------------|-----|---------|
| `relayClient` | no | Compose-scoped subscription managers, filter assemblers, EOSE managers, preloaders. (Despite a `composeSubscriptionManagers` subpackage name, this is subscription-lifecycle logic, not UI.) |
| `relays` | no | Low-level EOSE/relay-timing bookkeeping (`EOSECache`, `EOSERelayList`). |
### Platform abstractions (`expect`/`actual`)
| Package | UI? | Purpose |
|----------------|-----|---------|
| `util` | no | KMP primitives: `KmpLock`, `WeakReference`, number/URL/codepoint helpers, list/debug helpers. **This is the only general-utility package** — there is no `utils`. |
| `keystorage` | no | Secure key storage interface (Keystore / keychain / keyring actuals). |
| `threading` | no | `checkNotInMainThread` assertion. |
| `tor` | no | Tor manager interface + settings. |
| `service` | no | Cross-cutting services: `BundledUpdate` batching (common); `service/upload` (JVM), `service/nwc`, `service/lnurl` (jvmAndroid). **Singular `service`** — there is no `services`. |
### UI (Compose — **not** CLI-safe)
| Package | UI? | Purpose |
|----------------|-----|---------|
| `ui` | yes | **Cross-cutting** shared composables only, organized by area: `ui/components`, `ui/theme`, `ui/signing`, `ui/thread`, `ui/feeds` (feed DAL + filters — see debt §4), `ui/notifications`, `ui/screens`, `ui/elements`, `ui/layouts`, `ui/markdown`, plus Compose helpers in `ui/state` (cached-state) and `ui/text` (TextField extensions). Feature-specific UI lives in `<feature>/ui`, **not** here. |
| `nip23LongContent` | yes | Long-form (NIP-23) article UI: `nip23LongContent/ui/article` (reader) + `…/ui/editor` (authoring). The model lives in `model/nip23LongContent`. |
| `icons` | yes | `ImageVector` icon definitions + builders. |
| `hashtags` | yes | Custom hashtag `ImageVector`s. |
| `robohash` | yes | Procedural robohash avatar `ImageVector` assembly. |
### Mixed (documented debt — see §4)
| Package | UI? | Purpose |
|----------------|-----|---------|
| `nip64Chess` | mixed | Live-chess feature: game/lobby/subscription logic **and** board/lobby composables in one flat package. Needs a `nip64Chess/ui` split. (Mirrors `quartz/.../nip64Chess`.) |
| `domain` | no | Currently only `domain/nip46` (Nostr Connect signer flows). Sparse; candidate to fold into a clearer home. |
---
## 3. Conventions
### Feature-oriented, not layer-partitioned
We keep a feature's model, state, and UI **together** under one feature
package rather than splitting the whole module into top-level `ui/` /
`viewmodels/` / `model/` layers. Within a feature, separate UI from logic with a
`ui` **subpackage** (e.g. `profile/ProfileBroadcastStatus` vs
`profile/ui/ProfileBroadcastBanner`) so the CLI-safe boundary (§1) stays
visible.
The big shared cross-feature packages (`model`, `ui`, `relayClient`, `util`,
`icons`) are the exception — they are organized by layer because many features
share them.
### Naming
- **Singular, no synonyms.** `util` (not `utils`), `service` (not `services`).
One concept → one package name.
- **Feature UI vs cross-cutting UI — the deciding test.** A composable goes in
`<feature>/ui` if it renders/edits *one* feature's content (it would make no
sense outside that feature) — e.g. `profile/ui`, `nip53LiveActivities/ui`,
`nip23LongContent/ui`. It goes in `ui/<area>` only if it is reusable across
features (theme, avatars, buttons, layouts, markdown rendering, shimmer…).
When in doubt, ask "could a second, unrelated feature reuse this as-is?" —
yes → `ui/<area>`, no → `<feature>/ui`. The top-level `ui/` package holds
**no** feature-specific composables.
### NIP as the second axis (mirror `quartz`)
`quartz` is ~94% organized by NIP (`nipNN<slug>` per spec), and that is correct
*there* — the protocol layer is naturally NIP-partitioned. `commons` is **not**
organized by NIP at the top level, and should not be: most of it is
cross-cutting infrastructure (`ui`, `relayClient`, `viewmodels`, `feeds`,
`util`…) that serves many NIPs at once, and the load-bearing UI/non-UI boundary
(§1) cuts *across* NIPs, so a NIP-first top level would just nest the same
problem one level down.
Instead, **layer is the primary axis, NIP is the secondary axis**:
- The big shared layers stay layer-organized (`model`, `relayClient`, the
cross-cutting `ui`, …).
- **Inside a layer, NIP-specific code goes in a `nipNN<slug>` subpackage whose
name matches `quartz` exactly** — e.g. `model/nip57Zaps`. This gives a clean
trace: `quartz/nip57Zaps``commons/model/nip57Zaps`.
- **A top-level package that *is* a single self-contained NIP feature takes the
same name as its `quartz` counterpart**, and owns its own UI under
`<feature>/ui`: `nip64Chess`, `nipACWebRtcCalls`, `nip53LiveActivities`,
`nip23LongContent`, `marmot`. (`marmot` is un-numbered in `quartz` too.)
Generic/multi-NIP packages keep their concern name (`search`, `preview`,
`actions`, `richtext`…).
- **Feature UI is never under `ui/`.** A single-NIP feature's composables live
in `<feature>/ui` (e.g. `nip53LiveActivities/ui`), not `ui/nip53LiveActivities`.
`ui/` is exclusively cross-cutting (§2, Naming).
### Source sets
| Source set | For |
|---------------|-----|
| `commonMain` | KMP code for **all** targets (Android, JVM, iOS). Gated by `verifyKmpPurity` — no Jackson/OkHttp/`System.currentTimeMillis`/`java.util.UUID`/JVM `@Synchronized`/`@Volatile`. Use the KMP replacements. |
| `jvmAndroid` | Shared by Android + Desktop, **not** iOS. Where JVM-bound deps live (`nestsClient`, Coil-OkHttp, markdown, `viewModel()` helper, NWC/LNURL). |
| `jvmMain` | Desktop-only (keyring, EXIF, `service/upload`). `dependsOn(jvmAndroid)`. |
| `androidMain` | Android-only (Keystore, DataStore). `dependsOn(jvmAndroid)`. |
| `iosMain` | iOS `actual`s. Compile-only spike today. |
When adding platform code, prefer the **most common** source set that still
compiles: `commonMain``jvmAndroid` → platform-specific. See
`/kotlin-multiplatform`.
### Where does my code go? (quick guide)
1. **Pure Nostr protocol** (events/NIPs/crypto)? → not here, it's `quartz`.
2. **A composable** rendered by ≥2 front ends, or that you want iOS to share? →
`ui/<area>` or `<feature>/ui`. Never in `cli`.
3. **A ViewModel / `StateFlow` state holder**? → `viewmodels` or `state` (or
`<feature>` if feature-scoped). Keep it CLI-safe where practical.
4. **Relay subscription / filter assembly**? → `relayClient`.
5. **A domain model or per-NIP event wrapper**? → `model` (`model/nipNN…`).
6. **A platform capability behind `expect`/`actual`** (storage, crypto,
threading)? → the matching abstraction package + actuals in platform sets.
7. **A generic helper**? → `util`. (Resist creating a new top-level package for
one file.)
---
## 4. Known debt / follow-ups
These are intentionally *documented*, not silently tolerated. Fix opportunistically.
- **`nip64Chess` is UI+logic in one flat package.** `LiveChessGame.kt` mixes a
state class with composables. Split into `nip64Chess/` (logic) +
`nip64Chess/ui/` (composables); this needs file-level surgery (extracting
composables out of logic files), not just moves, so it is deferred.
- **`ui/feeds` holds the feed data-access layer** (`FeedFilter`,
`ChangesFlowFilter`, `FeedContentState`), which is logic, not UI, and overlaps
conceptually with the top-level `feeds` (custom-feed definitions). Consider
moving the DAL out of `ui/`.
- **`domain` is sparse** (only `nip46`). Either grow it as the home for
use-case/flow types or rename it to the matching `nip46RemoteSigner` per the
NIP-second-axis rule.
- **`relays` vs `relayClient`** are coherent but close in name; `relays` is
low-level EOSE bookkeeping, `relayClient` is the subscription client. Keep the
distinction in mind when adding files.
- Several **single-file feature packages** (`account`, `marmot`,
`nip53LiveActivities`, `keystorage`, `threading`) are kept as feature/abstraction
namespaces expected to grow; do not fold them into `util` just for size.
- **`onchain`** (on-chain zap splitting) is `quartz`-adjacent but un-numbered;
leave readable unless a clear NIP number lands.
---
## 5. See also
- `nipACWebRtcCalls/ARCHITECTURE.md` — WebRTC call state machine deep-dive.
- Root `.claude/CLAUDE.md` — module overview, sharing philosophy, build commands.
- `/kotlin-multiplatform`, `/compose-expert`, `/feed-patterns`, `/relay-client`,
`/account-state` skills for the patterns referenced above.
@@ -0,0 +1,312 @@
# Amethyst → Commons Migration Plan
**Date:** 2026-05-30 · **Owning module:** `commons` · **Status:** plan (no code moved yet)
Goal: move the **shared** parts of the `amethyst` Android app down into `commons`
so Desktop, the headless `cli`, and future iOS reuse them, leaving `amethyst`
as a thin Android-native shell (Activity, navigation, notifications, platform
services). This is the inverse direction of the `commons` package refactor that
preceded it (see `commons/ARCHITECTURE.md`) — that cleaned up *where* shared
code lives; this plan decides *what* still needs to move there.
## Scope surveyed
`amethyst/src/main`**1817 Kotlin files**. Coupling signals:
| Signal | Files | Meaning |
|---|---|---|
| `androidx.compose.*` | 1075 | UI |
| `R.string` / `amethyst.R` | 559 | localized strings — see §"Strings" |
| `android.content.Context` | 142 | platform handle to abstract |
| Activity / Service / `android.app` | 40 | clearly stays native |
| `com.fasterxml.jackson` | 6 | purity-gate only matters for commonMain/iOS |
Three structured passes (model/, service/, ui/) produced the matrices below.
---
## 0. What is ALREADY in commons (do NOT re-extract)
The agents that surveyed `amethyst` did not all know commons' current state.
These already exist and the amethyst copies are **dedup / re-point** targets,
not new extractions:
- **Account scaffolding:** `model/IAccount`, `model/account/{AccountInfo,
AccountStorage,SignerType}`, `state/{FollowState,UserMetadataState,
LoadingState,EventCollectionState}`, `model/cache/*`. **But the concrete
`Account.kt` (3565 LOC) and `LocalCache.kt` (3917 LOC) are NOT extracted** —
only their interfaces/scaffolding are. They are the keystone (§Phase A).
- **Relay layer:** `relayClient/*` (assemblers, eoseManagers, composeSubscription
managers, preload, subscriptions, nip17Dm) and `relays/*`.
- **Formatters / util:** `util/PubKeyFormatter`, `util/NumberFormatters`,
`util/PlatformNumberFormatter` (expect/actual). amethyst's
`ui/note/PubKeyFormatter.kt` is a **duplicate**.
- **Services:** `service/lnurl/{LightningAddressResolver,OkHttpLnurlEndpointResolver}`,
`service/nwc`, `service/upload`. amethyst's `service/lnurl/LightningAddressResolver.kt`
is a **duplicate**.
- **Strings:** commons localizes through **Compose Multiplatform Resources**
(`composeResources/values/strings.xml`, `stringResource(Res.string.x)`),
already 41 strings. ← This is the migration path for `R.string`, NOT a custom
`StringProvider`.
- **Image loading:** Coil 3 is already KMP — `coil.compose` in `commonMain`,
`coil.okhttp` in `jvmAndroid`. Shared composables can call `AsyncImage`
directly; **image loading is largely a non-blocker**.
- **Misc:** `tor/`, `keystorage/` (interface + actuals), `richtext/`,
`preview/` (OpenGraph/MetaTags parsers), `feeds/`, `ui/feeds` (feed DAL).
---
## 1. Cross-cutting blockers (the real Phase 0)
Ranked by how much extraction they unblock. Note the corrections vs. the raw
survey (Coil and Strings are *less* of a blocker than assumed; account state is
*more*).
| Blocker | Reality | Resolution |
|---|---|---|
| **Account state** (`Account.kt`, `LocalCache.kt`, `AccountViewModel`) | The dominant blocker. ~53 UI files read it; nearly every ViewModel depends on it. commons has only the interfaces. | Extract `Account`/`LocalCache` business logic to `commons/model/account` + `model/cache`, behind the existing `IAccount`/cache interfaces. Keep Android-only bits (LocationState wiring, `@Stable`) in a thin adapter. **Keystone — §Phase A.** |
| **`R.string`** (559) | Solved pattern already in use. | Migrate needed strings into `commons/.../composeResources/values/strings.xml`; replace `R.string.x``Res.string.x`. No new abstraction. Crowdin config already exists at repo root. |
| **`Context`** (142) | Mostly file pick / clipboard / intents / cacheDir. | Small `expect`/`actual` platform-service interfaces (file picker, clipboard, URI open). Most are confined to `ui/actions/uploads` and a few service packages. |
| **`INav`** (nav callbacks) | Already an interface; low friction. | Shared composables take `onNavigate: (route) -> Unit` lambdas, not `INav` directly. |
| **Image loading (Coil)** | Already KMP in commons. | Use commons Coil directly; only the Android `ImageLoaderSetup`/fetchers stay native (or move to `jvmAndroid`). |
| **OkHttp / Jackson** | Allowed in `jvmAndroid`; banned only in `commonMain`/iOS. | HTTP/JSON code → `jvmAndroid`. Only promote to `commonMain` (for iOS) by swapping to Ktor + kotlinx.serialization, and only when iOS needs it. |
---
## 2. model/ — migration matrix
Most of `amethyst/model` is CLI-safe Nostr state and extracts cleanly. Verdicts:
**Extract** (pure) · **Abstract** (needs storage/HTTP/keystore seam) ·
**Keystone** · **Native**.
| Area | Verdict | Destination | Note |
|---|---|---|---|
| `Account.kt`, `LocalCache.kt`, `accountsCache/` | **Keystone** | `model/account`, `model/cache` | 3565+3917 LOC; behind `IAccount`/cache ifaces; LocationState → adapter |
| `AccountSettings`, `AccountSyncedSettings*` | Extract | `model/account` | pure data + event (de)serialization |
| `UiSettings`, `UiSettingsFlow` | Abstract | `model/` | drop `R.string` (use string keys / `Res`) |
| `nip01UserMetadata`, `nip02FollowLists`, `nip17Dms`, `nip30CustomEmojis`, `nip47WalletConnect`, `nip51Lists`, `nip65RelayList`, `nip72Communities`, `nip78AppSpecific`, `nip86RelayManagement`, `nipA3PaymentTargets`, `nipB7Blossom`, `nipBCOnchainZaps`, `trustedAssertions`, `zap` | Extract | `model/nipNN<slug>` (mirror quartz) | pure event-state wrappers; bulk move |
| `topNavFeeds/`, `algoFeeds/`, `serverList/` | Extract | `feeds/`, `model/relayLists` | feed query orchestration is platform-agnostic |
| `edits/` | Extract | `model/nip51Lists` or `model/nip37Drafts` | encrypted list/draft state |
| `preferences/` (DataStore, KeyStoreEncryption, *SharedPreferences) | Abstract | `keystorage/`, `model/preferences` | schemas → commons; persistence actuals stay Android |
| `marmot/` (Android*Store) | Abstract | `keystorage/marmot` + commons logic | KeyStore-backed stores → expect/actual |
| `nip03Timestamp`, `nip11RelayInfo`, `privacyOptions` (OkHttp), `localRelays` | Abstract | `jvmAndroid` (HTTP) + commonMain (logic) | OkHttp → jvmAndroid; ContentResolver stays native |
| `torState/` | Extract | `tor/`, `model/torState` | relay-eval logic is pure |
| `EncryptedStorage`, `LocalPreferences` | Abstract | `keystorage/` | core logic + iface; SharedPrefs impl stays Android |
| `Amethyst.kt` (Application), `AppModules.kt` (DI) | **Native** | — | Android lifecycle / Context assembly |
**Highest value, lowest friction:** the ~16 `nipNN` state packages + `topNavFeeds`
(bulk pure moves), then `AccountSettings`/`AccountSyncedSettings`.
---
## 3. service/ — migration matrix
| Package (files) | Verdict | Destination | Note |
|---|---|---|---|
| `relayClient` (81) | **Native** | — (dedup) | Android orchestration wiring over commons `relayClient`; do NOT duplicate. Lift any LocalCache-free EOSE managers into commons. |
| `playback` (60) | Native | — | ExoPlayer/Media3, PIP — Android-only |
| `notifications` (15) | Native | — | foreground service, NotificationManager |
| `okhttp` (17) | Extract | `jvmAndroid` | OkHttp clients/interceptors/WS bridge; already JVM-shaped |
| `uploads` (25) | Abstract | `jvmAndroid` + commonMain iface | compression/codecs stay Android; orchestration → common |
| `images` (10) | Abstract | `jvmAndroid` (Coil setup) + commonMain (hashes already in commons) | |
| `cashu` (10) | Extract | `commonMain` (e.g. `model/nip60Cashu` or `service/cashu`) | parser is pure; split off Compose `GenericLoadable` |
| `call` (10) | Abstract | commonMain (state) + `jvmAndroid` (AudioManager/WebRTC) | WebRTC hard to abstract |
| `location` (6) | Abstract | commonMain (geohash) + `jvmAndroid` (Geocoder) | |
| `connectivity` (3) | Abstract | commonMain (model) + actual | trivial seam |
| `broadcast` (2), `ai` (2), `previews` (2) | Extract | commonMain (`service/`, `preview`) | previews already wraps commons parsers — consolidate |
| `lnurl` (2) | Extract / **dedup** | `jvmAndroid` `service/lnurl` | `LightningAddressResolver` duplicates commons — reconcile |
| `namecoin` (1) | Abstract | `jvmAndroid` | ElectrumX over OkHttp |
| `relays` (1, EOSE) | Extract / dedup | `relayClient`/`relays` | consolidate EOSE state |
| `eventCache` (1) | Abstract | commonMain | eviction logic pure; trim trigger via iface |
| `scheduledposts` (4), `calendar` (4) | Native (model extractable) | — | WorkManager-bound; model → commonMain if needed |
| `crashreports` (4), `logging` (3) | Native (DTO extractable) | — | Choreographer/Build native |
| `tts` (2), `cast` (2), `nests` (2) | Native | — | TTS / Cast / foreground service |
| root utils (`ByteFormatter`, `CountFormatter`, etc.) | Extract | `util/` | some may already exist — check first |
**Highest value, lowest friction:** `okhttp``jvmAndroid`, `cashu`, `broadcast`,
`ai`, `previews` consolidation, `lnurl` dedup, EOSE consolidation.
---
## 4. ui/ — migration matrix
commons rule (from `ARCHITECTURE.md`): cross-cutting composables → `ui/<area>`;
feature-specific → `<feature>/ui`. Deciding test: "could an unrelated feature
reuse this as-is?"
| Area (files) | Shareable | Destination | Blockers |
|---|---|---|---|
| `ui/dal` (5) | already re-exports | — | none — delete shims, point at `ui/feeds` |
| `ui/theme` (5) | ~90%, **dup** | merge into `ui/theme` | system-chrome (status/nav bar) stays Android |
| `ui/feeds` (12) | ~95% | `ui/feeds` | a few `R.string`, AccountVM |
| `ui/layouts` (9) | ~85% | `ui/layouts` | 2× `R.string`, 1× `INav` |
| `ui/components` (59) | ~75% | split `ui/components` (atoms) + `<feature>/ui` (galleries) | ~30 atoms clean; Coil/AccountVM/`R.string` on the rest |
| `ui/note` (184) | ~5070% | formatters→`ui/text`; avatars→`ui/elements`/`profile/ui`; renderers→`<nipNN>/ui` | AccountVM (29), `R.string` (24), Note model coupling |
| `ui/actions` (41) | ~65% | transforms→commonMain; VMs→`viewmodels`; media→stay Android | Context (7), AccountVM (7), `R.string` (8) |
| `ui/screen` (971) | mostly native | — | app screens; harvest reusable atoms opportunistically |
| `ui/navigation`, `ui/call`, `ui/cast`, `ui/broadcast`, `ui/tor` | native | — | platform wiring |
**Lowest-friction UI wins** (after string migration): TimeAgo/date formatters →
`ui/text`; `ClickableUrl/Email/Phone`; `GenericLoadable``ui/state`; input
transforms; `DisappearingBar*``ui/layouts`; feed-state composables; `ui/dal`
shim deletion; `ui/theme` consolidation. ~42% of note/components/actions
composables (112/266) carry no `R.string` at all.
---
## 5. Reconciliation / dedup backlog (do early — prevents drift)
1. `service/relayClient` (Android) vs `commons/relayClient` — keep Android as
wiring only; lift cache-independent EOSE managers down.
2. `service/lnurl/LightningAddressResolver` vs `commons/service/lnurl/…` — one
canonical copy.
3. `ui/note/PubKeyFormatter` vs `commons/util/PubKeyFormatter` — delete the dup.
4. `ui/theme` vs `commons/ui/theme` — single color/type source.
5. `ui/dal` vs `commons/ui/feeds` — delete re-export shims.
6. `service/relays/EOSE*` scattered — one EOSE-state home.
---
## 6. Phased roadmap
- **Phase A — Account keystone (largest, gates the rest).** Extract `Account` +
`LocalCache` business logic into `commons/model/account` + `model/cache`
behind the existing interfaces; amethyst keeps a thin Android adapter
(LocationState, Compose `@Stable`). Unblocks ViewModels + interactive UI.
- **Phase B — pure model & service extracts (parallel, low risk).** The ~16
`nipNN` model packages, `topNavFeeds`, `cashu`, `broadcast`, `ai`,
`previews`, EOSE/lnurl/PubKeyFormatter dedups, `okhttp``jvmAndroid`.
- **Phase C — storage/network seams.** `keystorage` expect/actual for prefs +
KeyStore + marmot stores; HTTP packages (nip11/nip03/namecoin/privacyOptions)
`jvmAndroid`.
- **Phase D — string migration + shared UI atoms.** Migrate the needed strings
into `composeResources`, then extract the low-friction UI list in §4.
- **Phase E — feature UI.** Note type renderers → `<nipNN>/ui`; read-only
renderers (Badge, Emoji, Poll display) before interactive ones (reactions,
composers). Much of `ui/note/creators` and reaction/zap rows may stay native
unless Desktop/iOS need them.
- **Never moves:** Application/DI, notifications, playback, WorkManager jobs,
TTS, Cast, foreground services, navigation shells, Activity-bound code.
## 7. Suggested first PR (smallest valuable slice)
Phase B subset — zero account dependency, immediately reusable by Desktop:
the dedups (§5.25.5) + a bulk move of 46 pure `nipNN` model packages
(`nip17Dms`, `nip30CustomEmojis`, `nip47WalletConnect`, `nip65RelayList`,
`trustedAssertions`, `zap`). Mechanical, compile-verifiable, no new abstractions.
Phase A is its own large PR and should be planned separately.
---
## 8. Architecture end-state (why the tree below is shaped this way)
The ports already exist in commons (`IAccount`, `ICacheProvider`,
`ICacheEventStream`) but **every app reimplements them**`amethyst`
(`object LocalCache` 3917 LOC + `class Account` 3565 LOC), `desktopApp`
(`class DesktopLocalCache` 739 LOC + `DesktopIAccount` 278 LOC), `cli`
(stateless). Crucially these implementations are **platform-agnostic already**
(`Account.kt` has one android import — `@Stable` — plus a single `LocationState`
coupling; `LocalCache.kt` has two; `DesktopLocalCache` has zero). So this is
**accidental forking, not essential divergence.** The end-state is **one
canonical implementation per port, living in commons**, with apps keeping only
thin platform adapters.
Two consequences shape the package tree:
1. **Decompose the monoliths; don't relocate them.** `Account` (28 `StateFlow`s +
~170 `suspend fun`s) splits into *state* (per-user reactive state), *actions*
(the verbs), and a thin *façade* implementing `IAccount`. `LocalCache`
becomes the cache *engine* as a `class` (so every platform gets multi-account;
Desktop already needed it).
2. **Litmus test = CLI-constructibility.** `signer → cache → account state →
actions → relay client → pure services` must build in a headless `main()`
with no Compose/Android. Anything that can't is mislayered or missing a port.
Layer is the primary axis; NIP is the secondary axis (`nipNN<slug>` matching
quartz). The raw per-NIP list **state** lives in `model/nipNN<slug>`; the
account's *composed/derived* view of those lists lives in `model/account/*`. The
generic `state/` package (load-a-thing `StateFlow` machines) is **not** where
account-specific state goes.
## 9. Target package hierarchy (the destination tree)
`commonMain/.../commons/` (✚ = new package, ⤺ = merge into existing, ✔ = exists):
```
model/
├─ cache/
│ ├─ ICacheProvider.kt ICacheEventStream.kt ✔ read ports
│ ├─ ILocalCache.kt ✚ write port (from amethyst)
│ ├─ LocalCache.kt ← unified cache ENGINE as a CLASS (amethyst object + DesktopLocalCache collapse here)
│ └─ UserMetadataCache.kt ✔
├─ account/
│ ├─ Account.kt ← façade implementing IAccount (from amethyst Account)
│ ├─ AccountInfo.kt AccountStorage.kt SignerType.kt ✔
│ ├─ settings/ AccountSettings, AccountSyncedSettings(+Internal) ✚
│ ├─ follows/ derived per-feed live-follow lists + all-follows merge ✚
│ ├─ relays/ account relay sets + outbox relay cache ✚
│ └─ hidden/ LiveHiddenUsers — mute/spam aggregated view ✚
├─ nip02FollowList/ ⤺ raw Kind-3 follow-list state (amethyst nip02FollowLists)
├─ nip65RelayList/ ⤺ relay-list state
├─ nip51Lists/ ⤺ mute / bookmark / people lists
├─ nip30CustomEmojis/ ⤺ emoji-pack state
├─ nip01Core/ ⤺ kind-0 user-metadata relay-hint state (amethyst nip01UserMetadata)
├─ nip17Dm/ nip47WalletConnect/ nip60Cashu/ nip72ModCommunities/
│ nip78AppData/ nip85TrustedAssertions/ nip86RelayManagement/
│ nipA3PaymentTargets/ nipB7Blossom/ nipBCOnchainZaps/ ✚ per-NIP state holders (bulk)
└─ settings/ ✚ UiSettings / UiSettingsFlow (global, NOT account-scoped)
state/ ✔ generic StateFlow machines (FollowState, …) — unchanged
actions/ ✔ the account "verbs" — extend, don't duplicate
│ existing: DmActions FollowActions SearchActions ZapActions ZapSplitResolver
│ add: PostActions ReactionActions BookmarkActions MuteActions
│ RelayActions ProfileActions GroupActions DeletionActions …
feeds/
├─ custom/ ✔
├─ topNav/ ✚ (amethyst model/topNavFeeds)
└─ algo/ ✚ (amethyst model/algoFeeds)
relayClient/ ✔ + cache-free EOSE managers lifted from amethyst service/relays
keystorage/
├─ SecureKeyStorage.kt ✔
├─ preferences/ ✚ LocalPreferences logic + DataStore/SharedPrefs schema (+actuals)
├─ account/ ✚ account persistence (pairs with model/account/AccountStorage)
└─ marmot/ ✚ MLS key-package / message / group-state store ports (+actuals)
service/
├─ upload/ nwc/ lnurl/ ✔ (lnurl: dedup amethyst LightningAddressResolver)
├─ broadcast/ ai/ connectivity/ eventCache/ ✚ commonMain (pure)
└─ okhttp/ nip11RelayInfo/ nip03Timestamp/ namecoin/ privacyOptions/ ✚ jvmAndroid (OkHttp/Jackson)
tor/ ✔ + torState relay-evaluation logic
```
UI destinations follow `commons/ARCHITECTURE.md` (cross-cutting → `ui/<area>`,
feature-specific → `<feature>/ui`) and are detailed in §4 — out of scope for the
"common objects" core tier above.
## 10. Package-naming decisions (the rules behind §9)
1. **`model/nipNN<slug>` (raw list state) vs `model/account/*` (composed view).**
`model/nip02FollowList` holds the raw Kind-3 list; `model/account/follows`
holds the user's *derived* per-feed follow flows that compose it. Same for
relays (`nip65RelayList` vs `account/relays`) and mutes (`nip51Lists` vs
`account/hidden`).
2. **NIP slugs match quartz exactly**, normalizing amethyst's drift:
`nip02FollowLists→nip02FollowList`, `nip01UserMetadata→nip01Core`,
`nip30CustomEmojis` (keep — existing commons name), `nip72Communities→
nip72ModCommunities`, `nip78AppSpecific→nip78AppData`,
`trustedAssertions→nip85TrustedAssertions` (or keep the existing readable
`model/trustedAssertions` — pick one and converge).
3. **`state/` stays generic.** Account-specific state never goes here; it goes in
`model/account/*`. `state/` is reusable load-a-thing `StateFlow` machines.
4. **`actions/` = verbs, `model/` = nouns/state, `cache/` = the store.** The 170
`Account` methods become `*Actions` files grouped by concern, taking
`signer + cache + relayClient`.
5. **`keystorage/` = persistence ports + actuals; the settings *schema* is
`model/account/settings`.** Schema (what) and persistence (how/where) are
separate packages.
6. **`service/` name is source-set-agnostic.** Pure services sit in `commonMain`,
OkHttp/Jackson ones in `jvmAndroid` — same `service/<x>` path, different
source set (per `ARCHITECTURE.md` §"Source sets").
7. **Keep the name `LocalCache`** for the engine class (222 amethyst files / 560
call-sites reference it); amethyst keeps a thin `object LocalCache` delegating
to one shared `class` instance during transition.
@@ -18,7 +18,7 @@
* 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.compose
package com.vitorpamplona.amethyst.commons.ui.text
import androidx.compose.ui.text.TextRange
import androidx.compose.ui.text.input.TextFieldValue
@@ -18,7 +18,7 @@
* 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.chess
package com.vitorpamplona.amethyst.commons.nip64Chess
import android.content.Context
import android.content.SharedPreferences
@@ -18,7 +18,7 @@
* 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.utils
package com.vitorpamplona.amethyst.commons.util
// Debug flag for commons module - kept false to avoid application dependencies
// Application-level modules (amethyst, desktopApp) can implement their own debug timing
@@ -18,7 +18,7 @@
* 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.data
package com.vitorpamplona.amethyst.commons.model.cache
import com.vitorpamplona.quartz.nip01Core.metadata.UserMetadata
import kotlinx.coroutines.flow.MutableStateFlow
@@ -18,7 +18,7 @@
* 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.compose.article
package com.vitorpamplona.amethyst.commons.nip23LongContent.ui.article
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@@ -18,7 +18,7 @@
* 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.compose.article
package com.vitorpamplona.amethyst.commons.nip23LongContent.ui.article
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
@@ -18,7 +18,7 @@
* 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.compose.editor
package com.vitorpamplona.amethyst.commons.nip23LongContent.ui.editor
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -18,7 +18,7 @@
* 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.compose.editor
package com.vitorpamplona.amethyst.commons.nip23LongContent.ui.editor
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
@@ -18,7 +18,7 @@
* 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.compose.editor
package com.vitorpamplona.amethyst.commons.nip23LongContent.ui.editor
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
@@ -18,7 +18,7 @@
* 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.compose.nip53LiveActivities
package com.vitorpamplona.amethyst.commons.nip53LiveActivities.ui
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
@@ -18,7 +18,7 @@
* 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.chess
package com.vitorpamplona.amethyst.commons.nip64Chess
import com.vitorpamplona.amethyst.commons.util.KmpLock
import com.vitorpamplona.amethyst.commons.util.withLock
@@ -18,7 +18,7 @@
* 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.chess
package com.vitorpamplona.amethyst.commons.nip64Chess
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
@@ -18,7 +18,7 @@
* 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.chess
package com.vitorpamplona.amethyst.commons.nip64Chess
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.animateContentSize
@@ -18,7 +18,7 @@
* 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.chess
package com.vitorpamplona.amethyst.commons.nip64Chess
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.normalizeRelayUrl
@@ -18,7 +18,7 @@
* 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.chess
package com.vitorpamplona.amethyst.commons.nip64Chess
/**
* Persists dismissed chess game IDs locally per user.
@@ -18,7 +18,7 @@
* 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.chess
package com.vitorpamplona.amethyst.commons.nip64Chess
import com.vitorpamplona.amethyst.commons.util.KmpLock
import com.vitorpamplona.amethyst.commons.util.withLock
@@ -18,7 +18,7 @@
* 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.chess
package com.vitorpamplona.amethyst.commons.nip64Chess
import com.vitorpamplona.quartz.utils.Log
import kotlinx.coroutines.CoroutineScope
@@ -18,7 +18,7 @@
* 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.chess
package com.vitorpamplona.amethyst.commons.nip64Chess
import com.vitorpamplona.quartz.nip64Chess.ChessEngine
import com.vitorpamplona.quartz.nip64Chess.ChessStateReconstructor
@@ -18,7 +18,7 @@
* 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.chess
package com.vitorpamplona.amethyst.commons.nip64Chess
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
@@ -18,7 +18,7 @@
* 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.chess
package com.vitorpamplona.amethyst.commons.nip64Chess
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.clickable
@@ -18,7 +18,7 @@
* 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.chess
package com.vitorpamplona.amethyst.commons.nip64Chess
import com.vitorpamplona.amethyst.commons.util.KmpLock
import com.vitorpamplona.amethyst.commons.util.withLock
@@ -18,7 +18,7 @@
* 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.chess
package com.vitorpamplona.amethyst.commons.nip64Chess
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip64Chess.Color
@@ -18,7 +18,7 @@
* 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.chess
package com.vitorpamplona.amethyst.commons.nip64Chess
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.SolidColor
@@ -18,7 +18,7 @@
* 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.chess
package com.vitorpamplona.amethyst.commons.nip64Chess
import androidx.compose.foundation.background
import androidx.compose.foundation.border
@@ -18,7 +18,7 @@
* 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.chess
package com.vitorpamplona.amethyst.commons.nip64Chess
import com.vitorpamplona.amethyst.commons.util.KmpLock
import com.vitorpamplona.amethyst.commons.util.withLock
@@ -18,7 +18,7 @@
* 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.chess
package com.vitorpamplona.amethyst.commons.nip64Chess
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.animateContentSize
@@ -18,7 +18,7 @@
* 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.chess
package com.vitorpamplona.amethyst.commons.nip64Chess
/**
* Platform-specific metadata provider for enriching chess challenges with display info.
@@ -18,7 +18,7 @@
* 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.chess
package com.vitorpamplona.amethyst.commons.nip64Chess
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
@@ -18,7 +18,7 @@
* 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.chess
package com.vitorpamplona.amethyst.commons.nip64Chess
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.tween
@@ -18,7 +18,7 @@
* 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.chess
package com.vitorpamplona.amethyst.commons.nip64Chess
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
@@ -18,7 +18,7 @@
* 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.chess
package com.vitorpamplona.amethyst.commons.nip64Chess
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
@@ -18,7 +18,7 @@
* 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.chess.subscription
package com.vitorpamplona.amethyst.commons.nip64Chess.subscription
import com.vitorpamplona.quartz.nip01Core.relay.client.pool.RelayBasedFilter
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
@@ -18,7 +18,7 @@
* 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.chess.subscription
package com.vitorpamplona.amethyst.commons.nip64Chess.subscription
import kotlinx.coroutines.flow.StateFlow
@@ -18,7 +18,7 @@
* 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.chess.subscription
package com.vitorpamplona.amethyst.commons.nip64Chess.subscription
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
@@ -18,7 +18,7 @@
* 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.chess.subscription
package com.vitorpamplona.amethyst.commons.nip64Chess.subscription
/**
* Shared time window constants for chess subscriptions.
@@ -34,7 +34,7 @@ quartz/commonMain/.../nipACWebRtcCalls/
├── tags/ # CallType, CallIdTag, etc.
└── WebRtcCallFactory.kt # Sign + gift-wrap helpers (P2P + group variants)
commons/commonMain/.../call/ # ← this package
commons/commonMain/.../nipACWebRtcCalls/ # ← this package
├── CallState.kt # Sealed state + EndReason enum
├── CallManager.kt # State machine, signaling dispatch, timeouts
├── PeerSession.kt # Platform-neutral per-peer signaling interface
@@ -54,7 +54,7 @@ amethyst/.../ui/call/ # Android call UI
```
`quartz` is pure Kotlin/Multiplatform protocol code with **zero** WebRTC or
Android dependencies. `commons/.../call/` is also platform-neutral — it holds
Android dependencies. `commons/.../nipACWebRtcCalls/` is also platform-neutral — it holds
the state machine and a `PeerSession` abstraction. All `org.webrtc` code lives
in `amethyst/service/call/`.
@@ -255,7 +255,7 @@ coverage.
## 10. Testing
`commons/commonTest/.../call/CallManagerTest.kt` is the canonical reference
`commons/commonTest/.../nipACWebRtcCalls/CallManagerTest.kt` is the canonical reference
for expected state transitions. It uses real `NostrSignerInternal` keys so
the factory-sign-wrap pipeline is exercised end-to-end, with `publishEvent`
captured into a list for assertions. Construct events directly with the
@@ -18,10 +18,10 @@
* 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.call
package com.vitorpamplona.amethyst.commons.nipACWebRtcCalls
import com.vitorpamplona.amethyst.commons.call.CallManager.Companion.CALL_TIMEOUT_MS
import com.vitorpamplona.amethyst.commons.call.CallManager.Companion.CONNECTING_TIMEOUT_MS
import com.vitorpamplona.amethyst.commons.nipACWebRtcCalls.CallManager.Companion.CALL_TIMEOUT_MS
import com.vitorpamplona.amethyst.commons.nipACWebRtcCalls.CallManager.Companion.CONNECTING_TIMEOUT_MS
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
@@ -18,7 +18,7 @@
* 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.call
package com.vitorpamplona.amethyst.commons.nipACWebRtcCalls
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nipACWebRtcCalls.events.CallAnswerEvent
@@ -18,7 +18,7 @@
* 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.call
package com.vitorpamplona.amethyst.commons.nipACWebRtcCalls
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable
@@ -18,7 +18,7 @@
* 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.call
package com.vitorpamplona.amethyst.commons.nipACWebRtcCalls
/**
* Represents a single ICE candidate received from a peer.
@@ -18,7 +18,7 @@
* 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.call
package com.vitorpamplona.amethyst.commons.nipACWebRtcCalls
import com.vitorpamplona.amethyst.commons.util.KmpLock
import com.vitorpamplona.amethyst.commons.util.withLock
@@ -18,7 +18,7 @@
* 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.profile
package com.vitorpamplona.amethyst.commons.profile.ui
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.animateContentSize
@@ -52,6 +52,7 @@ import androidx.compose.ui.unit.dp
import com.vitorpamplona.amethyst.commons.icons.symbols.Icon
import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbol
import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
import com.vitorpamplona.amethyst.commons.profile.ProfileBroadcastStatus
/**
* Shared banner showing profile metadata broadcast progress.
@@ -20,9 +20,9 @@
*/
package com.vitorpamplona.amethyst.commons.search
import com.vitorpamplona.amethyst.commons.chess.RelaySyncState
import com.vitorpamplona.amethyst.commons.chess.RelaySyncStatus
import com.vitorpamplona.amethyst.commons.model.User
import com.vitorpamplona.amethyst.commons.nip64Chess.RelaySyncState
import com.vitorpamplona.amethyst.commons.nip64Chess.RelaySyncStatus
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.displayUrl
@@ -18,7 +18,7 @@
* 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.compose.elements
package com.vitorpamplona.amethyst.commons.ui.elements
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
@@ -20,7 +20,7 @@
*/
package com.vitorpamplona.amethyst.commons.ui.feeds
import com.vitorpamplona.amethyst.commons.utils.logTime
import com.vitorpamplona.amethyst.commons.util.logTime
abstract class AdditiveFeedFilter<T> :
FeedFilter<T>(),
@@ -28,7 +28,7 @@ import com.vitorpamplona.amethyst.commons.model.cache.ICacheProvider
import com.vitorpamplona.amethyst.commons.service.BasicBundledInsert
import com.vitorpamplona.amethyst.commons.service.BasicBundledUpdate
import com.vitorpamplona.amethyst.commons.threading.checkNotInMainThread
import com.vitorpamplona.amethyst.commons.utils.equalImmutableLists
import com.vitorpamplona.amethyst.commons.util.equalImmutableLists
import com.vitorpamplona.quartz.nip09Deletions.DeletionEvent
import com.vitorpamplona.quartz.utils.flattenToSet
import kotlinx.collections.immutable.ImmutableList
@@ -20,7 +20,7 @@
*/
package com.vitorpamplona.amethyst.commons.ui.feeds
import com.vitorpamplona.amethyst.commons.utils.logTime
import com.vitorpamplona.amethyst.commons.util.logTime
abstract class FeedFilter<T> : IFeedFilter<T> {
override fun loadTop(): List<T> {
@@ -18,7 +18,7 @@
* 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.compose.layouts
package com.vitorpamplona.amethyst.commons.ui.layouts
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
@@ -18,7 +18,7 @@
* 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.compose
package com.vitorpamplona.amethyst.commons.ui.state
import androidx.collection.LruCache
import androidx.compose.runtime.Composable
@@ -18,7 +18,7 @@
* 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.compose
package com.vitorpamplona.amethyst.commons.ui.state
import androidx.collection.LruCache
import androidx.compose.runtime.Composable
@@ -18,7 +18,7 @@
* 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.compose
package com.vitorpamplona.amethyst.commons.ui.text
import androidx.compose.foundation.text.input.TextFieldState
import androidx.compose.ui.text.TextRange
@@ -18,7 +18,7 @@
* 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.compose
package com.vitorpamplona.amethyst.commons.ui.text
import androidx.compose.ui.text.TextRange
import androidx.compose.ui.text.input.TextFieldValue
@@ -18,7 +18,7 @@
* 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.utils
package com.vitorpamplona.amethyst.commons.util
import com.vitorpamplona.quartz.utils.Log
import kotlin.time.DurationUnit
@@ -18,7 +18,7 @@
* 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.utils
package com.vitorpamplona.amethyst.commons.util
import kotlinx.collections.immutable.ImmutableList
@@ -18,7 +18,7 @@
* 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.call
package com.vitorpamplona.amethyst.commons.nipACWebRtcCalls
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair

Some files were not shown because too many files have changed in this diff Show More