Merge branch 'vitorpamplona:main' into add-feed-indicator-to-live-bubbles

This commit is contained in:
David Kaspar
2025-09-18 21:12:14 +01:00
committed by GitHub
13 changed files with 91 additions and 122 deletions

View File

@@ -33,34 +33,32 @@ import kotlin.time.measureTimedValue
@Suppress("SENSELESS_COMPARISON") @Suppress("SENSELESS_COMPARISON")
val isDebug = BuildConfig.DEBUG || BuildConfig.BUILD_TYPE == "benchmark" val isDebug = BuildConfig.DEBUG || BuildConfig.BUILD_TYPE == "benchmark"
fun debugState(context: Context) { private const val STATE_DUMP_TAG = "STATE DUMP"
// Amethyst.instance.client
// .allSubscriptions()
// .forEach { Log.d("STATE DUMP", "${it.key} ${it.value.filters.joinToString { it.filter.toJson() }}") }
fun debugState(context: Context) {
val totalMemoryMb = Runtime.getRuntime().totalMemory() / (1024 * 1024) val totalMemoryMb = Runtime.getRuntime().totalMemory() / (1024 * 1024)
val freeMemoryMb = Runtime.getRuntime().freeMemory() / (1024 * 1024) val freeMemoryMb = Runtime.getRuntime().freeMemory() / (1024 * 1024)
val maxMemoryMb = Runtime.getRuntime().maxMemory() / (1024 * 1024) val maxMemoryMb = Runtime.getRuntime().maxMemory() / (1024 * 1024)
val jvmHeapAllocatedMb = totalMemoryMb - freeMemoryMb val jvmHeapAllocatedMb = totalMemoryMb - freeMemoryMb
Log.d("STATE DUMP", "Total Heap Allocated: $jvmHeapAllocatedMb/$maxMemoryMb MB") Log.d(STATE_DUMP_TAG, "Total Heap Allocated: $jvmHeapAllocatedMb/$maxMemoryMb MB")
val nativeHeap = Debug.getNativeHeapAllocatedSize() / (1024 * 1024) val nativeHeap = Debug.getNativeHeapAllocatedSize() / (1024 * 1024)
val maxNative = Debug.getNativeHeapSize() / (1024 * 1024) val maxNative = Debug.getNativeHeapSize() / (1024 * 1024)
Log.d("STATE DUMP", "Total Native Heap Allocated: $nativeHeap/$maxNative MB") Log.d(STATE_DUMP_TAG, "Total Native Heap Allocated: $nativeHeap/$maxNative MB")
val activityManager: ActivityManager? = context.getSystemService() val activityManager: ActivityManager? = context.getSystemService()
if (activityManager != null) { if (activityManager != null) {
val isLargeHeap = (context.applicationInfo.flags and ApplicationInfo.FLAG_LARGE_HEAP) != 0 val isLargeHeap = (context.applicationInfo.flags and ApplicationInfo.FLAG_LARGE_HEAP) != 0
val memClass = if (isLargeHeap) activityManager.largeMemoryClass else activityManager.memoryClass val memClass = if (isLargeHeap) activityManager.largeMemoryClass else activityManager.memoryClass
Log.d("STATE DUMP", "Memory Class $memClass MB (largeHeap $isLargeHeap)") Log.d(STATE_DUMP_TAG, "Memory Class $memClass MB (largeHeap $isLargeHeap)")
} }
Log.d( Log.d(
"STATE DUMP", STATE_DUMP_TAG,
"Connected Relays: " + "Connected Relays: " +
Amethyst.instance.client Amethyst.instance.client
.relayStatusFlow() .relayStatusFlow()
@@ -71,16 +69,16 @@ fun debugState(context: Context) {
) )
Log.d( Log.d(
"STATE DUMP", STATE_DUMP_TAG,
"Image Disk Cache ${(Amethyst.instance.diskCache.size) / (1024 * 1024)}/${(Amethyst.instance.diskCache.maxSize) / (1024 * 1024)} MB", "Image Disk Cache ${(Amethyst.instance.diskCache.size) / (1024 * 1024)}/${(Amethyst.instance.diskCache.maxSize) / (1024 * 1024)} MB",
) )
Log.d( Log.d(
"STATE DUMP", STATE_DUMP_TAG,
"Image Memory Cache ${(Amethyst.instance.memoryCache.size) / (1024 * 1024)}/${(Amethyst.instance.memoryCache.maxSize) / (1024 * 1024)} MB", "Image Memory Cache ${(Amethyst.instance.memoryCache.size) / (1024 * 1024)}/${(Amethyst.instance.memoryCache.maxSize) / (1024 * 1024)} MB",
) )
Log.d( Log.d(
"STATE DUMP", STATE_DUMP_TAG,
"Notes: " + "Notes: " +
LocalCache.notes.filter { _, it -> it.flowSet != null }.size + LocalCache.notes.filter { _, it -> it.flowSet != null }.size +
" / " + " / " +
@@ -89,7 +87,7 @@ fun debugState(context: Context) {
LocalCache.notes.size(), LocalCache.notes.size(),
) )
Log.d( Log.d(
"STATE DUMP", STATE_DUMP_TAG,
"Addressables: " + "Addressables: " +
LocalCache.addressables.filter { _, it -> it.flowSet != null }.size + LocalCache.addressables.filter { _, it -> it.flowSet != null }.size +
" / " + " / " +
@@ -98,7 +96,7 @@ fun debugState(context: Context) {
LocalCache.addressables.size(), LocalCache.addressables.size(),
) )
Log.d( Log.d(
"STATE DUMP", STATE_DUMP_TAG,
"Users: " + "Users: " +
LocalCache.users.filter { _, it -> it.flowSet != null }.size + LocalCache.users.filter { _, it -> it.flowSet != null }.size +
" / " + " / " +
@@ -107,7 +105,7 @@ fun debugState(context: Context) {
LocalCache.users.size(), LocalCache.users.size(),
) )
Log.d( Log.d(
"STATE DUMP", STATE_DUMP_TAG,
"Public Chat Channels: " + "Public Chat Channels: " +
LocalCache.publicChatChannels.filter { _, it -> it.flowSet != null }.size + LocalCache.publicChatChannels.filter { _, it -> it.flowSet != null }.size +
" / " + " / " +
@@ -116,7 +114,7 @@ fun debugState(context: Context) {
LocalCache.publicChatChannels.values().sumOf { it.notes.size() }, LocalCache.publicChatChannels.values().sumOf { it.notes.size() },
) )
Log.d( Log.d(
"STATE DUMP", STATE_DUMP_TAG,
"Live Chat Channels: " + "Live Chat Channels: " +
LocalCache.liveChatChannels.filter { _, it -> it.flowSet != null }.size + LocalCache.liveChatChannels.filter { _, it -> it.flowSet != null }.size +
" / " + " / " +
@@ -125,7 +123,7 @@ fun debugState(context: Context) {
LocalCache.liveChatChannels.values().sumOf { it.notes.size() }, LocalCache.liveChatChannels.values().sumOf { it.notes.size() },
) )
Log.d( Log.d(
"STATE DUMP", STATE_DUMP_TAG,
"Ephemeral Chat Channels: " + "Ephemeral Chat Channels: " +
LocalCache.ephemeralChannels.filter { _, it -> it.flowSet != null }.size + LocalCache.ephemeralChannels.filter { _, it -> it.flowSet != null }.size +
" / " + " / " +
@@ -135,7 +133,7 @@ fun debugState(context: Context) {
) )
LocalCache.chatroomList.forEach { key, room -> LocalCache.chatroomList.forEach { key, room ->
Log.d( Log.d(
"STATE DUMP", STATE_DUMP_TAG,
"Private Chats $key: " + "Private Chats $key: " +
room.rooms.size() + room.rooms.size() +
" / " + " / " +
@@ -143,12 +141,12 @@ fun debugState(context: Context) {
) )
} }
Log.d( Log.d(
"STATE DUMP", STATE_DUMP_TAG,
"Deletion Events: " + "Deletion Events: " +
LocalCache.deletionIndex.size(), LocalCache.deletionIndex.size(),
) )
Log.d( Log.d(
"STATE DUMP", STATE_DUMP_TAG,
"Observable Events: " + "Observable Events: " +
LocalCache.observablesByKindAndETag.size + LocalCache.observablesByKindAndETag.size +
" / " + " / " +
@@ -156,13 +154,13 @@ fun debugState(context: Context) {
) )
Log.d( Log.d(
"STATE DUMP", STATE_DUMP_TAG,
"Spam: " + "Spam: " +
LocalCache.antiSpam.spamMessages.size() + " / " + LocalCache.antiSpam.recentEventIds.size() + " / " + LocalCache.antiSpam.recentAddressables.size(), LocalCache.antiSpam.spamMessages.size() + " / " + LocalCache.antiSpam.recentEventIds.size() + " / " + LocalCache.antiSpam.recentAddressables.size(),
) )
Log.d( Log.d(
"STATE DUMP", STATE_DUMP_TAG,
"Memory used by Events: " + "Memory used by Events: " +
LocalCache.notes.sumOf { _, note -> note.event?.countMemory() ?: 0 } / (1024 * 1024) + LocalCache.notes.sumOf { _, note -> note.event?.countMemory() ?: 0 } / (1024 * 1024) +
" MB", " MB",
@@ -179,10 +177,10 @@ fun debugState(context: Context) {
.sumByGroup(groupMap = { _, it -> it.event?.kind }, sumOf = { _, it -> it.event?.countMemory()?.toLong() ?: 0L }) .sumByGroup(groupMap = { _, it -> it.event?.kind }, sumOf = { _, it -> it.event?.countMemory()?.toLong() ?: 0L })
qttNotes.toList().sortedByDescending { bytesNotes[it.first] }.forEach { (kind, qtt) -> qttNotes.toList().sortedByDescending { bytesNotes[it.first] }.forEach { (kind, qtt) ->
Log.d("STATE DUMP", "Kind ${kind.toString().padStart(5,' ')}:\t${qtt.toString().padStart(6,' ')} elements\t${bytesNotes[kind]?.div((1024 * 1024))}MB ") Log.d(STATE_DUMP_TAG, "Kind ${kind.toString().padStart(5,' ')}:\t${qtt.toString().padStart(6,' ')} elements\t${bytesNotes[kind]?.div((1024 * 1024))}MB ")
} }
qttAddressables.toList().sortedByDescending { bytesNotes[it.first] }.forEach { (kind, qtt) -> qttAddressables.toList().sortedByDescending { bytesNotes[it.first] }.forEach { (kind, qtt) ->
Log.d("STATE DUMP", "Kind ${kind.toString().padStart(5,' ')}:\t${qtt.toString().padStart(6,' ')} elements\t${bytesAddressables[kind]?.div((1024 * 1024))}MB ") Log.d(STATE_DUMP_TAG, "Kind ${kind.toString().padStart(5,' ')}:\t${qtt.toString().padStart(6,' ')} elements\t${bytesAddressables[kind]?.div((1024 * 1024))}MB ")
} }
} }

View File

@@ -75,8 +75,9 @@ class AccountPreferenceStores(
) )
} }
fun removeAccount(npub: String) { fun removeAccount(npub: String): Boolean {
file(npub).delete() val deleted = file(npub).delete()
storeCache.remove(npub) storeCache.remove(npub)
return deleted
} }
} }

View File

@@ -69,8 +69,9 @@ class AccountSecretsEncryptedStores(
serializer = Nip47WalletConnect.Nip47URI::serializer, serializer = Nip47WalletConnect.Nip47URI::serializer,
) )
fun removeAccount(npub: String) { fun removeAccount(npub: String): Boolean {
file(npub).delete() val deleted = file(npub).delete()
storeCache.remove(npub) storeCache.remove(npub)
return deleted
} }
} }

View File

@@ -27,16 +27,18 @@ import okio.FileNotFoundException
import java.io.FileInputStream import java.io.FileInputStream
import java.io.InputStreamReader import java.io.InputStreamReader
private const val STACK_TRACE_FILENAME = "stack.trace"
class CrashReportCache( class CrashReportCache(
val appContext: Context, val appContext: Context,
) { ) {
private fun outputStream() = appContext.openFileOutput("stack.trace", Context.MODE_PRIVATE) private fun outputStream() = appContext.openFileOutput(STACK_TRACE_FILENAME, Context.MODE_PRIVATE)
private fun deleteReport() = appContext.deleteFile("stack.trace") private fun deleteReport() = appContext.deleteFile(STACK_TRACE_FILENAME)
private fun inputStreamOrNull(): FileInputStream? = private fun inputStreamOrNull(): FileInputStream? =
try { try {
appContext.openFileInput("stack.trace") appContext.openFileInput(STACK_TRACE_FILENAME)
} catch (_: FileNotFoundException) { } catch (_: FileNotFoundException) {
null null
} }

View File

@@ -52,13 +52,12 @@ import com.vitorpamplona.quartz.utils.TimeUtils
import java.math.BigDecimal import java.math.BigDecimal
import kotlin.coroutines.cancellation.CancellationException import kotlin.coroutines.cancellation.CancellationException
private const val TAG = "EventNotificationConsumer"
private const val ACCOUNT_QUERY_PARAM = "?account="
class EventNotificationConsumer( class EventNotificationConsumer(
private val applicationContext: Context, private val applicationContext: Context,
) { ) {
companion object {
const val TAG = "EventNotificationConsumer"
}
suspend fun consume(event: GiftWrapEvent) { suspend fun consume(event: GiftWrapEvent) {
Log.d(TAG, "New Notification Arrived") Log.d(TAG, "New Notification Arrived")
@@ -210,7 +209,7 @@ class EventNotificationConsumer(
val user = chatNote.author?.toBestDisplayName() ?: "" val user = chatNote.author?.toBestDisplayName() ?: ""
val userPicture = chatNote.author?.profilePicture() val userPicture = chatNote.author?.profilePicture()
val noteUri = val noteUri =
chatNote.toNEvent() + "?account=" + chatNote.toNEvent() + ACCOUNT_QUERY_PARAM +
account.signer.pubKey account.signer.pubKey
.hexToByteArray() .hexToByteArray()
.toNpub() .toNpub()
@@ -255,7 +254,7 @@ class EventNotificationConsumer(
val user = chatNote.author?.toBestDisplayName() ?: "" val user = chatNote.author?.toBestDisplayName() ?: ""
val userPicture = chatNote.author?.profilePicture() val userPicture = chatNote.author?.profilePicture()
val noteUri = val noteUri =
chatNote.toNEvent() + "?account=" + chatNote.toNEvent() + ACCOUNT_QUERY_PARAM +
account.signer.pubKey account.signer.pubKey
.hexToByteArray() .hexToByteArray()
.toNpub() .toNpub()
@@ -297,7 +296,7 @@ class EventNotificationConsumer(
val user = note.author?.toBestDisplayName() ?: "" val user = note.author?.toBestDisplayName() ?: ""
val userPicture = note.author?.profilePicture() val userPicture = note.author?.profilePicture()
val noteUri = val noteUri =
note.toNEvent() + "?account=" + note.toNEvent() + ACCOUNT_QUERY_PARAM +
account.signer.pubKey account.signer.pubKey
.hexToByteArray() .hexToByteArray()
.toNpub() .toNpub()
@@ -324,17 +323,17 @@ class EventNotificationConsumer(
signer: NostrSigner, signer: NostrSigner,
): String? { ): String? {
val event = note.event val event = note.event
when (event) { return when (event) {
is PrivateDmEvent -> { is PrivateDmEvent -> {
return event.decryptContent(signer) event.decryptContent(signer)
} }
is LnZapRequestEvent -> { is LnZapRequestEvent -> {
return decryptZapContentAuthor(event, signer)?.content decryptZapContentAuthor(event, signer)?.content
} }
else -> { else -> {
return event?.content event?.content
} }
} }
} }
@@ -402,7 +401,7 @@ class EventNotificationConsumer(
} }
val userPicture = senderInfo.first.profilePicture() val userPicture = senderInfo.first.profilePicture()
val noteUri = val noteUri =
"notifications?account=" + "notifications$ACCOUNT_QUERY_PARAM" +
account.signer.pubKey account.signer.pubKey
.hexToByteArray() .hexToByteArray()
.toNpub() .toNpub()
@@ -437,7 +436,7 @@ class EventNotificationConsumer(
val userPicture = senderInfo.first.profilePicture() val userPicture = senderInfo.first.profilePicture()
val noteUri = val noteUri =
"notifications?account=" + "notifications$ACCOUNT_QUERY_PARAM" +
account.signer.pubKey account.signer.pubKey
.hexToByteArray() .hexToByteArray()
.toNpub() .toNpub()

View File

@@ -37,7 +37,6 @@ val DEFAULT_MEDIA_SERVERS: List<ServerName> =
ServerName("Nostr.Build", "https://nostr.build", ServerType.NIP96), ServerName("Nostr.Build", "https://nostr.build", ServerType.NIP96),
ServerName("NostrCheck.me (NIP-96)", "https://nostrcheck.me", ServerType.NIP96), ServerName("NostrCheck.me (NIP-96)", "https://nostrcheck.me", ServerType.NIP96),
ServerName("Sovbit", "https://files.sovbit.host", ServerType.NIP96), ServerName("Sovbit", "https://files.sovbit.host", ServerType.NIP96),
ServerName("Void.cat", "https://void.cat", ServerType.NIP96),
ServerName("Satellite (Paid)", "https://cdn.satellite.earth", ServerType.Blossom), ServerName("Satellite (Paid)", "https://cdn.satellite.earth", ServerType.Blossom),
ServerName("NostrCheck.me (Blossom)", "https://cdn.nostrcheck.me", ServerType.Blossom), ServerName("NostrCheck.me (Blossom)", "https://cdn.nostrcheck.me", ServerType.Blossom),
ServerName("Nostr.Download", "https://nostr.download", ServerType.Blossom), ServerName("Nostr.Download", "https://nostr.download", ServerType.Blossom),

View File

@@ -20,27 +20,16 @@
*/ */
package com.vitorpamplona.amethyst.ui.navigation.navs package com.vitorpamplona.amethyst.ui.navigation.navs
import androidx.compose.material3.DrawerState
import com.vitorpamplona.amethyst.ui.navigation.routes.Route import com.vitorpamplona.amethyst.ui.navigation.routes.Route
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlin.reflect.KClass import kotlin.reflect.KClass
class ObservableNav( class ObservableNav(
val sourceNav: INav, private val sourceNav: INav,
override val navigationScope: CoroutineScope, override val navigationScope: CoroutineScope,
val onBeforeNavigate: () -> Unit, val onBeforeNavigate: () -> Unit,
) : INav { ) : INav by sourceNav {
override val drawerState: DrawerState = sourceNav.drawerState
override fun closeDrawer() {
sourceNav.closeDrawer()
}
override fun openDrawer() {
sourceNav.openDrawer()
}
override fun nav(route: Route) { override fun nav(route: Route) {
navigationScope.launch { navigationScope.launch {
onBeforeNavigate() onBeforeNavigate()
@@ -71,11 +60,11 @@ class ObservableNav(
override fun <T : Route> popUpTo( override fun <T : Route> popUpTo(
route: Route, route: Route,
upToClass: KClass<T>, klass: KClass<T>,
) { ) {
navigationScope.launch { navigationScope.launch {
onBeforeNavigate() onBeforeNavigate()
} }
sourceNav.popUpTo(route, upToClass) sourceNav.popUpTo(route, klass)
} }
} }

View File

@@ -72,8 +72,10 @@ import com.vitorpamplona.amethyst.ui.theme.ripple24dp
import com.vitorpamplona.amethyst.ui.theme.warningColorOnSecondSurface import com.vitorpamplona.amethyst.ui.theme.warningColorOnSecondSurface
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
private const val DAMUS_RELAY_URL = "wss://relay.damus.io"
@Composable @Composable
public fun RelayBadgesHorizontal( fun RelayBadgesHorizontal(
baseNote: Note, baseNote: Note,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
nav: INav, nav: INav,
@@ -162,24 +164,24 @@ fun RenderRelayIconPreview() {
ThemeComparisonColumn { ThemeComparisonColumn {
Row { Row {
RenderRelayIcon( RenderRelayIcon(
displayUrl = "wss://relay.damus.io", displayUrl = DAMUS_RELAY_URL,
iconUrl = "wss://relay.damus.io", iconUrl = DAMUS_RELAY_URL,
loadProfilePicture = true, loadProfilePicture = true,
pingInMs = 100, pingInMs = 100,
loadRobohash = true, loadRobohash = true,
iconModifier = LargeRelayIconModifier, iconModifier = LargeRelayIconModifier,
) )
RenderRelayIcon( RenderRelayIcon(
displayUrl = "wss://relay.damus.io", displayUrl = DAMUS_RELAY_URL,
iconUrl = "wss://relay.damus.io", iconUrl = DAMUS_RELAY_URL,
loadProfilePicture = true, loadProfilePicture = true,
pingInMs = 300, pingInMs = 300,
loadRobohash = true, loadRobohash = true,
iconModifier = LargeRelayIconModifier, iconModifier = LargeRelayIconModifier,
) )
RenderRelayIcon( RenderRelayIcon(
displayUrl = "wss://relay.damus.io", displayUrl = DAMUS_RELAY_URL,
iconUrl = "wss://relay.damus.io", iconUrl = DAMUS_RELAY_URL,
loadProfilePicture = true, loadProfilePicture = true,
pingInMs = 500, pingInMs = 500,
loadRobohash = true, loadRobohash = true,

View File

@@ -28,9 +28,12 @@ import com.vitorpamplona.quartz.utils.TimeUtils
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
private const val YEAR_DATE_FORMAT = "MMM dd, yyyy"
private const val MONTH_DATE_FORMAT = "MMM dd"
var locale = Locale.getDefault() var locale = Locale.getDefault()
var yearFormatter = SimpleDateFormat("MMM dd, yyyy", locale) var yearFormatter = SimpleDateFormat(YEAR_DATE_FORMAT, locale)
var monthFormatter = SimpleDateFormat("MMM dd", locale) var monthFormatter = SimpleDateFormat(MONTH_DATE_FORMAT, locale)
fun timeAgo( fun timeAgo(
time: Long?, time: Long?,
@@ -46,8 +49,8 @@ fun timeAgo(
if (locale != Locale.getDefault()) { if (locale != Locale.getDefault()) {
locale = Locale.getDefault() locale = Locale.getDefault()
yearFormatter = SimpleDateFormat("MMM dd, yyyy", locale) yearFormatter = SimpleDateFormat(YEAR_DATE_FORMAT, locale)
monthFormatter = SimpleDateFormat("MMM dd", locale) monthFormatter = SimpleDateFormat(MONTH_DATE_FORMAT, locale)
} }
"" + yearFormatter.format(time * 1000) "" + yearFormatter.format(time * 1000)
@@ -55,8 +58,8 @@ fun timeAgo(
// Dec 12 // Dec 12
if (locale != Locale.getDefault()) { if (locale != Locale.getDefault()) {
locale = Locale.getDefault() locale = Locale.getDefault()
yearFormatter = SimpleDateFormat("MMM dd, yyyy", locale) yearFormatter = SimpleDateFormat(YEAR_DATE_FORMAT, locale)
monthFormatter = SimpleDateFormat("MMM dd", locale) monthFormatter = SimpleDateFormat(MONTH_DATE_FORMAT, locale)
} }
"" + monthFormatter.format(time * 1000) "" + monthFormatter.format(time * 1000)
@@ -86,8 +89,8 @@ fun timeAgoNoDot(
if (locale != Locale.getDefault()) { if (locale != Locale.getDefault()) {
locale = Locale.getDefault() locale = Locale.getDefault()
yearFormatter = SimpleDateFormat("MMM dd, yyyy", locale) yearFormatter = SimpleDateFormat(YEAR_DATE_FORMAT, locale)
monthFormatter = SimpleDateFormat("MMM dd", locale) monthFormatter = SimpleDateFormat(MONTH_DATE_FORMAT, locale)
} }
yearFormatter.format(time * 1000) yearFormatter.format(time * 1000)
@@ -95,8 +98,8 @@ fun timeAgoNoDot(
// Dec 12 // Dec 12
if (locale != Locale.getDefault()) { if (locale != Locale.getDefault()) {
locale = Locale.getDefault() locale = Locale.getDefault()
yearFormatter = SimpleDateFormat("MMM dd, yyyy", locale) yearFormatter = SimpleDateFormat(YEAR_DATE_FORMAT, locale)
monthFormatter = SimpleDateFormat("MMM dd", locale) monthFormatter = SimpleDateFormat(MONTH_DATE_FORMAT, locale)
} }
monthFormatter.format(time * 1000) monthFormatter.format(time * 1000)
@@ -127,8 +130,8 @@ fun dateFormatter(
if (locale != Locale.getDefault()) { if (locale != Locale.getDefault()) {
locale = Locale.getDefault() locale = Locale.getDefault()
yearFormatter = SimpleDateFormat("MMM dd, yyyy", locale) yearFormatter = SimpleDateFormat(YEAR_DATE_FORMAT, locale)
monthFormatter = SimpleDateFormat("MMM dd", locale) monthFormatter = SimpleDateFormat(MONTH_DATE_FORMAT, locale)
} }
yearFormatter.format(time * 1000) yearFormatter.format(time * 1000)
@@ -136,8 +139,8 @@ fun dateFormatter(
// Dec 12 // Dec 12
if (locale != Locale.getDefault()) { if (locale != Locale.getDefault()) {
locale = Locale.getDefault() locale = Locale.getDefault()
yearFormatter = SimpleDateFormat("MMM dd, yyyy", locale) yearFormatter = SimpleDateFormat(YEAR_DATE_FORMAT, locale)
monthFormatter = SimpleDateFormat("MMM dd", locale) monthFormatter = SimpleDateFormat(MONTH_DATE_FORMAT, locale)
} }
monthFormatter.format(time * 1000) monthFormatter.format(time * 1000)

View File

@@ -59,6 +59,8 @@ import com.vitorpamplona.amethyst.ui.theme.chatDraftBackground
import com.vitorpamplona.amethyst.ui.theme.mediumImportanceLink import com.vitorpamplona.amethyst.ui.theme.mediumImportanceLink
import com.vitorpamplona.amethyst.ui.theme.messageBubbleLimits import com.vitorpamplona.amethyst.ui.theme.messageBubbleLimits
private const val RELAYS_AND_ACTIONS_TEXT = "Relays and Actions"
@OptIn(ExperimentalFoundationApi::class) @OptIn(ExperimentalFoundationApi::class)
@Composable @Composable
fun ChatBubbleLayout( fun ChatBubbleLayout(
@@ -205,7 +207,7 @@ private fun BubblePreview() {
}, },
) )
}, },
detailRow = { Text("Relays and Actions") }, detailRow = { Text(RELAYS_AND_ACTIONS_TEXT) },
) { bgColor -> ) { bgColor ->
Text("This is my note") Text("This is my note")
} }
@@ -239,7 +241,7 @@ private fun BubblePreview() {
}, },
) )
}, },
detailRow = { Text("Relays and Actions") }, detailRow = { Text(RELAYS_AND_ACTIONS_TEXT) },
) { bgColor -> ) { bgColor ->
Text("This is a very long long loong note") Text("This is a very long long loong note")
} }
@@ -273,7 +275,7 @@ private fun BubblePreview() {
}, },
) )
}, },
detailRow = { Text("Relays and Actions") }, detailRow = { Text(RELAYS_AND_ACTIONS_TEXT) },
) { bgColor -> ) { bgColor ->
Text("This is a draft note") Text("This is a draft note")
} }
@@ -307,7 +309,7 @@ private fun BubblePreview() {
}, },
) )
}, },
detailRow = { Text("Relays and Actions") }, detailRow = { Text(RELAYS_AND_ACTIONS_TEXT) },
) { bgColor -> ) { bgColor ->
Text("Short note") Text("Short note")
} }

View File

@@ -43,7 +43,7 @@ fun MessagesScreen(
val act = LocalContext.current.getActivity() val act = LocalContext.current.getActivity()
val windowSizeClass = calculateWindowSizeClass(act) val windowSizeClass = calculateWindowSizeClass(act)
val twoPane by remember { val twoPane by remember(windowSizeClass.widthSizeClass) {
derivedStateOf { derivedStateOf {
when (windowSizeClass.widthSizeClass) { when (windowSizeClass.widthSizeClass) {
WindowWidthSizeClass.Compact -> false WindowWidthSizeClass.Compact -> false

View File

@@ -20,20 +20,16 @@
*/ */
package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.twopane package com.vitorpamplona.amethyst.ui.screen.loggedIn.chats.rooms.twopane
import androidx.compose.material3.DrawerState
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.navigation.routes.Route import com.vitorpamplona.amethyst.ui.navigation.routes.Route
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlin.reflect.KClass
class TwoPaneNav( class TwoPaneNav(
val nav: INav, private val nav: INav,
override val navigationScope: CoroutineScope, override val navigationScope: CoroutineScope,
) : INav { ) : INav by nav {
override val drawerState: DrawerState = nav.drawerState
val innerNav = mutableStateOf<Route?>(null) val innerNav = mutableStateOf<Route?>(null)
override fun nav(route: Route) { override fun nav(route: Route) {
@@ -56,27 +52,4 @@ class TwoPaneNav(
} }
} }
} }
override fun newStack(route: Route) {
nav.newStack(route)
}
override fun popBack() {
nav.popBack()
}
override fun <T : Route> popUpTo(
route: Route,
klass: KClass<T>,
) {
nav.popUpTo<T>(route, klass)
}
override fun closeDrawer() {
nav.closeDrawer()
}
override fun openDrawer() {
nav.openDrawer()
}
} }

View File

@@ -33,16 +33,16 @@ import kotlinx.collections.immutable.toImmutableMap
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
@Immutable @Immutable
abstract class Card { interface Card {
abstract fun createdAt(): Long fun createdAt(): Long
abstract fun id(): String fun id(): String
} }
@Immutable @Immutable
class BadgeCard( class BadgeCard(
val note: Note, val note: Note,
) : Card() { ) : Card {
override fun createdAt(): Long = note.createdAt() ?: 0L override fun createdAt(): Long = note.createdAt() ?: 0L
override fun id() = note.idHex override fun id() = note.idHex
@@ -51,7 +51,7 @@ class BadgeCard(
@Immutable @Immutable
class NoteCard( class NoteCard(
val note: Note, val note: Note,
) : Card() { ) : Card {
override fun createdAt(): Long = note.createdAt() ?: 0L override fun createdAt(): Long = note.createdAt() ?: 0L
override fun id() = note.idHex override fun id() = note.idHex
@@ -61,7 +61,7 @@ class NoteCard(
class ZapUserSetCard( class ZapUserSetCard(
val user: User, val user: User,
val zapEvents: ImmutableList<CombinedZap>, val zapEvents: ImmutableList<CombinedZap>,
) : Card() { ) : Card {
val createdAt = zapEvents.maxOfOrNull { it.createdAt() ?: 0L } ?: 0L val createdAt = zapEvents.maxOfOrNull { it.createdAt() ?: 0L } ?: 0L
override fun createdAt(): Long = createdAt override fun createdAt(): Long = createdAt
@@ -75,7 +75,7 @@ class MultiSetCard(
val boostEvents: ImmutableList<Note>, val boostEvents: ImmutableList<Note>,
val likeEvents: ImmutableList<Note>, val likeEvents: ImmutableList<Note>,
val zapEvents: ImmutableList<CombinedZap>, val zapEvents: ImmutableList<CombinedZap>,
) : Card() { ) : Card {
val maxCreatedAt = val maxCreatedAt =
maxOf( maxOf(
zapEvents.maxOfOrNull { it.createdAt() ?: 0L } ?: 0L, zapEvents.maxOfOrNull { it.createdAt() ?: 0L } ?: 0L,
@@ -108,7 +108,7 @@ class MultiSetCard(
@Immutable @Immutable
class MessageSetCard( class MessageSetCard(
val note: Note, val note: Note,
) : Card() { ) : Card {
override fun createdAt(): Long = note.createdAt() ?: 0L override fun createdAt(): Long = note.createdAt() ?: 0L
override fun id() = note.idHex override fun id() = note.idHex