- 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(),
|
previews = emptyList(),
|
||||||
createdAt = null,
|
createdAt = null,
|
||||||
updatedAt = null,
|
updatedAt = null,
|
||||||
permission = "owner",
|
permission = NoteAccess.OWNER.wire,
|
||||||
shared = false,
|
shared = false,
|
||||||
sharedBy = null,
|
sharedBy = null,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -345,7 +345,7 @@ fun EditorLabelRow(
|
|||||||
// `#` on every chip, matching the card. This row still shows the
|
// `#` on every chip, matching the card. This row still shows the
|
||||||
// tags the BODY owns as well — it is the control surface, and 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.
|
// "from tag" hint beside one is what says why it has no cross.
|
||||||
text = "#${label.name}",
|
text = hashtag(label.name),
|
||||||
style = MaterialTheme.typography.labelLarge,
|
style = MaterialTheme.typography.labelLarge,
|
||||||
color = tint.tagInk(dark),
|
color = tint.tagInk(dark),
|
||||||
modifier =
|
modifier =
|
||||||
|
|||||||
@@ -286,7 +286,7 @@ private fun ExactReminderPicker(
|
|||||||
) { Text(stringResource(R.string.picker_next)) }
|
) { Text(stringResource(R.string.picker_next)) }
|
||||||
},
|
},
|
||||||
dismissButton = {
|
dismissButton = {
|
||||||
TextButton(onClick = onDismiss) { Text(stringResource(R.string.editor_cancel)) }
|
TextButton(onClick = onDismiss) { Text(stringResource(R.string.cancel)) }
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
DatePicker(state = state)
|
DatePicker(state = state)
|
||||||
@@ -312,7 +312,7 @@ private fun ExactReminderPicker(
|
|||||||
}) { Text(stringResource(R.string.picker_set)) }
|
}) { Text(stringResource(R.string.picker_set)) }
|
||||||
},
|
},
|
||||||
dismissButton = {
|
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
|
* refuses the rest anyway — this only keeps the editor from offering what would be
|
||||||
* refused.
|
* 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
|
val Note.access: NoteAccess
|
||||||
get() =
|
get() = NoteAccess.entries.firstOrNull { it.wire == permission } ?: NoteAccess.OWNER
|
||||||
when (permission) {
|
|
||||||
"edit" -> NoteAccess.EDIT
|
|
||||||
"view" -> NoteAccess.VIEW
|
|
||||||
else -> NoteAccess.OWNER
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Its content can't change here: it is in the trash, or shared with us to view. */
|
/** Its content can't change here: it is in the trash, or shared with us to view. */
|
||||||
val Note.readOnlyHere: Boolean
|
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
|
// 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
|
// through the picker — and the hash is how you would type either. It
|
||||||
// also keeps a lifted chip reading as the `#todo` somebody wrote.
|
// also keeps a lifted chip reading as the `#todo` somebody wrote.
|
||||||
text = "#${label.name}",
|
text = hashtag(label.name),
|
||||||
style = MaterialTheme.typography.labelSmall,
|
style = MaterialTheme.typography.labelSmall,
|
||||||
color = tint.tagInk(dark),
|
color = tint.tagInk(dark),
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
|
|||||||
@@ -126,18 +126,51 @@ fun MenuItem(
|
|||||||
fun ConfirmDeleteDialog(
|
fun ConfirmDeleteDialog(
|
||||||
onConfirm: () -> Unit,
|
onConfirm: () -> Unit,
|
||||||
onDismiss: () -> 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(
|
AlertDialog(
|
||||||
onDismissRequest = onDismiss,
|
onDismissRequest = onDismiss,
|
||||||
title = { Text(stringResource(R.string.editor_delete_forever_title)) },
|
title = { Text(title) },
|
||||||
text = { Text(stringResource(R.string.editor_delete_forever_body)) },
|
text = {
|
||||||
confirmButton = {
|
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||||
TextButton(onClick = onConfirm) {
|
Text(text = body, style = MaterialTheme.typography.bodyMedium)
|
||||||
Text(stringResource(R.string.editor_delete_forever_confirm))
|
footnote?.let {
|
||||||
|
Text(
|
||||||
|
text = it,
|
||||||
|
style = MaterialTheme.typography.labelSmall,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
confirmButton = {
|
||||||
|
TextButton(onClick = onConfirm) { Text(confirm) }
|
||||||
|
},
|
||||||
dismissButton = {
|
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 val VIEW = NoteAccess.VIEW.wire
|
||||||
private const val EDIT = "edit"
|
private val EDIT = NoteAccess.EDIT.wire
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Share sheet: who the note is shared with, and adding someone or a group.
|
* 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.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||||
import androidx.compose.material3.AlertDialog
|
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
@@ -280,20 +279,14 @@ private fun LinkedPanel(
|
|||||||
// Confirmed because it is not obvious what disconnecting does to the notes.
|
// 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
|
// The copy answers that first — they stay — since the fear it raises is
|
||||||
// "will this delete something?", not "am I sure?".
|
// "will this delete something?", not "am I sure?".
|
||||||
AlertDialog(
|
ConfirmDialog(
|
||||||
onDismissRequest = { confirmingUnlink = false },
|
title = stringResource(R.string.sync_disconnect_title),
|
||||||
title = { Text(stringResource(R.string.sync_disconnect_title)) },
|
body = stringResource(R.string.sync_disconnect_body),
|
||||||
text = { Text(stringResource(R.string.sync_disconnect_body)) },
|
confirm = stringResource(R.string.sync_disconnect),
|
||||||
confirmButton = {
|
onDismiss = { confirmingUnlink = false },
|
||||||
TextButton(onClick = {
|
onConfirm = {
|
||||||
confirmingUnlink = false
|
confirmingUnlink = false
|
||||||
onUnlink()
|
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 ->
|
is TagDialog.ConfirmMerge ->
|
||||||
ConfirmDialog(
|
ConfirmDialog(
|
||||||
title = stringResource(R.string.tags_rename_merges_title, hash(open.into.name)),
|
title = stringResource(R.string.tags_rename_merges_title, hashtag(open.into.name)),
|
||||||
body = stringResource(R.string.tags_rename_merges_body, hash(open.into.name)),
|
body = stringResource(R.string.tags_rename_merges_body, hashtag(open.into.name)),
|
||||||
confirm = stringResource(R.string.tags_rename_merges_confirm),
|
confirm = stringResource(R.string.tags_rename_merges_confirm),
|
||||||
onDismiss = { dialog = null },
|
onDismiss = { dialog = null },
|
||||||
onConfirm = {
|
onConfirm = {
|
||||||
@@ -204,7 +204,7 @@ fun TagsScreen(
|
|||||||
|
|
||||||
is TagDialog.Delete ->
|
is TagDialog.Delete ->
|
||||||
ConfirmDialog(
|
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
|
// 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
|
// 40 notes" is a different decision from "delete this tag?". It comes
|
||||||
// from the LIST, the only call the core populates a count on.
|
// 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
|
* It is the reason these are called tags at all — a dialog that said "Delete
|
||||||
* reason these are called tags at all — a dialog that said "Delete grocery?" would
|
* grocery?" would be talking about something else.
|
||||||
* be talking about something else.
|
|
||||||
*/
|
*/
|
||||||
private fun hash(name: String): String = "#$name"
|
internal fun hashtag(name: String): String = "#$name"
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun NewTagField(
|
private fun NewTagField(
|
||||||
@@ -333,7 +333,7 @@ private fun TagRow(
|
|||||||
|
|
||||||
Column(modifier = Modifier.weight(1f)) {
|
Column(modifier = Modifier.weight(1f)) {
|
||||||
Text(
|
Text(
|
||||||
text = hash(tag.name),
|
text = hashtag(tag.name),
|
||||||
style = MaterialTheme.typography.bodyLarge,
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
color = tint.tagInk(dark),
|
color = tint.tagInk(dark),
|
||||||
)
|
)
|
||||||
@@ -410,7 +410,7 @@ private fun RenameDialog(
|
|||||||
|
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
onDismissRequest = onDismiss,
|
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 = {
|
text = {
|
||||||
PlainTextField(
|
PlainTextField(
|
||||||
value = text,
|
value = text,
|
||||||
@@ -427,7 +427,7 @@ private fun RenameDialog(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
dismissButton = {
|
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(
|
AlertDialog(
|
||||||
onDismissRequest = onDismiss,
|
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 = {
|
text = {
|
||||||
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||||
Text(
|
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,
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
)
|
)
|
||||||
if (others.isEmpty()) {
|
if (others.isEmpty()) {
|
||||||
@@ -469,7 +469,7 @@ private fun MergeDialog(
|
|||||||
others.forEach { other ->
|
others.forEach { other ->
|
||||||
val tint = labelTintFor(other.name, other.color)
|
val tint = labelTintFor(other.name, other.color)
|
||||||
Text(
|
Text(
|
||||||
text = hash(other.name),
|
text = hashtag(other.name),
|
||||||
style = MaterialTheme.typography.bodyLarge,
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
color = tint.tagInk(dark),
|
color = tint.tagInk(dark),
|
||||||
modifier =
|
modifier =
|
||||||
@@ -483,7 +483,7 @@ private fun MergeDialog(
|
|||||||
},
|
},
|
||||||
confirmButton = {},
|
confirmButton = {},
|
||||||
dismissButton = {
|
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(
|
AlertDialog(
|
||||||
onDismissRequest = onDismiss,
|
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 = {
|
text = {
|
||||||
Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
|
Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
|
||||||
NOTE_TINTS.forEach { (key, tint) ->
|
NOTE_TINTS.forEach { (key, tint) ->
|
||||||
@@ -544,41 +544,7 @@ private fun ColourDialog(
|
|||||||
},
|
},
|
||||||
confirmButton = {},
|
confirmButton = {},
|
||||||
dismissButton = {
|
dismissButton = {
|
||||||
TextButton(onClick = onDismiss) { Text(stringResource(R.string.tags_cancel)) }
|
TextButton(onClick = onDismiss) { Text(stringResource(R.string.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)) }
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,7 +65,7 @@
|
|||||||
<string name="editor_unarchive">Unarchive</string>
|
<string name="editor_unarchive">Unarchive</string>
|
||||||
<string name="editor_trash">Move to trash</string>
|
<string name="editor_trash">Move to trash</string>
|
||||||
<string name="editor_restore">Restore</string>
|
<string name="editor_restore">Restore</string>
|
||||||
<string name="editor_cancel">Cancel</string>
|
<string name="cancel">Cancel</string>
|
||||||
<string name="editor_share">Share…</string>
|
<string name="editor_share">Share…</string>
|
||||||
|
|
||||||
<!-- Sharing (#5175). Shares live on the server, so the sheet says so when this
|
<!-- 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. -->
|
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_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 -->
|
<!-- Pickers -->
|
||||||
<string name="label_picker_title">Tags</string>
|
<string name="label_picker_title">Tags</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user