feat: allow editing synced lists, not just device-only ones
The edit pencil was gated to local lists even though rename/delete already worked end to end for synced collections. Read-only shares keep the icon but show it disabled, with a hint on tap explaining why. Refs #29
This commit is contained in:
@@ -52,6 +52,7 @@ import androidx.compose.material3.ExperimentalMaterial3Api
|
|||||||
import androidx.compose.material3.ExtendedFloatingActionButton
|
import androidx.compose.material3.ExtendedFloatingActionButton
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.LocalContentColor
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.MediumTopAppBar
|
import androidx.compose.material3.MediumTopAppBar
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
@@ -139,9 +140,12 @@ fun TaskListScreen(
|
|||||||
val list = content?.list
|
val list = content?.list
|
||||||
val listName = list?.name
|
val listName = list?.name
|
||||||
val listId = (filter as? TaskFilter.OfList)?.listId
|
val listId = (filter as? TaskFilter.OfList)?.listId
|
||||||
// Editing is offered for a device-only list. A collection that belongs to an
|
// Editing is offered for any writable list, device or synced — renaming and
|
||||||
// account is the server's to rename or remove, not ours.
|
// recolouring a synced collection is a server write the same sheet already
|
||||||
|
// handles. A read-only share keeps the icon but disables it: tapping it
|
||||||
|
// explains why rather than the button silently not being there.
|
||||||
var editingList by rememberSaveable { mutableStateOf(false) }
|
var editingList by rememberSaveable { mutableStateOf(false) }
|
||||||
|
var readOnlyHint by rememberSaveable { mutableStateOf(false) }
|
||||||
val listWriteFailure by viewModel.listWriteFailure.collectAsStateWithLifecycle()
|
val listWriteFailure by viewModel.listWriteFailure.collectAsStateWithLifecycle()
|
||||||
val listDeleted by viewModel.listDeleted.collectAsStateWithLifecycle()
|
val listDeleted by viewModel.listDeleted.collectAsStateWithLifecycle()
|
||||||
// The list this screen is about is gone; there is nothing left to show.
|
// The list this screen is about is gone; there is nothing left to show.
|
||||||
@@ -152,6 +156,12 @@ fun TaskListScreen(
|
|||||||
viewModel.clearListWriteFailure()
|
viewModel.clearListWriteFailure()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
LaunchedEffect(readOnlyHint) {
|
||||||
|
if (readOnlyHint) {
|
||||||
|
delay(4_000)
|
||||||
|
readOnlyHint = false
|
||||||
|
}
|
||||||
|
}
|
||||||
// One add affordance, never two: a real list with the setting on gets a pinned
|
// One add affordance, never two: a real list with the setting on gets a pinned
|
||||||
// bottom quick-add bar; everything else (incl. smart lists, which have no single
|
// bottom quick-add bar; everything else (incl. smart lists, which have no single
|
||||||
// target list) gets the floating "New task" button.
|
// target list) gets the floating "New task" button.
|
||||||
@@ -181,11 +191,20 @@ fun TaskListScreen(
|
|||||||
IconButton(onClick = { showSort = true }, enabled = content != null) {
|
IconButton(onClick = { showSort = true }, enabled = content != null) {
|
||||||
Icon(Icons.AutoMirrored.Rounded.Sort, contentDescription = stringResource(R.string.sort_title))
|
Icon(Icons.AutoMirrored.Rounded.Sort, contentDescription = stringResource(R.string.sort_title))
|
||||||
}
|
}
|
||||||
if (list != null && list.isLocal) {
|
if (list != null) {
|
||||||
IconButton(onClick = { editingList = true }) {
|
IconButton(
|
||||||
|
onClick = {
|
||||||
|
if (list.isReadOnly) readOnlyHint = true else editingList = true
|
||||||
|
},
|
||||||
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
Icons.Rounded.Edit,
|
Icons.Rounded.Edit,
|
||||||
contentDescription = stringResource(R.string.list_edit_title),
|
contentDescription = stringResource(R.string.list_edit_title),
|
||||||
|
tint = if (list.isReadOnly) {
|
||||||
|
LocalContentColor.current.copy(alpha = 0.38f)
|
||||||
|
} else {
|
||||||
|
LocalContentColor.current
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -230,23 +249,28 @@ fun TaskListScreen(
|
|||||||
.height(SnackChipHeight),
|
.height(SnackChipHeight),
|
||||||
contentAlignment = Alignment.CenterStart,
|
contentAlignment = Alignment.CenterStart,
|
||||||
) {
|
) {
|
||||||
// One chip, one anchor: the undo receipt takes precedence, and a
|
// One chip, one anchor, in priority order: the undo receipt first,
|
||||||
// refused list write reports itself once the undo window is clear.
|
// then a refused list write once the undo window is clear, then the
|
||||||
|
// read-only edit hint — the quietest of the three, so it never
|
||||||
|
// outranks something the other two are actually reporting.
|
||||||
val failure = listWriteFailure
|
val failure = listWriteFailure
|
||||||
if (undo != null || failure == null) {
|
when {
|
||||||
SnackChip(
|
undo != null -> SnackChip(
|
||||||
visible = undo != null,
|
visible = true,
|
||||||
message = stringResource(
|
message = stringResource(
|
||||||
if (undo is UndoReceipt.Completed) R.string.task_completed else R.string.task_deleted,
|
if (undo is UndoReceipt.Completed) R.string.task_completed else R.string.task_deleted,
|
||||||
),
|
),
|
||||||
actionLabel = stringResource(R.string.undo),
|
actionLabel = stringResource(R.string.undo),
|
||||||
onAction = viewModel::undo,
|
onAction = viewModel::undo,
|
||||||
)
|
)
|
||||||
} else {
|
failure != null -> SnackChip(
|
||||||
SnackChip(
|
|
||||||
visible = true,
|
visible = true,
|
||||||
message = stringResource(listWriteFailureMessage(failure)),
|
message = stringResource(listWriteFailureMessage(failure)),
|
||||||
)
|
)
|
||||||
|
else -> SnackChip(
|
||||||
|
visible = readOnlyHint,
|
||||||
|
message = stringResource(R.string.list_read_only),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user