- Panel.kt ConfirmDialog (moved from TagsScreen, where it was private): the delete-forever dialog, the sync disconnect and the tag dialogs all ask through it now. - strings.xml: editor_cancel and tags_cancel were both "Cancel"; they are one cancel string. - NoteAccess carries the core's permission string (wire). The access lookup, the share sheet's choices and the board's draft read it from there instead of writing "owner"/"edit"/"view" again. - hashtag(name): the #-prefixed tag name that TagsScreen, the card and the editor chips each wrote. Formatted and checked with ktlint in the CI image. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -718,7 +718,7 @@ private fun blankDraft(): Note =
|
||||
previews = emptyList(),
|
||||
createdAt = null,
|
||||
updatedAt = null,
|
||||
permission = "owner",
|
||||
permission = NoteAccess.OWNER.wire,
|
||||
shared = false,
|
||||
sharedBy = null,
|
||||
)
|
||||
|
||||
@@ -345,7 +345,7 @@ fun EditorLabelRow(
|
||||
// `#` on every chip, matching the card. This row still shows the
|
||||
// tags the BODY owns as well — it is the control surface, and the
|
||||
// "from tag" hint beside one is what says why it has no cross.
|
||||
text = "#${label.name}",
|
||||
text = hashtag(label.name),
|
||||
style = MaterialTheme.typography.labelLarge,
|
||||
color = tint.tagInk(dark),
|
||||
modifier =
|
||||
|
||||
@@ -286,7 +286,7 @@ private fun ExactReminderPicker(
|
||||
) { Text(stringResource(R.string.picker_next)) }
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = onDismiss) { Text(stringResource(R.string.editor_cancel)) }
|
||||
TextButton(onClick = onDismiss) { Text(stringResource(R.string.cancel)) }
|
||||
},
|
||||
) {
|
||||
DatePicker(state = state)
|
||||
@@ -312,7 +312,7 @@ private fun ExactReminderPicker(
|
||||
}) { Text(stringResource(R.string.picker_set)) }
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = onDismiss) { Text(stringResource(R.string.editor_cancel)) }
|
||||
TextButton(onClick = onDismiss) { Text(stringResource(R.string.cancel)) }
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -10,15 +10,17 @@ import com.fabledsword.inkwell.core.Note
|
||||
* refuses the rest anyway — this only keeps the editor from offering what would be
|
||||
* refused.
|
||||
*/
|
||||
enum class NoteAccess { OWNER, EDIT, VIEW }
|
||||
enum class NoteAccess(
|
||||
/** The core's `permission` string, which is also the server's and the web's. */
|
||||
val wire: String,
|
||||
) {
|
||||
OWNER("owner"),
|
||||
EDIT("edit"),
|
||||
VIEW("view"),
|
||||
}
|
||||
|
||||
val Note.access: NoteAccess
|
||||
get() =
|
||||
when (permission) {
|
||||
"edit" -> NoteAccess.EDIT
|
||||
"view" -> NoteAccess.VIEW
|
||||
else -> NoteAccess.OWNER
|
||||
}
|
||||
get() = NoteAccess.entries.firstOrNull { it.wire == permission } ?: NoteAccess.OWNER
|
||||
|
||||
/** Its content can't change here: it is in the trash, or shared with us to view. */
|
||||
val Note.readOnlyHere: Boolean
|
||||
|
||||
@@ -402,7 +402,7 @@ private fun LabelChips(labels: List<NoteLabel>) {
|
||||
// this row is a tag — a tag lifted off its own line, or one attached
|
||||
// through the picker — and the hash is how you would type either. It
|
||||
// also keeps a lifted chip reading as the `#todo` somebody wrote.
|
||||
text = "#${label.name}",
|
||||
text = hashtag(label.name),
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = tint.tagInk(dark),
|
||||
maxLines = 1,
|
||||
|
||||
@@ -126,18 +126,51 @@ fun MenuItem(
|
||||
fun ConfirmDeleteDialog(
|
||||
onConfirm: () -> Unit,
|
||||
onDismiss: () -> Unit,
|
||||
) {
|
||||
ConfirmDialog(
|
||||
title = stringResource(R.string.editor_delete_forever_title),
|
||||
body = stringResource(R.string.editor_delete_forever_body),
|
||||
confirm = stringResource(R.string.editor_delete_forever_confirm),
|
||||
onDismiss = onDismiss,
|
||||
onConfirm = onConfirm,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A confirmation: what is about to happen, what it costs, and one way out.
|
||||
*
|
||||
* Every dialog that asks before acting is this one, so they all word their buttons
|
||||
* and lay out their consequences the same way.
|
||||
*/
|
||||
@Composable
|
||||
fun ConfirmDialog(
|
||||
title: String,
|
||||
body: String,
|
||||
confirm: String,
|
||||
onDismiss: () -> Unit,
|
||||
onConfirm: () -> Unit,
|
||||
footnote: String? = null,
|
||||
) {
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismiss,
|
||||
title = { Text(stringResource(R.string.editor_delete_forever_title)) },
|
||||
text = { Text(stringResource(R.string.editor_delete_forever_body)) },
|
||||
confirmButton = {
|
||||
TextButton(onClick = onConfirm) {
|
||||
Text(stringResource(R.string.editor_delete_forever_confirm))
|
||||
title = { Text(title) },
|
||||
text = {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
Text(text = body, style = MaterialTheme.typography.bodyMedium)
|
||||
footnote?.let {
|
||||
Text(
|
||||
text = it,
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(onClick = onConfirm) { Text(confirm) }
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = onDismiss) { Text(stringResource(R.string.editor_cancel)) }
|
||||
TextButton(onClick = onDismiss) { Text(stringResource(R.string.cancel)) }
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -58,8 +58,8 @@ fun SharedByLine(note: Note) {
|
||||
)
|
||||
}
|
||||
|
||||
private const val VIEW = "view"
|
||||
private const val EDIT = "edit"
|
||||
private val VIEW = NoteAccess.VIEW.wire
|
||||
private val EDIT = NoteAccess.EDIT.wire
|
||||
|
||||
/**
|
||||
* The Share sheet: who the note is shared with, and adding someone or a group.
|
||||
|
||||
@@ -11,7 +11,6 @@ import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
@@ -280,20 +279,14 @@ private fun LinkedPanel(
|
||||
// Confirmed because it is not obvious what disconnecting does to the notes.
|
||||
// The copy answers that first — they stay — since the fear it raises is
|
||||
// "will this delete something?", not "am I sure?".
|
||||
AlertDialog(
|
||||
onDismissRequest = { confirmingUnlink = false },
|
||||
title = { Text(stringResource(R.string.sync_disconnect_title)) },
|
||||
text = { Text(stringResource(R.string.sync_disconnect_body)) },
|
||||
confirmButton = {
|
||||
TextButton(onClick = {
|
||||
ConfirmDialog(
|
||||
title = stringResource(R.string.sync_disconnect_title),
|
||||
body = stringResource(R.string.sync_disconnect_body),
|
||||
confirm = stringResource(R.string.sync_disconnect),
|
||||
onDismiss = { confirmingUnlink = false },
|
||||
onConfirm = {
|
||||
confirmingUnlink = false
|
||||
onUnlink()
|
||||
}) { Text(stringResource(R.string.sync_disconnect)) }
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = { confirmingUnlink = false }) {
|
||||
Text(stringResource(R.string.editor_cancel))
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -181,8 +181,8 @@ fun TagsScreen(
|
||||
|
||||
is TagDialog.ConfirmMerge ->
|
||||
ConfirmDialog(
|
||||
title = stringResource(R.string.tags_rename_merges_title, hash(open.into.name)),
|
||||
body = stringResource(R.string.tags_rename_merges_body, hash(open.into.name)),
|
||||
title = stringResource(R.string.tags_rename_merges_title, hashtag(open.into.name)),
|
||||
body = stringResource(R.string.tags_rename_merges_body, hashtag(open.into.name)),
|
||||
confirm = stringResource(R.string.tags_rename_merges_confirm),
|
||||
onDismiss = { dialog = null },
|
||||
onConfirm = {
|
||||
@@ -204,7 +204,7 @@ fun TagsScreen(
|
||||
|
||||
is TagDialog.Delete ->
|
||||
ConfirmDialog(
|
||||
title = stringResource(R.string.tags_delete_title, hash(open.tag.name)),
|
||||
title = stringResource(R.string.tags_delete_title, hashtag(open.tag.name)),
|
||||
// The count is the part that makes the consequence real — "it is on
|
||||
// 40 notes" is a different decision from "delete this tag?". It comes
|
||||
// from the LIST, the only call the core populates a count on.
|
||||
@@ -235,13 +235,13 @@ fun TagsScreen(
|
||||
}
|
||||
|
||||
/**
|
||||
* `#` on the name, everywhere it is spoken about.
|
||||
* `#` on the name, everywhere it is spoken about: the chips on a card and in the
|
||||
* editor (`NoteCard.kt`, `EditorChrome.kt`) and every dialog here.
|
||||
*
|
||||
* The chips already wear it (`NoteCard.kt`, `EditorChrome.kt`) and it is the
|
||||
* reason these are called tags at all — a dialog that said "Delete grocery?" would
|
||||
* be talking about something else.
|
||||
* It is the reason these are called tags at all — a dialog that said "Delete
|
||||
* grocery?" would be talking about something else.
|
||||
*/
|
||||
private fun hash(name: String): String = "#$name"
|
||||
internal fun hashtag(name: String): String = "#$name"
|
||||
|
||||
@Composable
|
||||
private fun NewTagField(
|
||||
@@ -333,7 +333,7 @@ private fun TagRow(
|
||||
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text(
|
||||
text = hash(tag.name),
|
||||
text = hashtag(tag.name),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = tint.tagInk(dark),
|
||||
)
|
||||
@@ -410,7 +410,7 @@ private fun RenameDialog(
|
||||
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismiss,
|
||||
title = { Text(stringResource(R.string.tags_rename_title, hash(tag.name))) },
|
||||
title = { Text(stringResource(R.string.tags_rename_title, hashtag(tag.name))) },
|
||||
text = {
|
||||
PlainTextField(
|
||||
value = text,
|
||||
@@ -427,7 +427,7 @@ private fun RenameDialog(
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = onDismiss) { Text(stringResource(R.string.tags_cancel)) }
|
||||
TextButton(onClick = onDismiss) { Text(stringResource(R.string.cancel)) }
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -452,11 +452,11 @@ private fun MergeDialog(
|
||||
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismiss,
|
||||
title = { Text(stringResource(R.string.tags_merge_title, hash(tag.name))) },
|
||||
title = { Text(stringResource(R.string.tags_merge_title, hashtag(tag.name))) },
|
||||
text = {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
Text(
|
||||
text = stringResource(R.string.tags_merge_body, hash(tag.name)),
|
||||
text = stringResource(R.string.tags_merge_body, hashtag(tag.name)),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
)
|
||||
if (others.isEmpty()) {
|
||||
@@ -469,7 +469,7 @@ private fun MergeDialog(
|
||||
others.forEach { other ->
|
||||
val tint = labelTintFor(other.name, other.color)
|
||||
Text(
|
||||
text = hash(other.name),
|
||||
text = hashtag(other.name),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = tint.tagInk(dark),
|
||||
modifier =
|
||||
@@ -483,7 +483,7 @@ private fun MergeDialog(
|
||||
},
|
||||
confirmButton = {},
|
||||
dismissButton = {
|
||||
TextButton(onClick = onDismiss) { Text(stringResource(R.string.tags_cancel)) }
|
||||
TextButton(onClick = onDismiss) { Text(stringResource(R.string.cancel)) }
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -507,7 +507,7 @@ private fun ColourDialog(
|
||||
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismiss,
|
||||
title = { Text(stringResource(R.string.tags_colour_of, hash(tag.name))) },
|
||||
title = { Text(stringResource(R.string.tags_colour_of, hashtag(tag.name))) },
|
||||
text = {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
|
||||
NOTE_TINTS.forEach { (key, tint) ->
|
||||
@@ -544,41 +544,7 @@ private fun ColourDialog(
|
||||
},
|
||||
confirmButton = {},
|
||||
dismissButton = {
|
||||
TextButton(onClick = onDismiss) { Text(stringResource(R.string.tags_cancel)) }
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/** A destructive confirmation: what it is, what it costs, and one way out. */
|
||||
@Composable
|
||||
private fun ConfirmDialog(
|
||||
title: String,
|
||||
body: String,
|
||||
confirm: String,
|
||||
onDismiss: () -> Unit,
|
||||
onConfirm: () -> Unit,
|
||||
footnote: String? = null,
|
||||
) {
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismiss,
|
||||
title = { Text(title) },
|
||||
text = {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
Text(text = body, style = MaterialTheme.typography.bodyMedium)
|
||||
footnote?.let {
|
||||
Text(
|
||||
text = it,
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(onClick = onConfirm) { Text(confirm) }
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = onDismiss) { Text(stringResource(R.string.tags_cancel)) }
|
||||
TextButton(onClick = onDismiss) { Text(stringResource(R.string.cancel)) }
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
<string name="editor_unarchive">Unarchive</string>
|
||||
<string name="editor_trash">Move to trash</string>
|
||||
<string name="editor_restore">Restore</string>
|
||||
<string name="editor_cancel">Cancel</string>
|
||||
<string name="cancel">Cancel</string>
|
||||
<string name="editor_share">Share…</string>
|
||||
|
||||
<!-- Sharing (#5175). Shares live on the server, so the sheet says so when this
|
||||
@@ -159,7 +159,6 @@
|
||||
said here rather than left as a surprise. -->
|
||||
<string name="tags_delete_from_text">Tags written as #tag in a note come back when that note is next edited.</string>
|
||||
|
||||
<string name="tags_cancel">Cancel</string>
|
||||
|
||||
<!-- Pickers -->
|
||||
<string name="label_picker_title">Tags</string>
|
||||
|
||||
Reference in New Issue
Block a user