Skip to content

Commit

Permalink
Bug fixes:
Browse files Browse the repository at this point in the history
* Don't accidentally delete checklist items & images when upserting note
* Checklist items don't steal focus from note title
* Correct ordering of checklist items
  • Loading branch information
Eboreg committed Nov 3, 2023
1 parent 7362ef8 commit fbbe5da
Show file tree
Hide file tree
Showing 10 changed files with 92 additions and 90 deletions.
10 changes: 5 additions & 5 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ android {
applicationId = "us.huseli.retain"
minSdk = 26
targetSdk = targetSdk
versionCode = 4
versionName = "1.0.0-beta.4"
versionCode = 5
versionName = "1.0.0-beta.5"
vectorDrawables.useSupportLibrary = true
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
// buildConfigField("String", "dropboxAppKey", "\"${dropboxAppKey}\"")
Expand Down Expand Up @@ -157,13 +157,13 @@ dependencies {
implementation("com.google.accompanist:accompanist-systemuicontroller:0.27.0")

// HTML parsing:
implementation("org.jsoup:jsoup:1.16.1")
implementation("org.jsoup:jsoup:1.16.2")

// SFTP:
implementation(group = "com.github.mwiede", name = "jsch", version = "0.2.9")
implementation(group = "com.github.mwiede", name = "jsch", version = "0.2.12")

// Dropbox:
implementation("com.dropbox.core:dropbox-core-sdk:5.4.5")
implementation("com.dropbox.core:dropbox-core-sdk:5.4.6")

// Theme:
implementation("com.github.Eboreg:RetainTheme:2.2.1")
Expand Down
6 changes: 3 additions & 3 deletions app/release/output-metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@
"type": "SINGLE",
"filters": [],
"attributes": [],
"versionCode": 4,
"versionName": "1.0.0-beta.4",
"outputFile": "retain_1.0.0-beta.4-release.apk"
"versionCode": 5,
"versionName": "1.0.0-beta.5",
"outputFile": "retain_1.0.0-beta.5-release.apk"
}
],
"elementType": "File"
Expand Down
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.sharp.Add
import androidx.compose.material.icons.sharp.ExpandMore
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
Expand All @@ -23,6 +24,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.unit.dp
import org.burnoutcrew.reorderable.ReorderableItem
Expand All @@ -45,6 +47,7 @@ fun LazyListScope.ChecklistNoteChecklist(
onItemFocus: (ChecklistItem) -> Unit,
onShowCheckedClick: () -> Unit,
backgroundColor: Color,
onAddItemClick: () -> Unit,
) {
items(uncheckedItems, key = { it.id }) { item ->
ReorderableItem(state, key = item.id) { isDragging ->
Expand Down Expand Up @@ -72,9 +75,9 @@ fun LazyListScope.ChecklistNoteChecklist(
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.padding(vertical = 8.dp)
.fillMaxWidth()
.clickable { onShowCheckedClick() },
.clickable { onShowCheckedClick() }
.padding(vertical = 8.dp),
) {
Icon(
imageVector = Icons.Sharp.ExpandMore,
Expand Down Expand Up @@ -108,9 +111,28 @@ fun LazyListScope.ChecklistNoteChecklist(
)
}
}
} else {
item {
Spacer(Modifier.height(4.dp))
} else item { Spacer(Modifier.height(4.dp)) }

item {
// "Add item" link:
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.clickable(onClick = onAddItemClick)
.padding(vertical = 8.dp)
.fillMaxWidth()
) {
Icon(
imageVector = Icons.Sharp.Add,
contentDescription = null,
modifier = Modifier.padding(horizontal = 12.dp),
tint = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.5f),
)
Text(
text = stringResource(R.string.add_item),
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.5f),
modifier = Modifier.padding(horizontal = 6.dp)
)
}
}
}
Expand Down
46 changes: 12 additions & 34 deletions app/src/main/java/us/huseli/retain/compose/notescreen/NoteScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.sharp.Add
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
Expand All @@ -30,6 +27,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextRange
Expand Down Expand Up @@ -188,7 +186,9 @@ fun NoteScreen(
verticalAlignment = Alignment.CenterVertically,
) {
OutlinedTextField(
modifier = Modifier.weight(1f),
modifier = Modifier
.weight(1f)
.onFocusChanged { if (it.isFocused) viewModel.setChecklistItemFocus(null) },
value = note?.title ?: "",
onValueChange = { viewModel.setTitle(it) },
textStyle = MaterialTheme.typography.headlineSmall,
Expand All @@ -199,7 +199,7 @@ fun NoteScreen(
keyboardActions = KeyboardActions(
onNext = {
if (note?.type == NoteType.CHECKLIST && checkedItems.isEmpty() && uncheckedItems.isEmpty()) {
viewModel.insertChecklistItem(text = "", checked = false, index = 0)
viewModel.insertChecklistItem(text = "", checked = false, position = 0)
}
}
),
Expand Down Expand Up @@ -258,36 +258,14 @@ fun NoteScreen(
backgroundColor = noteColor,
focusedItemId = focusedChecklistItemId,
onItemFocus = { viewModel.setChecklistItemFocus(it) },
)

item {
// "Add item" link:
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.clickable {
viewModel.insertChecklistItem(
text = "",
checked = false,
index = uncheckedItems.size,
)
}
.padding(vertical = 8.dp)
.fillMaxWidth()
) {
Icon(
imageVector = Icons.Sharp.Add,
contentDescription = null,
modifier = Modifier.padding(horizontal = 12.dp),
tint = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.5f),
)
Text(
text = stringResource(R.string.add_item),
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.5f),
modifier = Modifier.padding(horizontal = 6.dp)
onAddItemClick = {
viewModel.insertChecklistItem(
text = "",
checked = false,
position = uncheckedItems.size,
)
}
}
},
)
}
}
}
Expand Down
6 changes: 5 additions & 1 deletion app/src/main/java/us/huseli/retain/dao/NoteDao.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ interface NoteDao {
@Delete
suspend fun _delete(notes: Collection<Note>)

@Query("SELECT EXISTS(SELECT * FROM note WHERE noteId = :noteId)")
suspend fun _exists(noteId: UUID): Boolean

@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun _insert(note: Note)

Expand Down Expand Up @@ -77,6 +80,7 @@ interface NoteDao {
@Transaction
suspend fun upsert(note: Note) {
_makePlaceFor(note.id, note.position)
_insert(note)
if (_exists(note.id)) update(listOf(note))
else _insert(note)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,6 @@ data class ChecklistItem(
@ColumnInfo(name = "checklistItemChecked", defaultValue = "0") val checked: Boolean = false,
@ColumnInfo(name = "checklistItemPosition", defaultValue = "0") val position: Int = 0,
) : Comparable<ChecklistItem> {
override fun toString() =
"<ChecklistItem: id=$id, text=$text, position=$position, noteId=$noteId>"

override fun compareTo(other: ChecklistItem): Int = position - other.position

override fun equals(other: Any?) = other is ChecklistItem &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,6 @@ data class Note(
@ColumnInfo(name = "noteIsDeleted", defaultValue = "0") val isDeleted: Boolean = false,
@ColumnInfo(name = "noteIsArchived", defaultValue = "0") val isArchived: Boolean = false,
) : Comparable<Note> {
override fun toString() =
"<Note: id=$id, title=$title, created=$created, updated=$updated, isDeleted=$isDeleted, isArchived=$isArchived]>"

override fun compareTo(other: Note) = (updated.epochSecond - other.updated.epochSecond).toInt()

override fun equals(other: Any?) = other is Note &&
Expand Down
74 changes: 39 additions & 35 deletions app/src/main/java/us/huseli/retain/viewmodels/NoteViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -90,13 +90,20 @@ class NoteViewModel @Inject constructor(
_selectedImages.value = emptySet()
}

fun insertChecklistItem(text: String, checked: Boolean, index: Int): ChecklistItem =
ChecklistItem(text = text, checked = checked, position = index, noteId = _noteId).also { item ->
_checklistItems.value = _checklistItems.value.toMutableList().apply { add(item.position, item) }
setChecklistItemFocus(item)
updateChecklistItemPositions()
save(NotePojo.Component.CHECKLIST_ITEMS)
}
fun insertChecklistItem(text: String, checked: Boolean, position: Int): ChecklistItem {
val item = ChecklistItem(text = text, checked = checked, position = position, noteId = _noteId)

_checklistItems.value = _checklistItems.value
.map {
if (it.position >= position && it.checked == checked) it.copy(position = it.position + 1)
else it
}.plus(item).sorted()

setChecklistItemFocus(item)
save(NotePojo.Component.CHECKLIST_ITEMS)

return item
}

fun insertImage(uri: Uri) = viewModelScope.launch {
repository.uriToImage(uri, _noteId)?.let { image ->
Expand All @@ -112,8 +119,8 @@ class NoteViewModel @Inject constructor(
_selectedImages.value = _images.value.map { it.filename }.toSet()
}

fun setChecklistItemFocus(item: ChecklistItem) {
_focusedChecklistItemId.value = item.id
fun setChecklistItemFocus(item: ChecklistItem?) {
_focusedChecklistItemId.value = item?.id
}

fun setChecklistItemText(item: ChecklistItem, value: String) {
Expand Down Expand Up @@ -145,28 +152,29 @@ class NoteViewModel @Inject constructor(
* Splits item's text at cursor position, moves the last part to a new
* item, moves focus to this item.
*/
val index = _checklistItems.value.indexOfFirst { it.id == item.id }
val head = textFieldValue.text.substring(0, textFieldValue.selection.start)
val tail = textFieldValue.text.substring(textFieldValue.selection.start)

log("onNextItem: item=$item, head=$head, tail=$tail, index=$index")
setChecklistItemText(item, head)
insertChecklistItem(text = tail, checked = item.checked, index = index + 1)
insertChecklistItem(text = tail, checked = item.checked, position = item.position + 1)
}

fun switchItemPositions(from: ItemPosition, to: ItemPosition) {
/**
* We cannot use ItemPosition.index because the lazy column contains
* a whole bunch of other junk than checklist items.
*/
val fromIdx = _checklistItems.value.indexOfFirst { it.id == from.key }
val toIdx = _checklistItems.value.indexOfFirst { it.id == to.key }

if (fromIdx > -1 && toIdx > -1) {
log("switchItemPositions($from, $to) before: ${_checklistItems.value}")
_checklistItems.value = _checklistItems.value.toMutableList().apply { add(toIdx, removeAt(fromIdx)) }
log("switchItemPositions($from, $to) after: ${_checklistItems.value}")
updateChecklistItemPositions()
val fromItem = _checklistItems.value.firstOrNull { it.id == from.key }
val toItem = _checklistItems.value.firstOrNull { it.id == to.key }

if (fromItem != null && toItem != null) {
_checklistItems.value = _checklistItems.value.map { item ->
when (item.id) {
fromItem.id -> item.copy(position = toItem.position)
toItem.id -> item.copy(position = fromItem.position)
else -> item
}
}.sorted()
_isUnsaved.value = true
}
}
Expand All @@ -184,8 +192,12 @@ class NoteViewModel @Inject constructor(
}

fun uncheckAllItems() {
_checklistItems.value = _checklistItems.value.map { it.copy(checked = false) }
updateChecklistItemPositions()
var position = _checklistItems.value.filter { !it.checked }.maxOfOrNull { it.position } ?: -1

_checklistItems.value = _checklistItems.value.map { item ->
if (item.checked) item.copy(checked = false, position = ++position)
else item
}.sorted()
save(NotePojo.Component.CHECKLIST_ITEMS)
}

Expand All @@ -206,13 +218,12 @@ class NoteViewModel @Inject constructor(
}

fun updateChecklistItemChecked(item: ChecklistItem, checked: Boolean) {
_checklistItems.value = _checklistItems.value.toMutableList().apply {
val position = filter { it.checked == checked }.takeIf { it.isNotEmpty() }?.maxOf { it.position } ?: -1
val position = _checklistItems.value.filter { it.checked == checked }.maxOfOrNull { it.position } ?: -1

removeIf { it.id == item.id }
add(item.copy(checked = checked, position = position + 1))
}
updateChecklistItemPositions()
_checklistItems.value = _checklistItems.value.map {
if (it.id == item.id) it.copy(checked = checked, position = position + 1)
else it
}.sorted()
save(NotePojo.Component.CHECKLIST_ITEMS)
}

Expand All @@ -237,13 +248,6 @@ class NoteViewModel @Inject constructor(
_isUnsaved.value = false
}

private fun updateChecklistItemPositions() {
_checklistItems.value = _checklistItems.value.mapIndexed { index, item ->
if (item.position != index) item.copy(position = index) else item
}
_isUnsaved.value = true
}

private fun updateImagePositions() {
_images.value = _images.value.mapIndexed { index, image ->
if (image.position != index) image.copy(position = index) else image
Expand Down
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
plugins {
id("com.android.application") version "8.1.2" apply false
id("com.android.library") version "8.1.2" apply false
id("com.google.dagger.hilt.android") version "2.48" apply false
id("com.google.dagger.hilt.android") version "2.48.1" apply false
// id("com.google.devtools.ksp") version "1.8.10-1.0.9" apply false
id("com.google.devtools.ksp") version "1.9.10-1.0.13" apply false
id("org.jetbrains.kotlin.android") version "1.9.10" apply false
Expand Down

0 comments on commit fbbe5da

Please sign in to comment.