mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-09 16:14:40 +00:00
feat(media-servers): auto-save on every change; polish cache card & add flow
Addresses review feedback on the redesign: - Auto-save: drop the Save button and replace the cancel "X" with a standard back arrow (TopBarWithBackButton). Every add/remove persists immediately; a reorder persists once the drag gesture ends, so dragging no longer publishes a kind-10063 event on every intermediate swap. - On-device cache card now uses the app's filled surfaceContainer card style (no outline), matching the main Settings screens. - Drop the redundant intro sentence at the top of the screen. - Rework the "Add a server" block: recommended servers come first as one-tap chips, followed by the "or paste a server address" field. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GJ77Hm5L7fXEbPWbUds1iA
This commit is contained in:
+18
-12
@@ -45,6 +45,7 @@ import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
@@ -101,24 +102,21 @@ fun AllMediaBody(
|
||||
itemCount = { blossomServersState.size },
|
||||
)
|
||||
|
||||
// Auto-save the reordering once the drag finishes, rather than on every intermediate swap.
|
||||
LaunchedEffect(dragState.isDragging) {
|
||||
if (!dragState.isDragging) blossomServersViewModel.persistPending()
|
||||
}
|
||||
|
||||
LazyColumn(
|
||||
modifier = modifier,
|
||||
contentPadding = FeedPadding,
|
||||
userScrollEnabled = !dragState.isDragging,
|
||||
) {
|
||||
item {
|
||||
Text(
|
||||
text = stringRes(id = R.string.set_preferred_media_servers),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.grayText,
|
||||
modifier = Modifier.padding(top = 12.dp, bottom = 4.dp),
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
SectionLabel(
|
||||
title = stringRes(id = R.string.media_servers_priority_section),
|
||||
caption = stringRes(id = R.string.media_servers_reorder_hint),
|
||||
modifier = Modifier.padding(top = 8.dp),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -289,10 +287,9 @@ private fun AddServerSection(
|
||||
) {
|
||||
SectionLabel(title = stringRes(id = R.string.media_servers_add_section))
|
||||
|
||||
MediaServerEditField(R.string.add_a_blossom_server) { onAddServer(it) }
|
||||
|
||||
// Recommended servers first — one tap adds a known-good host.
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth().padding(top = 14.dp),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
@@ -323,6 +320,15 @@ private fun AddServerSection(
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// ...or paste any server address.
|
||||
Text(
|
||||
text = stringRes(id = R.string.media_servers_add_url_label),
|
||||
style = MaterialTheme.typography.labelLarge,
|
||||
color = MaterialTheme.colorScheme.grayText,
|
||||
modifier = Modifier.padding(top = 16.dp, bottom = 8.dp),
|
||||
)
|
||||
MediaServerEditField(R.string.add_a_blossom_server) { onAddServer(it) }
|
||||
}
|
||||
|
||||
/** A recommended server as a tappable pill. Once added it reads as done and stops responding. */
|
||||
|
||||
+8
-18
@@ -21,7 +21,6 @@
|
||||
package com.vitorpamplona.amethyst.ui.actions.mediaServers
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
@@ -53,7 +52,7 @@ import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.commons.icons.symbols.Icon
|
||||
import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols
|
||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||
import com.vitorpamplona.amethyst.ui.navigation.topbars.SavingTopBar
|
||||
import com.vitorpamplona.amethyst.ui.navigation.topbars.TopBarWithBackButton
|
||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.allGoodColor
|
||||
@@ -72,9 +71,7 @@ fun AllMediaServersScreen(
|
||||
blossomServersViewModel.load()
|
||||
}
|
||||
|
||||
MediaServersScaffold(blossomServersViewModel, accountViewModel) {
|
||||
nav.popBack()
|
||||
}
|
||||
MediaServersScaffold(blossomServersViewModel, accountViewModel, nav)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@@ -82,20 +79,13 @@ fun AllMediaServersScreen(
|
||||
fun MediaServersScaffold(
|
||||
blossomServersViewModel: BlossomServersViewModel,
|
||||
accountViewModel: AccountViewModel,
|
||||
onClose: () -> Unit,
|
||||
nav: INav,
|
||||
) {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
SavingTopBar(
|
||||
titleRes = R.string.media_servers,
|
||||
onCancel = {
|
||||
blossomServersViewModel.refresh()
|
||||
onClose()
|
||||
},
|
||||
onPost = {
|
||||
blossomServersViewModel.saveFileServers()
|
||||
onClose()
|
||||
},
|
||||
TopBarWithBackButton(
|
||||
caption = stringRes(id = R.string.media_servers),
|
||||
nav = nav,
|
||||
)
|
||||
},
|
||||
) { padding ->
|
||||
@@ -132,8 +122,8 @@ fun MediaCacheSection(accountViewModel: AccountViewModel) {
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(16.dp))
|
||||
.border(1.dp, MaterialTheme.colorScheme.outlineVariant, RoundedCornerShape(16.dp)),
|
||||
.clip(RoundedCornerShape(20.dp))
|
||||
.background(MaterialTheme.colorScheme.surfaceContainer),
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth().padding(14.dp),
|
||||
|
||||
+25
-22
@@ -123,12 +123,17 @@ class BlossomServersViewModel : ViewModel() {
|
||||
}
|
||||
|
||||
fun addServerList(serverList: List<String>) {
|
||||
serverList.forEach { serverUrl ->
|
||||
addServer(serverUrl)
|
||||
}
|
||||
var added = false
|
||||
serverList.forEach { if (addServerInternal(it)) added = true }
|
||||
if (added) persist()
|
||||
}
|
||||
|
||||
fun addServer(serverUrl: String) {
|
||||
if (addServerInternal(serverUrl)) persist()
|
||||
}
|
||||
|
||||
/** Adds a server to the in-memory list (no persist). Returns true if it was new. */
|
||||
private fun addServerInternal(serverUrl: String): Boolean {
|
||||
val normalizedUrl =
|
||||
try {
|
||||
Rfc3986.normalize(serverUrl.trim())
|
||||
@@ -147,15 +152,12 @@ class BlossomServersViewModel : ViewModel() {
|
||||
normalizedUrl,
|
||||
ServerType.Blossom,
|
||||
)
|
||||
if (_fileServers.value.contains(serverRef)) {
|
||||
return
|
||||
} else {
|
||||
_fileServers.update {
|
||||
it.plus(serverRef)
|
||||
}
|
||||
probeServer(serverRef.baseUrl)
|
||||
}
|
||||
if (_fileServers.value.contains(serverRef)) return false
|
||||
|
||||
_fileServers.update { it.plus(serverRef) }
|
||||
probeServer(serverRef.baseUrl)
|
||||
isModified = true
|
||||
return true
|
||||
}
|
||||
|
||||
fun removeServer(
|
||||
@@ -176,21 +178,22 @@ class BlossomServersViewModel : ViewModel() {
|
||||
}
|
||||
pruneHealth()
|
||||
isModified = true
|
||||
persist()
|
||||
}
|
||||
}
|
||||
|
||||
fun removeAllServers() {
|
||||
_fileServers.update { emptyList() }
|
||||
isModified = true
|
||||
}
|
||||
/**
|
||||
* Publishes any pending change. Called after each discrete edit (add/remove) and,
|
||||
* for a reorder, once the drag gesture completes — so a drag doesn't publish a
|
||||
* kind-10063 event on every intermediate swap.
|
||||
*/
|
||||
fun persistPending() = persist()
|
||||
|
||||
fun saveFileServers() {
|
||||
if (isModified) {
|
||||
accountViewModel.launchSigner {
|
||||
val serverList = _fileServers.value.map { it.baseUrl }
|
||||
account.sendBlossomServersList(serverList)
|
||||
refresh()
|
||||
}
|
||||
private fun persist() {
|
||||
if (!isModified) return
|
||||
isModified = false
|
||||
accountViewModel.launchSigner {
|
||||
account.sendBlossomServersList(_fileServers.value.map { it.baseUrl })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1557,6 +1557,7 @@
|
||||
<string name="media_servers_priority_section">Upload priority</string>
|
||||
<string name="media_servers_add_section">Add a server</string>
|
||||
<string name="media_servers_recommended_label">Recommended</string>
|
||||
<string name="media_servers_add_url_label">Or paste a server address</string>
|
||||
<string name="media_servers_cache_section">On-device cache</string>
|
||||
<string name="media_server_primary_badge">Primary</string>
|
||||
<string name="media_server_added">Added</string>
|
||||
|
||||
Reference in New Issue
Block a user