From 4b76958c14852f6cc57dd3ca31e494b35f8fe987 Mon Sep 17 00:00:00 2001 From: Jeffery Orazulike Date: Mon, 30 Oct 2023 12:10:51 +0100 Subject: [PATCH 1/3] feat(checklist): implemented the checklist --- .idea/deploymentTargetDropDown.xml | 15 +- .../notify/components/appbar/AddEditTopBar.kt | 16 +- .../data/converters/CollectionConverter.kt | 36 +++ .../notify/data/converters/ListConverter.kt | 27 -- .../com/aritra/notify/data/db/NoteDatabase.kt | 11 +- .../com/aritra/notify/domain/models/Note.kt | 1 + .../com/aritra/notify/domain/models/Todo.kt | 14 + .../notes/addEditScreen/AddEditRoute.kt | 11 +- .../notes/addEditScreen/AddEditScreen.kt | 68 ++++- .../notes/addEditScreen/AddEditViewModel.kt | 13 +- .../notes/addEditScreen/NoteChecklist.kt | 242 ++++++++++++++++++ .../main/java/com/aritra/notify/utils/Json.kt | 18 ++ app/src/main/res/values/strings.xml | 1 + 13 files changed, 416 insertions(+), 57 deletions(-) create mode 100644 app/src/main/java/com/aritra/notify/data/converters/CollectionConverter.kt delete mode 100644 app/src/main/java/com/aritra/notify/data/converters/ListConverter.kt create mode 100644 app/src/main/java/com/aritra/notify/domain/models/Todo.kt create mode 100644 app/src/main/java/com/aritra/notify/ui/screens/notes/addEditScreen/NoteChecklist.kt create mode 100644 app/src/main/java/com/aritra/notify/utils/Json.kt diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml index 0c0c3383..73b6ec28 100644 --- a/.idea/deploymentTargetDropDown.xml +++ b/.idea/deploymentTargetDropDown.xml @@ -3,7 +3,20 @@ - + + + + + + + + + + + + + + diff --git a/app/src/main/java/com/aritra/notify/components/appbar/AddEditTopBar.kt b/app/src/main/java/com/aritra/notify/components/appbar/AddEditTopBar.kt index 9be1d2ca..248d51e3 100644 --- a/app/src/main/java/com/aritra/notify/components/appbar/AddEditTopBar.kt +++ b/app/src/main/java/com/aritra/notify/components/appbar/AddEditTopBar.kt @@ -50,16 +50,14 @@ fun AddEditTopBar( { if (isNew) { onBackPress() + } else if (description.isBlank()) { + Toast.makeText( + context, + "Your note cannot be blank", + Toast.LENGTH_SHORT + ).show() } else { - if (description.isBlank()) { - Toast.makeText( - context, - "Your note cannot be blank", - Toast.LENGTH_SHORT - ).show() - } else { - saveNote() - } + saveNote() } } } diff --git a/app/src/main/java/com/aritra/notify/data/converters/CollectionConverter.kt b/app/src/main/java/com/aritra/notify/data/converters/CollectionConverter.kt new file mode 100644 index 00000000..bab0005c --- /dev/null +++ b/app/src/main/java/com/aritra/notify/data/converters/CollectionConverter.kt @@ -0,0 +1,36 @@ +package com.aritra.notify.data.converters + +import android.net.Uri +import androidx.room.TypeConverter +import com.aritra.notify.domain.models.Todo +import com.aritra.notify.utils.fromJson +import com.aritra.notify.utils.toString +import com.google.gson.Gson + +object CollectionConverter { + + @TypeConverter + fun fromUriListToString(value: List): String { + return Gson().toJson(value.map { it?.toString() ?: "" }) + } + + @TypeConverter + fun fromStringToUriList(value: String): List { + return try { + val stringList = Gson().fromJson>(value) // using extension function + stringList?.map { Uri.parse(it) } ?: emptyList() + } catch (e: Exception) { + emptyList() + } + } + + @TypeConverter + fun fromTodoListToString(value: List?): String? { + return Gson().toString(value) + } + + @TypeConverter + fun fromStringToTodoList(value: String?): List? { + return Gson().fromJson(value) + } +} diff --git a/app/src/main/java/com/aritra/notify/data/converters/ListConverter.kt b/app/src/main/java/com/aritra/notify/data/converters/ListConverter.kt deleted file mode 100644 index 4057c090..00000000 --- a/app/src/main/java/com/aritra/notify/data/converters/ListConverter.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.aritra.notify.data.converters - -import android.net.Uri -import androidx.room.TypeConverter -import com.google.gson.Gson -import com.google.gson.reflect.TypeToken - -object ListConverter { - - private inline fun Gson.fromJson(json: String): T = - fromJson(json, object : TypeToken() {}.type) - - @TypeConverter - fun fromListToString(value: List): String { - return Gson().toJson(value.map { it?.toString() ?: "" }) - } - - @TypeConverter - fun fromStringToList(value: String): List { - return try { - val stringList = Gson().fromJson>(value) // using extension function - stringList.map { Uri.parse(it) } - } catch (e: Exception) { - listOf() - } - } -} diff --git a/app/src/main/java/com/aritra/notify/data/db/NoteDatabase.kt b/app/src/main/java/com/aritra/notify/data/db/NoteDatabase.kt index 338e6ea4..e89b0f5e 100644 --- a/app/src/main/java/com/aritra/notify/data/db/NoteDatabase.kt +++ b/app/src/main/java/com/aritra/notify/data/db/NoteDatabase.kt @@ -5,8 +5,8 @@ import androidx.room.Database import androidx.room.Room import androidx.room.RoomDatabase import androidx.room.TypeConverters +import com.aritra.notify.data.converters.CollectionConverter import com.aritra.notify.data.converters.DateTypeConverter -import com.aritra.notify.data.converters.ListConverter import com.aritra.notify.data.converters.LocalDateTimeConverter import com.aritra.notify.data.converters.UriConverter import com.aritra.notify.data.dao.NoteDao @@ -14,8 +14,13 @@ import com.aritra.notify.data.dao.TrashNoteDao import com.aritra.notify.domain.models.Note import com.aritra.notify.domain.models.TrashNote -@Database(entities = [Note::class, TrashNote::class], version = 4) -@TypeConverters(DateTypeConverter::class, UriConverter::class, ListConverter::class, LocalDateTimeConverter::class) +@Database(entities = [Note::class, TrashNote::class], version = 5) +@TypeConverters( + DateTypeConverter::class, + UriConverter::class, + CollectionConverter::class, + LocalDateTimeConverter::class +) abstract class NoteDatabase : RoomDatabase() { abstract fun noteDao(): NoteDao diff --git a/app/src/main/java/com/aritra/notify/domain/models/Note.kt b/app/src/main/java/com/aritra/notify/domain/models/Note.kt index 47a7fb0e..119628bf 100644 --- a/app/src/main/java/com/aritra/notify/domain/models/Note.kt +++ b/app/src/main/java/com/aritra/notify/domain/models/Note.kt @@ -17,6 +17,7 @@ data class Note( var note: String = "", var dateTime: Date? = null, var image: List = emptyList(), + var checklist: List = emptyList(), @ColumnInfo(defaultValue = "false") var isMovedToTrash: Boolean = false, ) : Parcelable diff --git a/app/src/main/java/com/aritra/notify/domain/models/Todo.kt b/app/src/main/java/com/aritra/notify/domain/models/Todo.kt new file mode 100644 index 00000000..317e211e --- /dev/null +++ b/app/src/main/java/com/aritra/notify/domain/models/Todo.kt @@ -0,0 +1,14 @@ +package com.aritra.notify.domain.models + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +/** + * @property title the title of the todo + * @property isChecked the status of the todo + */ +@Parcelize +data class Todo( + val title: String = "", + val isChecked: Boolean = false, +) : Parcelable diff --git a/app/src/main/java/com/aritra/notify/ui/screens/notes/addEditScreen/AddEditRoute.kt b/app/src/main/java/com/aritra/notify/ui/screens/notes/addEditScreen/AddEditRoute.kt index 9e0fe9dd..2fcaeda2 100644 --- a/app/src/main/java/com/aritra/notify/ui/screens/notes/addEditScreen/AddEditRoute.kt +++ b/app/src/main/java/com/aritra/notify/ui/screens/notes/addEditScreen/AddEditRoute.kt @@ -12,6 +12,7 @@ import androidx.compose.ui.platform.LocalContext import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavBackStackEntry import androidx.navigation.NavController +import com.aritra.notify.domain.models.Todo import com.aritra.notify.ui.screens.notes.homeScreen.NoteScreenViewModel @Composable @@ -37,13 +38,14 @@ fun AddEditRoute( navController.popBackStack() } } - val saveNote: (String, String, List) -> Unit = remember(note, isNew) { - { title, description, images -> + val saveNote: (String, String, List, List) -> Unit = remember(note, isNew) { + { title, description, images, checklist -> if (isNew) { viewModel.insertNote( title = title, description = description, images = images, + checklist = checklist, onSuccess = { navigateBack() Toast.makeText(context, "Successfully Saved!", Toast.LENGTH_SHORT).show() @@ -54,12 +56,11 @@ fun AddEditRoute( title = title, description = description, images = images, + checklist = checklist, onSuccess = { updated -> + navigateBack() if (updated) { - navigateBack() Toast.makeText(context, "Successfully Updated!", Toast.LENGTH_SHORT).show() - } else { - navigateBack() } } ) diff --git a/app/src/main/java/com/aritra/notify/ui/screens/notes/addEditScreen/AddEditScreen.kt b/app/src/main/java/com/aritra/notify/ui/screens/notes/addEditScreen/AddEditScreen.kt index f81fd988..61bf6e61 100644 --- a/app/src/main/java/com/aritra/notify/ui/screens/notes/addEditScreen/AddEditScreen.kt +++ b/app/src/main/java/com/aritra/notify/ui/screens/notes/addEditScreen/AddEditScreen.kt @@ -1,7 +1,6 @@ package com.aritra.notify.ui.screens.notes.addEditScreen import android.net.Uri -import android.util.Log import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth @@ -13,6 +12,7 @@ import androidx.compose.foundation.verticalScroll import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.material3.TextField import androidx.compose.material3.TextFieldDefaults import androidx.compose.runtime.Composable @@ -45,6 +45,7 @@ import com.aritra.notify.components.camPreview.CameraPreview import com.aritra.notify.components.dialog.TextDialog import com.aritra.notify.components.drawing.DrawingScreen import com.aritra.notify.domain.models.Note +import com.aritra.notify.domain.models.Todo import com.aritra.notify.ui.theme.NotifyTheme import java.util.Date @@ -54,7 +55,7 @@ fun AddEditScreen( isNew: Boolean, modifier: Modifier = Modifier, navigateBack: () -> Unit, - saveNote: (String, String, List) -> Unit, + saveNote: (String, String, List, List) -> Unit, deleteNote: (() -> Unit) -> Unit, ) { val focus = LocalFocusManager.current @@ -66,7 +67,10 @@ fun AddEditScreen( mutableStateOf(note.note) } val images = remember { - mutableStateListOf(*note.image.filterNotNull().toTypedArray()) + mutableStateListOf() + } + val checklist = remember { + mutableStateListOf() } val cancelDialogState = remember { mutableStateOf(false) @@ -77,16 +81,25 @@ fun AddEditScreen( var openDrawingScreen by remember { mutableStateOf(false) } + var showChecklist by remember { + mutableStateOf(false) + } - // Makes sure that the title is updated when the note is updated + // Makes sure that the items are updated when the note is updated LaunchedEffect(note.title) { title = note.title } - - // Makes sure that the description is updated when the note is updated LaunchedEffect(note.note) { description = note.note } + LaunchedEffect(note.image) { + images.clear() + images.addAll(note.image.filterNotNull()) + } + LaunchedEffect(note.checklist) { + checklist.clear() + checklist.addAll(note.checklist.sortedBy { it.isChecked }) + } Scaffold( modifier = modifier, @@ -101,9 +114,7 @@ fun AddEditScreen( navigateBack }, saveNote = { - Log.e("AddEditScreen app bar", title) - Log.e("AddEditScreen app bar", description) - saveNote(title, description, images) + saveNote(title, description, images, checklist) }, deleteNote = deleteNote ) @@ -185,6 +196,21 @@ fun AddEditScreen( dateTime = note.dateTime ) + TextButton( + onClick = { + showChecklist = !showChecklist + }, + content = { + Text( + stringResource(R.string.show_checklist), + fontSize = 16.sp, + fontWeight = FontWeight.W700, + color = Color.Gray, + fontFamily = FontFamily(Font(R.font.poppins_medium)) + ) + } + ) + DescriptionTextField( scrollOffset = descriptionScrollOffset, contentSize = contentSize, @@ -241,6 +267,28 @@ fun AddEditScreen( ) } + if (showChecklist) { + NoteChecklist( + checklist = checklist, + onDismiss = { + showChecklist = false + }, + onAdd = { + checklist.add(Todo(title = it)) + }, + onDelete = { + checklist.removeAt(it) + }, + onUpdate = { index, newTitle -> + checklist[index] = checklist[index].copy(title = newTitle) + }, + onToggle = { index -> + checklist[index] = checklist[index].copy(isChecked = !checklist[index].isChecked) + checklist.sortBy { it.isChecked } + } + ) + } + TextDialog( title = stringResource(R.string.are_you_sure), description = stringResource(R.string.the_text_change_will_not_be_saved), @@ -265,7 +313,7 @@ private fun AddEditScreenPreview() = NotifyTheme { ), isNew = true, navigateBack = {}, - saveNote = { _, _, _ -> }, + saveNote = { _, _, _, _ -> }, deleteNote = {} ) } diff --git a/app/src/main/java/com/aritra/notify/ui/screens/notes/addEditScreen/AddEditViewModel.kt b/app/src/main/java/com/aritra/notify/ui/screens/notes/addEditScreen/AddEditViewModel.kt index 270fea82..b508673f 100644 --- a/app/src/main/java/com/aritra/notify/ui/screens/notes/addEditScreen/AddEditViewModel.kt +++ b/app/src/main/java/com/aritra/notify/ui/screens/notes/addEditScreen/AddEditViewModel.kt @@ -5,6 +5,7 @@ import android.net.Uri import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope import com.aritra.notify.domain.models.Note +import com.aritra.notify.domain.models.Todo import com.aritra.notify.domain.repository.NoteRepository import com.aritra.notify.domain.usecase.SaveSelectedImageUseCase import dagger.hilt.android.lifecycle.HiltViewModel @@ -69,12 +70,14 @@ class AddEditViewModel @Inject constructor( title: String, description: String, images: List, + checklist: List, onSuccess: () -> Unit, ) { viewModelScope.launch(Dispatchers.IO) { val note = _note.value.copy( title = title, - note = description + note = description, + checklist = checklist ) val id: Int = noteRepository.insertNoteToRoom(note).toInt() @@ -105,6 +108,7 @@ class AddEditViewModel @Inject constructor( title: String, description: String, images: List, + checklist: List, onSuccess: (updated: Boolean) -> Unit, ) = viewModelScope.launch(Dispatchers.IO) { val newNote = note.value @@ -112,7 +116,12 @@ class AddEditViewModel @Inject constructor( val oldNote = noteRepository.getNoteById(newNote.id) ?: return@launch // exit the method if the note has not been modified - if (oldNote.title == title && oldNote.note == description && oldNote.image == images) { + if ( + oldNote.title == title && + oldNote.note == description && + oldNote.image == images && + checklist == oldNote.checklist + ) { // Note has not been modified withContext(Dispatchers.Main) { onSuccess(false) diff --git a/app/src/main/java/com/aritra/notify/ui/screens/notes/addEditScreen/NoteChecklist.kt b/app/src/main/java/com/aritra/notify/ui/screens/notes/addEditScreen/NoteChecklist.kt new file mode 100644 index 00000000..0a605c13 --- /dev/null +++ b/app/src/main/java/com/aritra/notify/ui/screens/notes/addEditScreen/NoteChecklist.kt @@ -0,0 +1,242 @@ +package com.aritra.notify.ui.screens.notes.addEditScreen + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +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.filled.Edit +import androidx.compose.material3.CenterAlignedTopAppBar +import androidx.compose.material3.Checkbox +import androidx.compose.material3.Divider +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.ModalBottomSheet +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TextField +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +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.graphics.Color +import androidx.compose.ui.graphics.RectangleShape +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.text.input.KeyboardCapitalization +import androidx.compose.ui.text.style.TextDecoration +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import com.aritra.notify.R +import com.aritra.notify.domain.models.Todo + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun NoteChecklist( + checklist: List, + modifier: Modifier = Modifier, + onAdd: (String) -> Unit, + onDelete: (Int) -> Unit, + onUpdate: (Int, String) -> Unit, + onToggle: (Int) -> Unit, + onDismiss: () -> Unit, +) { + var showAddModal by remember { + mutableStateOf(false) + } + + ModalBottomSheet( + modifier = modifier, + onDismissRequest = onDismiss, + dragHandle = null, + content = { + Scaffold( + topBar = { + CenterAlignedTopAppBar( + title = { + Text("Checklist") + }, + actions = { + IconButton( + onClick = { + showAddModal = true + }, + modifier = Modifier.padding(8.dp), + content = { + Icon( + painter = painterResource(id = R.drawable.add_box_icon), + contentDescription = "Add" + ) + } + ) + } + ) + }, + content = { scaffoldPadding -> + LazyColumn( + modifier = Modifier + .fillMaxWidth() + .padding(scaffoldPadding), + contentPadding = PaddingValues(16.dp), + content = { + items( + count = checklist.size, + key = { index -> checklist[index] }, + itemContent = { index -> + ChecklistItem( + todo = checklist[index], + onValueChange = { + onUpdate(index, it) + }, + onCheckedChange = { + onToggle(index) + }, + onDelete = { + onDelete(index) + } + ) + if (index < checklist.lastIndex) { + Divider() + } + } + ) + } + ) + } + ) + } + ) + + if (showAddModal) { + AddEditTodoField( + text = "", + onUpdate = onAdd, + onDismiss = { + showAddModal = false + } + ) + } +} + +@Composable +private fun ChecklistItem( + todo: Todo, + modifier: Modifier = Modifier, + onValueChange: (String) -> Unit, + onCheckedChange: (Boolean) -> Unit, + onDelete: () -> Unit, +) { + var showEditModal by remember { + mutableStateOf(false) + } + Row( + modifier = modifier, + horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalAlignment = Alignment.CenterVertically, + content = { + Checkbox( + checked = todo.isChecked, + onCheckedChange = onCheckedChange + ) + Text( + todo.title, + modifier = Modifier.weight(1f), + style = TextStyle( + textDecoration = if (todo.isChecked) TextDecoration.LineThrough else TextDecoration.None + ), + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + IconButton( + onClick = { + showEditModal = true + }, + content = { + Icon( + imageVector = Icons.Default.Edit, + contentDescription = "Edit" + ) + } + ) + IconButton( + onClick = onDelete, + content = { + Icon( + painter = painterResource(id = R.drawable.ic_delete), + contentDescription = "Delete" + ) + } + ) + } + ) + + if (showEditModal) { + AddEditTodoField( + text = todo.title, + onUpdate = onValueChange, + onDismiss = { + showEditModal = false + } + ) + } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +private fun AddEditTodoField( + text: String, + onUpdate: (String) -> Unit, + onDismiss: () -> Unit, +) { + val focusRequester = remember { FocusRequester() } + var value by remember(text) { + mutableStateOf(text) + } + + LaunchedEffect(Unit) { + focusRequester.requestFocus() + } + + ModalBottomSheet( + onDismissRequest = onDismiss, + containerColor = Color.Transparent, + dragHandle = null, + shape = RectangleShape, + content = { + TextField( + modifier = Modifier + .fillMaxWidth() + .focusRequester(focusRequester), + value = value, + onValueChange = { + value = it + }, + singleLine = true, + keyboardActions = KeyboardActions( + onDone = { + if (value.isNotBlank()) { + onUpdate(value) + } + onDismiss() + } + ), + keyboardOptions = KeyboardOptions( + capitalization = KeyboardCapitalization.Sentences, + imeAction = ImeAction.Done + ) + ) + } + ) +} diff --git a/app/src/main/java/com/aritra/notify/utils/Json.kt b/app/src/main/java/com/aritra/notify/utils/Json.kt new file mode 100644 index 00000000..c644d400 --- /dev/null +++ b/app/src/main/java/com/aritra/notify/utils/Json.kt @@ -0,0 +1,18 @@ +package com.aritra.notify.utils + +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken + +inline fun Gson.fromJson(json: String?): T? { + if (json == null) { + return null + } + return this.fromJson(json, object : TypeToken() {}.type) +} + +inline fun Gson.toString(data: T?): String? { + if (data == null) { + return null + } + return toJson(data) +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3d1f99f3..39d9cf30 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -8,6 +8,7 @@ The text change will not be saved Notes Title + Show Checklist Share as Text Share as Image Cancel From 3db203fff344e80d6172b2178a8a854382fa757e Mon Sep 17 00:00:00 2001 From: Jeffery Orazulike Date: Tue, 31 Oct 2023 08:29:38 +0100 Subject: [PATCH 2/3] chore(checklist): flattened checklist into note --- .../components/appbar/AddEditBottomBar.kt | 9 ++ .../notes/addEditScreen/AddEditScreen.kt | 60 +++++------- .../notes/addEditScreen/NoteChecklist.kt | 98 +++++-------------- app/src/main/res/values/strings.xml | 2 +- 4 files changed, 59 insertions(+), 110 deletions(-) diff --git a/app/src/main/java/com/aritra/notify/components/appbar/AddEditBottomBar.kt b/app/src/main/java/com/aritra/notify/components/appbar/AddEditBottomBar.kt index 51f25d1b..04ca4e39 100644 --- a/app/src/main/java/com/aritra/notify/components/appbar/AddEditBottomBar.kt +++ b/app/src/main/java/com/aritra/notify/components/appbar/AddEditBottomBar.kt @@ -43,6 +43,7 @@ fun AddEditBottomBar( onSpeechRecognized: (String) -> Unit, showDrawingScreen: () -> Unit, showCameraSheet: () -> Unit, + addTodo: () -> Unit, ) { var showSheet by remember { mutableStateOf(false) } val sheetState = rememberModalBottomSheetState() @@ -137,6 +138,14 @@ fun AddEditBottomBar( showSheet = false } ) + BottomSheetOptions( + text = stringResource(R.string.add_todo), + icon = painterResource(id = R.drawable.add_box_icon), + onClick = { + addTodo() + showSheet = false + } + ) } ) } diff --git a/app/src/main/java/com/aritra/notify/ui/screens/notes/addEditScreen/AddEditScreen.kt b/app/src/main/java/com/aritra/notify/ui/screens/notes/addEditScreen/AddEditScreen.kt index 61bf6e61..1ceadce2 100644 --- a/app/src/main/java/com/aritra/notify/ui/screens/notes/addEditScreen/AddEditScreen.kt +++ b/app/src/main/java/com/aritra/notify/ui/screens/notes/addEditScreen/AddEditScreen.kt @@ -12,7 +12,6 @@ import androidx.compose.foundation.verticalScroll import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Text -import androidx.compose.material3.TextButton import androidx.compose.material3.TextField import androidx.compose.material3.TextFieldDefaults import androidx.compose.runtime.Composable @@ -66,6 +65,9 @@ fun AddEditScreen( var description by remember { mutableStateOf(note.note) } + var showAddTodo by remember { + mutableStateOf(false) + } val images = remember { mutableStateListOf() } @@ -81,9 +83,6 @@ fun AddEditScreen( var openDrawingScreen by remember { mutableStateOf(false) } - var showChecklist by remember { - mutableStateOf(false) - } // Makes sure that the items are updated when the note is updated LaunchedEffect(note.title) { @@ -196,18 +195,24 @@ fun AddEditScreen( dateTime = note.dateTime ) - TextButton( - onClick = { - showChecklist = !showChecklist + NoteChecklist( + checklist = checklist, + showAddTodo = showAddTodo, + onAdd = { + checklist.add(Todo(title = it)) }, - content = { - Text( - stringResource(R.string.show_checklist), - fontSize = 16.sp, - fontWeight = FontWeight.W700, - color = Color.Gray, - fontFamily = FontFamily(Font(R.font.poppins_medium)) - ) + onDelete = { + checklist.removeAt(it) + }, + onUpdate = { index, newTitle -> + checklist[index] = checklist[index].copy(title = newTitle) + }, + onToggle = { index -> + checklist[index] = checklist[index].copy(isChecked = !checklist[index].isChecked) + checklist.sortBy { it.isChecked } + }, + hideAddTodo = { + showAddTodo = false } ) @@ -238,6 +243,9 @@ fun AddEditScreen( }, onSpeechRecognized = { description += " $it" + }, + addTodo = { + showAddTodo = true } ) } @@ -267,28 +275,6 @@ fun AddEditScreen( ) } - if (showChecklist) { - NoteChecklist( - checklist = checklist, - onDismiss = { - showChecklist = false - }, - onAdd = { - checklist.add(Todo(title = it)) - }, - onDelete = { - checklist.removeAt(it) - }, - onUpdate = { index, newTitle -> - checklist[index] = checklist[index].copy(title = newTitle) - }, - onToggle = { index -> - checklist[index] = checklist[index].copy(isChecked = !checklist[index].isChecked) - checklist.sortBy { it.isChecked } - } - ) - } - TextDialog( title = stringResource(R.string.are_you_sure), description = stringResource(R.string.the_text_change_will_not_be_saved), diff --git a/app/src/main/java/com/aritra/notify/ui/screens/notes/addEditScreen/NoteChecklist.kt b/app/src/main/java/com/aritra/notify/ui/screens/notes/addEditScreen/NoteChecklist.kt index 0a605c13..e656f554 100644 --- a/app/src/main/java/com/aritra/notify/ui/screens/notes/addEditScreen/NoteChecklist.kt +++ b/app/src/main/java/com/aritra/notify/ui/screens/notes/addEditScreen/NoteChecklist.kt @@ -1,23 +1,19 @@ package com.aritra.notify.ui.screens.notes.addEditScreen import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth -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.filled.Edit -import androidx.compose.material3.CenterAlignedTopAppBar import androidx.compose.material3.Checkbox import androidx.compose.material3.Divider import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.ModalBottomSheet -import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TextField import androidx.compose.runtime.Composable @@ -42,90 +38,48 @@ import androidx.compose.ui.unit.dp import com.aritra.notify.R import com.aritra.notify.domain.models.Todo -@OptIn(ExperimentalMaterial3Api::class) @Composable fun NoteChecklist( checklist: List, + showAddTodo: Boolean, modifier: Modifier = Modifier, onAdd: (String) -> Unit, onDelete: (Int) -> Unit, onUpdate: (Int, String) -> Unit, onToggle: (Int) -> Unit, - onDismiss: () -> Unit, + hideAddTodo: () -> Unit, ) { - var showAddModal by remember { - mutableStateOf(false) - } - - ModalBottomSheet( - modifier = modifier, - onDismissRequest = onDismiss, - dragHandle = null, - content = { - Scaffold( - topBar = { - CenterAlignedTopAppBar( - title = { - Text("Checklist") + if (checklist.isNotEmpty()) { + Column( + modifier = modifier + .fillMaxWidth(), + content = { + checklist.forEachIndexed { index, todo -> + ChecklistItem( + todo = todo, + onValueChange = { + onUpdate(index, it) }, - actions = { - IconButton( - onClick = { - showAddModal = true - }, - modifier = Modifier.padding(8.dp), - content = { - Icon( - painter = painterResource(id = R.drawable.add_box_icon), - contentDescription = "Add" - ) - } - ) - } - ) - }, - content = { scaffoldPadding -> - LazyColumn( - modifier = Modifier - .fillMaxWidth() - .padding(scaffoldPadding), - contentPadding = PaddingValues(16.dp), - content = { - items( - count = checklist.size, - key = { index -> checklist[index] }, - itemContent = { index -> - ChecklistItem( - todo = checklist[index], - onValueChange = { - onUpdate(index, it) - }, - onCheckedChange = { - onToggle(index) - }, - onDelete = { - onDelete(index) - } - ) - if (index < checklist.lastIndex) { - Divider() - } - } - ) + onCheckedChange = { + onToggle(index) + }, + onDelete = { + onDelete(index) } ) + if (index < checklist.lastIndex) { + Divider() + } } - ) - } - ) + } + ) + } - if (showAddModal) { + if (showAddTodo) { AddEditTodoField( text = "", onUpdate = onAdd, - onDismiss = { - showAddModal = false - } + onDismiss = hideAddTodo ) } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 39d9cf30..02997acc 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -8,7 +8,7 @@ The text change will not be saved Notes Title - Show Checklist + Add Todo Share as Text Share as Image Cancel From 185af678a03eb9f3a0c8af8ec6b330a2e12b66a4 Mon Sep 17 00:00:00 2001 From: Jeffery Orazulike Date: Fri, 3 Nov 2023 09:52:25 +0100 Subject: [PATCH 3/3] fix(todo-list): todo list not getting updated on note save --- .../notes/addEditScreen/AddEditViewModel.kt | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/com/aritra/notify/ui/screens/notes/addEditScreen/AddEditViewModel.kt b/app/src/main/java/com/aritra/notify/ui/screens/notes/addEditScreen/AddEditViewModel.kt index f59d321a..884568c0 100644 --- a/app/src/main/java/com/aritra/notify/ui/screens/notes/addEditScreen/AddEditViewModel.kt +++ b/app/src/main/java/com/aritra/notify/ui/screens/notes/addEditScreen/AddEditViewModel.kt @@ -104,14 +104,7 @@ class AddEditViewModel @Inject constructor( } } } - fun updateReminderDateTime(dateTime: LocalDateTime?) { - _note.update { - it.copy( - reminderDateTime = dateTime, - isReminded = false - ) - } - } + fun updateNote( title: String, description: String, @@ -142,7 +135,8 @@ class AddEditViewModel @Inject constructor( newNote.copy( title = title, note = description, - dateTime = Date() + dateTime = Date(), + checklist = checklist // if the image has not been modified, use the old image uri // image = if (oldNote.image == newNote.image) { // oldNote.image @@ -163,4 +157,13 @@ class AddEditViewModel @Inject constructor( onSuccess(true) } } + + fun updateReminderDateTime(dateTime: LocalDateTime?) { + _note.update { + it.copy( + reminderDateTime = dateTime, + isReminded = false + ) + } + } }