leftover changes

This commit is contained in:
nrobi144
2025-12-28 07:35:48 +02:00
parent e407e03e90
commit 0dcd480d43
5 changed files with 119 additions and 112 deletions
@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2024 Vitor Pamplona * Copyright (c) 2025 Vitor Pamplona
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of * Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in * this software and associated documentation files (the "Software"), to deal in
@@ -42,8 +42,6 @@ import androidx.compose.material.icons.filled.Refresh
import androidx.compose.material.icons.filled.Search import androidx.compose.material.icons.filled.Search
import androidx.compose.material.icons.filled.Settings import androidx.compose.material.icons.filled.Settings
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
@@ -86,56 +84,58 @@ import com.vitorpamplona.amethyst.desktop.network.DesktopRelayConnectionManager
import com.vitorpamplona.amethyst.desktop.ui.FeedScreen import com.vitorpamplona.amethyst.desktop.ui.FeedScreen
import com.vitorpamplona.amethyst.desktop.ui.LoginScreen import com.vitorpamplona.amethyst.desktop.ui.LoginScreen
fun main() = application { fun main() =
val windowState = rememberWindowState( application {
width = 1200.dp, val windowState =
height = 800.dp, rememberWindowState(
position = WindowPosition.Aligned(Alignment.Center) width = 1200.dp,
) height = 800.dp,
position = WindowPosition.Aligned(Alignment.Center),
)
Window( Window(
onCloseRequest = ::exitApplication, onCloseRequest = ::exitApplication,
state = windowState, state = windowState,
title = "Amethyst" title = "Amethyst",
) { ) {
MenuBar { MenuBar {
Menu("File") { Menu("File") {
Item( Item(
"New Note", "New Note",
shortcut = KeyShortcut(Key.N, ctrl = true), shortcut = KeyShortcut(Key.N, ctrl = true),
onClick = { /* TODO: Open new note dialog */ } onClick = { /* TODO: Open new note dialog */ },
) )
Separator() Separator()
Item( Item(
"Settings", "Settings",
shortcut = KeyShortcut(Key.Comma, ctrl = true), shortcut = KeyShortcut(Key.Comma, ctrl = true),
onClick = { /* TODO: Open settings */ } onClick = { /* TODO: Open settings */ },
) )
Separator() Separator()
Item( Item(
"Quit", "Quit",
shortcut = KeyShortcut(Key.Q, ctrl = true), shortcut = KeyShortcut(Key.Q, ctrl = true),
onClick = ::exitApplication onClick = ::exitApplication,
) )
} }
Menu("Edit") { Menu("Edit") {
Item("Copy", shortcut = KeyShortcut(Key.C, ctrl = true), onClick = { }) Item("Copy", shortcut = KeyShortcut(Key.C, ctrl = true), onClick = { })
Item("Paste", shortcut = KeyShortcut(Key.V, ctrl = true), onClick = { }) Item("Paste", shortcut = KeyShortcut(Key.V, ctrl = true), onClick = { })
} }
Menu("View") { Menu("View") {
Item("Feed", onClick = { }) Item("Feed", onClick = { })
Item("Messages", onClick = { }) Item("Messages", onClick = { })
Item("Notifications", onClick = { }) Item("Notifications", onClick = { })
} }
Menu("Help") { Menu("Help") {
Item("About Amethyst", onClick = { }) Item("About Amethyst", onClick = { })
Item("Keyboard Shortcuts", onClick = { }) Item("Keyboard Shortcuts", onClick = { })
}
} }
App()
} }
App()
} }
}
@Composable @Composable
fun App() { fun App() {
@@ -153,17 +153,17 @@ fun App() {
} }
MaterialTheme( MaterialTheme(
colorScheme = darkColorScheme() colorScheme = darkColorScheme(),
) { ) {
Surface( Surface(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background color = MaterialTheme.colorScheme.background,
) { ) {
when (accountState) { when (accountState) {
is AccountState.LoggedOut -> { is AccountState.LoggedOut -> {
LoginScreen( LoginScreen(
accountManager = accountManager, accountManager = accountManager,
onLoginSuccess = { currentScreen = AppScreen.Feed } onLoginSuccess = { currentScreen = AppScreen.Feed },
) )
} }
is AccountState.LoggedIn -> { is AccountState.LoggedIn -> {
@@ -192,7 +192,7 @@ fun MainContent(
// Sidebar Navigation // Sidebar Navigation
NavigationRail( NavigationRail(
modifier = Modifier.width(80.dp).fillMaxHeight(), modifier = Modifier.width(80.dp).fillMaxHeight(),
containerColor = MaterialTheme.colorScheme.surfaceVariant containerColor = MaterialTheme.colorScheme.surfaceVariant,
) { ) {
Spacer(Modifier.height(16.dp)) Spacer(Modifier.height(16.dp))
@@ -200,35 +200,35 @@ fun MainContent(
icon = { Icon(Icons.Default.Home, contentDescription = "Feed") }, icon = { Icon(Icons.Default.Home, contentDescription = "Feed") },
label = { Text("Feed") }, label = { Text("Feed") },
selected = currentScreen == AppScreen.Feed, selected = currentScreen == AppScreen.Feed,
onClick = { onScreenChange(AppScreen.Feed) } onClick = { onScreenChange(AppScreen.Feed) },
) )
NavigationRailItem( NavigationRailItem(
icon = { Icon(Icons.Default.Search, contentDescription = "Search") }, icon = { Icon(Icons.Default.Search, contentDescription = "Search") },
label = { Text("Search") }, label = { Text("Search") },
selected = currentScreen == AppScreen.Search, selected = currentScreen == AppScreen.Search,
onClick = { onScreenChange(AppScreen.Search) } onClick = { onScreenChange(AppScreen.Search) },
) )
NavigationRailItem( NavigationRailItem(
icon = { Icon(Icons.Default.Email, contentDescription = "Messages") }, icon = { Icon(Icons.Default.Email, contentDescription = "Messages") },
label = { Text("DMs") }, label = { Text("DMs") },
selected = currentScreen == AppScreen.Messages, selected = currentScreen == AppScreen.Messages,
onClick = { onScreenChange(AppScreen.Messages) } onClick = { onScreenChange(AppScreen.Messages) },
) )
NavigationRailItem( NavigationRailItem(
icon = { Icon(Icons.Default.Notifications, contentDescription = "Notifications") }, icon = { Icon(Icons.Default.Notifications, contentDescription = "Notifications") },
label = { Text("Alerts") }, label = { Text("Alerts") },
selected = currentScreen == AppScreen.Notifications, selected = currentScreen == AppScreen.Notifications,
onClick = { onScreenChange(AppScreen.Notifications) } onClick = { onScreenChange(AppScreen.Notifications) },
) )
NavigationRailItem( NavigationRailItem(
icon = { Icon(Icons.Default.Person, contentDescription = "Profile") }, icon = { Icon(Icons.Default.Person, contentDescription = "Profile") },
label = { Text("Profile") }, label = { Text("Profile") },
selected = currentScreen == AppScreen.Profile, selected = currentScreen == AppScreen.Profile,
onClick = { onScreenChange(AppScreen.Profile) } onClick = { onScreenChange(AppScreen.Profile) },
) )
Spacer(Modifier.weight(1f)) Spacer(Modifier.weight(1f))
@@ -239,7 +239,7 @@ fun MainContent(
icon = { Icon(Icons.Default.Settings, contentDescription = "Settings") }, icon = { Icon(Icons.Default.Settings, contentDescription = "Settings") },
label = { Text("Settings") }, label = { Text("Settings") },
selected = currentScreen == AppScreen.Settings, selected = currentScreen == AppScreen.Settings,
onClick = { onScreenChange(AppScreen.Settings) } onClick = { onScreenChange(AppScreen.Settings) },
) )
Spacer(Modifier.height(16.dp)) Spacer(Modifier.height(16.dp))
@@ -249,7 +249,7 @@ fun MainContent(
// Main Content // Main Content
Box( Box(
modifier = Modifier.weight(1f).fillMaxHeight().padding(24.dp) modifier = Modifier.weight(1f).fillMaxHeight().padding(24.dp),
) { ) {
when (currentScreen) { when (currentScreen) {
AppScreen.Feed -> FeedScreen(relayManager) AppScreen.Feed -> FeedScreen(relayManager)
@@ -263,14 +263,16 @@ fun MainContent(
} }
} }
@Composable @Composable
fun ProfileScreen(account: AccountState.LoggedIn, accountManager: AccountManager) { fun ProfileScreen(
account: AccountState.LoggedIn,
accountManager: AccountManager,
) {
Column { Column {
Text( Text(
"Profile", "Profile",
style = MaterialTheme.typography.headlineMedium, style = MaterialTheme.typography.headlineMedium,
color = MaterialTheme.colorScheme.onBackground color = MaterialTheme.colorScheme.onBackground,
) )
Spacer(Modifier.height(16.dp)) Spacer(Modifier.height(16.dp))
@@ -284,9 +286,10 @@ fun ProfileScreen(account: AccountState.LoggedIn, accountManager: AccountManager
OutlinedButton( OutlinedButton(
onClick = { accountManager.logout() }, onClick = { accountManager.logout() },
colors = androidx.compose.material3.ButtonDefaults.outlinedButtonColors( colors =
contentColor = Color.Red androidx.compose.material3.ButtonDefaults.outlinedButtonColors(
) contentColor = Color.Red,
),
) { ) {
Text("Logout") Text("Logout")
} }
@@ -303,24 +306,24 @@ fun RelaySettingsScreen(relayManager: DesktopRelayConnectionManager) {
Text( Text(
"Relay Settings", "Relay Settings",
style = MaterialTheme.typography.headlineMedium, style = MaterialTheme.typography.headlineMedium,
color = MaterialTheme.colorScheme.onBackground color = MaterialTheme.colorScheme.onBackground,
) )
Spacer(Modifier.height(8.dp)) Spacer(Modifier.height(8.dp))
Row( Row(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp) horizontalArrangement = Arrangement.spacedBy(8.dp),
) { ) {
Text( Text(
"${connectedRelays.size} of ${relayStatuses.size} relays connected", "${connectedRelays.size} of ${relayStatuses.size} relays connected",
color = MaterialTheme.colorScheme.onSurfaceVariant, color = MaterialTheme.colorScheme.onSurfaceVariant,
style = MaterialTheme.typography.bodyMedium style = MaterialTheme.typography.bodyMedium,
) )
IconButton(onClick = { relayManager.connect() }) { IconButton(onClick = { relayManager.connect() }) {
Icon( Icon(
Icons.Default.Refresh, Icons.Default.Refresh,
contentDescription = "Reconnect", contentDescription = "Reconnect",
tint = MaterialTheme.colorScheme.primary tint = MaterialTheme.colorScheme.primary,
) )
} }
} }
@@ -330,7 +333,7 @@ fun RelaySettingsScreen(relayManager: DesktopRelayConnectionManager) {
Row( Row(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(8.dp), horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically,
) { ) {
OutlinedTextField( OutlinedTextField(
value = newRelayUrl, value = newRelayUrl,
@@ -338,7 +341,7 @@ fun RelaySettingsScreen(relayManager: DesktopRelayConnectionManager) {
label = { Text("Add relay") }, label = { Text("Add relay") },
placeholder = { Text("wss://relay.example.com") }, placeholder = { Text("wss://relay.example.com") },
modifier = Modifier.weight(1f), modifier = Modifier.weight(1f),
singleLine = true singleLine = true,
) )
Button( Button(
onClick = { onClick = {
@@ -347,7 +350,7 @@ fun RelaySettingsScreen(relayManager: DesktopRelayConnectionManager) {
newRelayUrl = "" newRelayUrl = ""
} }
}, },
enabled = newRelayUrl.isNotBlank() enabled = newRelayUrl.isNotBlank(),
) { ) {
Text("Add") Text("Add")
} }
@@ -357,12 +360,12 @@ fun RelaySettingsScreen(relayManager: DesktopRelayConnectionManager) {
LazyColumn( LazyColumn(
verticalArrangement = Arrangement.spacedBy(8.dp), verticalArrangement = Arrangement.spacedBy(8.dp),
modifier = Modifier.weight(1f) modifier = Modifier.weight(1f),
) { ) {
items(relayStatuses.values.toList()) { status -> items(relayStatuses.values.toList()) { status ->
RelayStatusCard( RelayStatusCard(
status = status, status = status,
onRemove = { relayManager.removeRelay(status.url) } onRemove = { relayManager.removeRelay(status.url) },
) )
} }
} }
@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2024 Vitor Pamplona * Copyright (c) 2025 Vitor Pamplona
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of * Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in * this software and associated documentation files (the "Software"), to deal in
@@ -26,7 +26,8 @@ import java.util.concurrent.TimeUnit
object DesktopHttpClient { object DesktopHttpClient {
private val client: OkHttpClient by lazy { private val client: OkHttpClient by lazy {
OkHttpClient.Builder() OkHttpClient
.Builder()
.connectTimeout(30, TimeUnit.SECONDS) .connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS) .readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS) .writeTimeout(30, TimeUnit.SECONDS)
@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2024 Vitor Pamplona * Copyright (c) 2025 Vitor Pamplona
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of * Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in * this software and associated documentation files (the "Software"), to deal in
@@ -27,6 +27,7 @@ import com.vitorpamplona.quartz.nip01Core.relay.sockets.okhttp.BasicOkHttpWebSoc
* Desktop-specific relay connection manager that configures OkHttp for websockets. * Desktop-specific relay connection manager that configures OkHttp for websockets.
* Delegates to the shared RelayConnectionManager from commons. * Delegates to the shared RelayConnectionManager from commons.
*/ */
class DesktopRelayConnectionManager : RelayConnectionManager( class DesktopRelayConnectionManager :
websocketBuilder = BasicOkHttpWebSocket.Builder(DesktopHttpClient::getHttpClient) RelayConnectionManager(
) websocketBuilder = BasicOkHttpWebSocket.Builder(DesktopHttpClient::getHttpClient),
)
@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2024 Vitor Pamplona * Copyright (c) 2025 Vitor Pamplona
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of * Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in * this software and associated documentation files (the "Software"), to deal in
@@ -60,41 +60,43 @@ fun FeedScreen(relayManager: DesktopRelayConnectionManager) {
DisposableEffect(configuredRelays) { DisposableEffect(configuredRelays) {
if (configuredRelays.isNotEmpty()) { if (configuredRelays.isNotEmpty()) {
val subId = "global-feed-${System.currentTimeMillis()}" val subId = "global-feed-${System.currentTimeMillis()}"
val filters = listOf( val filters =
Filter( listOf(
kinds = listOf(TextNoteEvent.KIND), Filter(
limit = 50, kinds = listOf(TextNoteEvent.KIND),
limit = 50,
),
) )
)
relayManager.subscribe( relayManager.subscribe(
subId = subId, subId = subId,
filters = filters, filters = filters,
relays = configuredRelays, relays = configuredRelays,
listener = object : IRequestListener { listener =
override fun onEvent( object : IRequestListener {
event: Event, override fun onEvent(
isLive: Boolean, event: Event,
relay: NormalizedRelayUrl, isLive: Boolean,
forFilters: List<Filter>? relay: NormalizedRelayUrl,
) { forFilters: List<Filter>?,
if (event.id !in seenIds) { ) {
seenIds.add(event.id) if (event.id !in seenIds) {
events.add(0, event) seenIds.add(event.id)
if (events.size > 200) { events.add(0, event)
val removed = events.removeAt(events.size - 1) if (events.size > 200) {
seenIds.remove(removed.id) val removed = events.removeAt(events.size - 1)
seenIds.remove(removed.id)
}
} }
} }
}
override fun onEose( override fun onEose(
relay: NormalizedRelayUrl, relay: NormalizedRelayUrl,
forFilters: List<Filter>? forFilters: List<Filter>?,
) { ) {
// End of stored events // End of stored events
} }
} },
) )
onDispose { onDispose {
@@ -109,7 +111,7 @@ fun FeedScreen(relayManager: DesktopRelayConnectionManager) {
FeedHeader( FeedHeader(
title = "Global Feed", title = "Global Feed",
connectedRelayCount = connectedRelays.size, connectedRelayCount = connectedRelays.size,
onRefresh = { relayManager.connect() } onRefresh = { relayManager.connect() },
) )
Spacer(Modifier.height(16.dp)) Spacer(Modifier.height(16.dp))
@@ -120,12 +122,12 @@ fun FeedScreen(relayManager: DesktopRelayConnectionManager) {
LoadingState("Loading notes...") LoadingState("Loading notes...")
} else { } else {
LazyColumn( LazyColumn(
verticalArrangement = Arrangement.spacedBy(8.dp) verticalArrangement = Arrangement.spacedBy(8.dp),
) { ) {
items(events, key = { it.id }) { event -> items(events, key = { it.id }) { event ->
// Use NoteCard from commons // Use NoteCard from commons
NoteCard( NoteCard(
note = event.toNoteDisplayData() note = event.toNoteDisplayData(),
) )
} }
} }
@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2024 Vitor Pamplona * Copyright (c) 2025 Vitor Pamplona
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of * Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in * this software and associated documentation files (the "Software"), to deal in