diff --git a/app/src/main/java/io/github/wiiznokes/gitnote/data/AppPreferences.kt b/app/src/main/java/io/github/wiiznokes/gitnote/data/AppPreferences.kt index 51374d4..63a84d3 100644 --- a/app/src/main/java/io/github/wiiznokes/gitnote/data/AppPreferences.kt +++ b/app/src/main/java/io/github/wiiznokes/gitnote/data/AppPreferences.kt @@ -9,6 +9,7 @@ import io.github.wiiznokes.gitnote.ui.model.Cred import io.github.wiiznokes.gitnote.ui.model.CredType import io.github.wiiznokes.gitnote.ui.model.GitAuthor import io.github.wiiznokes.gitnote.ui.model.NoteMinWidth +import io.github.wiiznokes.gitnote.ui.model.NoteViewType import io.github.wiiznokes.gitnote.ui.model.SortOrder import io.github.wiiznokes.gitnote.ui.model.StorageConfiguration import io.github.wiiznokes.gitnote.ui.theme.Theme @@ -140,6 +141,7 @@ class AppPreferences( val noteMinWidth = enumPreference("noteMinWidth", NoteMinWidth.Default) val showFullNoteHeight = booleanPreference("showFullNoteHeight", false) + val noteViewType = enumPreference("noteViewType", NoteViewType.Grid) val rememberLastOpenedFolder = booleanPreference("rememberLastOpenedFolder", false) val lastOpenedFolder = stringPreference("lastOpenedFolder", "") diff --git a/app/src/main/java/io/github/wiiznokes/gitnote/ui/component/CustomDropDown.kt b/app/src/main/java/io/github/wiiznokes/gitnote/ui/component/CustomDropDown.kt index 7ac898c..5b88f17 100644 --- a/app/src/main/java/io/github/wiiznokes/gitnote/ui/component/CustomDropDown.kt +++ b/app/src/main/java/io/github/wiiznokes/gitnote/ui/component/CustomDropDown.kt @@ -16,10 +16,9 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalResources import androidx.compose.ui.unit.DpOffset import androidx.compose.ui.unit.dp -import androidx.core.util.TypedValueCompat.pxToDp +import androidx.compose.ui.platform.LocalDensity private val TAG = "CustomDropDown" @@ -41,10 +40,10 @@ fun CustomDropDown( mutableStateOf(Offset.Zero) } ) { - val m = LocalResources.current.displayMetrics - val x = pxToDp(clickPosition.value.x, m).dp - val y = pxToDp(clickPosition.value.y, m).dp - val offset = DpOffset(x, y) + val density = LocalDensity.current + val offset = with(density) { + DpOffset(clickPosition.value.x.toDp(), clickPosition.value.y.toDp()) + } MaterialTheme( shapes = MaterialTheme.shapes.copy(extraSmall = shape) @@ -75,4 +74,4 @@ fun CustomDropDown( } } } -} \ No newline at end of file +} diff --git a/app/src/main/java/io/github/wiiznokes/gitnote/ui/model/Grid.kt b/app/src/main/java/io/github/wiiznokes/gitnote/ui/model/Grid.kt index 328bbc8..8f35ef3 100644 --- a/app/src/main/java/io/github/wiiznokes/gitnote/ui/model/Grid.kt +++ b/app/src/main/java/io/github/wiiznokes/gitnote/ui/model/Grid.kt @@ -34,9 +34,14 @@ enum class NoteMinWidth(val size: Int) { override fun toString(): String = this.size.toString() } +enum class NoteViewType { + Grid, + List, +} + data class GridNote( @Embedded val note: Note, val isUnique: Boolean, val selected: Boolean = false, -) \ No newline at end of file +) diff --git a/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/app/grid/GridScreen.kt b/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/app/grid/GridScreen.kt index 55d85f0..930eabb 100644 --- a/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/app/grid/GridScreen.kt +++ b/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/app/grid/GridScreen.kt @@ -13,6 +13,7 @@ import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.safeContent @@ -21,6 +22,7 @@ import androidx.compose.foundation.lazy.staggeredgrid.LazyVerticalStaggeredGrid import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridItemSpan import androidx.compose.foundation.lazy.staggeredgrid.rememberLazyStaggeredGridState +import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.pullrefresh.PullRefreshIndicator import androidx.compose.material.pullrefresh.pullRefresh @@ -64,15 +66,15 @@ import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import androidx.paging.compose.collectAsLazyPagingItems -import dev.jeziellago.compose.markdowntext.MarkdownText import io.github.wiiznokes.gitnote.R import io.github.wiiznokes.gitnote.data.room.Note -import io.github.wiiznokes.gitnote.ui.component.CustomDropDown -import io.github.wiiznokes.gitnote.ui.component.CustomDropDownModel import io.github.wiiznokes.gitnote.ui.model.EditType import io.github.wiiznokes.gitnote.ui.model.FileExtension +import io.github.wiiznokes.gitnote.ui.model.GridNote +import io.github.wiiznokes.gitnote.ui.model.NoteViewType import io.github.wiiznokes.gitnote.ui.screen.app.DrawerScreen import io.github.wiiznokes.gitnote.ui.viewmodel.GridViewModel +import java.util.Date private const val TAG = "GridScreen" @@ -112,6 +114,8 @@ fun GridScreen( } } + val noteViewType by vm.prefs.noteViewType.getAsState() + val searchFocusRequester = remember { FocusRequester() } val fabExpanded = remember { @@ -148,7 +152,8 @@ fun GridScreen( onEditClick = onEditClick, selectedNotes = selectedNotes, nestedScrollConnection = nestedScrollConnection, - padding = padding + padding = padding, + noteViewType = noteViewType, ) TopBar( @@ -181,24 +186,11 @@ private fun GridView( onEditClick: (Note, EditType) -> Unit, selectedNotes: List, padding: PaddingValues, + noteViewType: NoteViewType, ) { val gridNotes = vm.gridNotes.collectAsLazyPagingItems() - - val gridState = rememberLazyStaggeredGridState() - - val query = vm.query.collectAsState() - var lastQuery: String = rememberSaveable { query.value } - - if (lastQuery != query.value) { - @Suppress("UNUSED_VALUE") - lastQuery = query.value - LaunchedEffect(null) { - gridState.animateScrollToItem(index = 0) - } - } - val isRefreshing by vm.isRefreshing.collectAsStateWithLifecycle() val pullRefreshState = rememberPullRefreshState(isRefreshing, { @@ -206,160 +198,80 @@ private fun GridView( vm.refresh() }) + val noteMinWidth = vm.prefs.noteMinWidth.getAsState() + val showFullPathOfNotes = vm.prefs.showFullPathOfNotes.getAsState() + val showFullNoteHeight = vm.prefs.showFullNoteHeight.getAsState() + + val topSpacerHeight = topBarHeight + 40.dp + 15.dp + Box { // todo: scroll even when there is nothing to scroll // todo: add scroll bar - val noteMinWidth = vm.prefs.noteMinWidth.getAsState() - val showFullPathOfNotes = vm.prefs.showFullPathOfNotes.getAsState() - val showFullNoteHeight = vm.prefs.showFullNoteHeight.getAsState() + val commonModifier = Modifier + .fillMaxSize() + .pullRefresh(pullRefreshState) + .nestedScroll(nestedScrollConnection) - LazyVerticalStaggeredGrid( - modifier = Modifier - .fillMaxSize() - .pullRefresh(pullRefreshState) - .nestedScroll(nestedScrollConnection), contentPadding = PaddingValues( - horizontal = 3.dp - ), columns = StaggeredGridCells.Adaptive(noteMinWidth.value.size.dp), state = gridState - - ) { - - item( - span = StaggeredGridItemSpan.FullLine - ) { - Spacer(modifier = Modifier.height(topBarHeight + 40.dp + 15.dp)) - } + when (noteViewType) { + NoteViewType.Grid -> { + val gridState = rememberLazyStaggeredGridState() - - items( - count = gridNotes.itemCount, - key = { index -> - val note = gridNotes[index]!! - note.note.id - } - ) { index -> - val gridNote = gridNotes[index]!! - - val dropDownExpanded = remember { - mutableStateOf(false) + LaunchedEffect(query.value) { + gridState.animateScrollToItem(index = 0) } - val clickPosition = remember { - mutableStateOf(Offset.Zero) - } + LazyVerticalStaggeredGrid( + modifier = commonModifier, + contentPadding = PaddingValues(horizontal = 3.dp), + columns = StaggeredGridCells.Adaptive(noteMinWidth.value.size.dp), + state = gridState + ) { + item(span = StaggeredGridItemSpan.FullLine) { + Spacer(modifier = Modifier.height(topSpacerHeight)) + } - Card( - colors = CardDefaults.cardColors( - containerColor = MaterialTheme.colorScheme.surface - ), - border = if (dropDownExpanded.value) { - BorderStroke( - width = 2.dp, color = MaterialTheme.colorScheme.primary - ) - } else if (gridNote.selected) { - BorderStroke( - width = 2.dp, color = MaterialTheme.colorScheme.onSurface - ) - } else { - BorderStroke( - width = 1.dp, - color = MaterialTheme.colorScheme.surfaceColorAtElevation(1000.dp) + items( + count = gridNotes.itemCount, + key = { index -> gridNotes[index]!!.note.id } + ) { index -> + val gridNote = gridNotes[index]!! + + NoteCard( + gridNote = gridNote, + vm = vm, + onEditClick = onEditClick, + selectedNotes = selectedNotes, + showFullPathOfNotes = showFullPathOfNotes.value, + showFullNoteHeight = showFullNoteHeight.value, + modifier = Modifier.padding(3.dp) ) - }, - modifier = Modifier - .sizeIn( - maxHeight = if (showFullNoteHeight.value) Dp.Unspecified else 500.dp - ) - .padding(3.dp) - .combinedClickable(onLongClick = { - dropDownExpanded.value = true - }, onClick = { - if (selectedNotes.isEmpty()) { - onEditClick( - gridNote.note, EditType.Update - ) - } else { - vm.selectNote( - gridNote.note, add = !gridNote.selected - ) - } - }) - .pointerInteropFilter { - clickPosition.value = Offset(it.x, it.y) - false - }, - ) { - Box { - - // need this box for clickPosition - Box { - CustomDropDown( - expanded = dropDownExpanded, - shape = MaterialTheme.shapes.medium, - options = listOf( - CustomDropDownModel( - text = stringResource(R.string.delete_this_note), - onClick = { - vm.deleteNote(gridNote.note) - }), - if (selectedNotes.isEmpty()) CustomDropDownModel( - text = stringResource( - R.string.select_multiple_notes - ), onClick = { - vm.selectNote(gridNote.note, true) - }) else null, - ), - clickPosition = clickPosition - ) - } - Column( - modifier = Modifier.padding(10.dp), - verticalArrangement = Arrangement.Top, - horizontalAlignment = Alignment.Start, - ) { - Text( - text = if (showFullPathOfNotes.value || !gridNote.isUnique) gridNote.note.relativePath else - gridNote.note.nameWithoutExtension(), - modifier = Modifier.padding(bottom = 6.dp), - overflow = TextOverflow.Ellipsis, - style = MaterialTheme.typography.titleMedium.copy( - fontWeight = FontWeight.Bold, - ), - color = MaterialTheme.colorScheme.tertiary - ) - - if (gridNote.note.fileExtension() is FileExtension.Md) { -// MarkdownText( -// markdown = gridNote.note.content, -// disableLinkMovementMethod = true, -// isTextSelectable = false, -// onLinkClicked = { } -// ) - Text( - text = gridNote.note.content, - modifier = Modifier, - overflow = TextOverflow.Ellipsis, - color = MaterialTheme.colorScheme.onSurface - ) - } else { - Text( - text = gridNote.note.content, - modifier = Modifier, - overflow = TextOverflow.Ellipsis, - color = MaterialTheme.colorScheme.onSurface - ) - } - } + } + + item(span = StaggeredGridItemSpan.FullLine) { + Spacer(modifier = Modifier.height(topBarHeight + 10.dp)) } } } + NoteViewType.List -> { + val listState = rememberLazyListState() + + LaunchedEffect(query.value) { + listState.animateScrollToItem(index = 0) + } - item( - span = StaggeredGridItemSpan.FullLine - ) { - Spacer(modifier = Modifier.height(topBarHeight + 10.dp)) + NoteListView( + gridNotes = gridNotes, + listState = listState, + modifier = commonModifier, + topSpacerHeight = topSpacerHeight, + selectedNotes = selectedNotes, + showFullPathOfNotes = showFullPathOfNotes.value, + onEditClick = onEditClick, + vm = vm, + ) } } @@ -378,6 +290,140 @@ private fun GridView( } +@Composable +private fun NoteCard( + gridNote: GridNote, + vm: GridViewModel, + onEditClick: (Note, EditType) -> Unit, + selectedNotes: List, + showFullPathOfNotes: Boolean, + showFullNoteHeight: Boolean, + modifier: Modifier = Modifier, +) { + val dropDownExpanded = remember { + mutableStateOf(false) + } + + val clickPosition = remember { + mutableStateOf(Offset.Zero) + } + + Card( + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.surface + ), + border = if (dropDownExpanded.value) { + BorderStroke( + width = 2.dp, color = MaterialTheme.colorScheme.primary + ) + } else if (gridNote.selected) { + BorderStroke( + width = 2.dp, color = MaterialTheme.colorScheme.onSurface + ) + } else { + BorderStroke( + width = 1.dp, + color = MaterialTheme.colorScheme.surfaceColorAtElevation(1000.dp) + ) + }, + modifier = modifier + .sizeIn( + maxHeight = if (showFullNoteHeight) Dp.Unspecified else 500.dp + ) + .combinedClickable(onLongClick = { + dropDownExpanded.value = true + }, onClick = { + if (selectedNotes.isEmpty()) { + onEditClick( + gridNote.note, EditType.Update + ) + } else { + vm.selectNote( + gridNote.note, add = !gridNote.selected + ) + } + }) + .pointerInteropFilter { + clickPosition.value = Offset(it.x, it.y) + false + }, + ) { + NoteCardContent( + gridNote = gridNote, + vm = vm, + selectedNotes = selectedNotes, + showFullPathOfNotes = showFullPathOfNotes, + clickPosition = clickPosition, + dropDownExpanded = dropDownExpanded + ) + } +} + +@Composable +private fun NoteCardContent( + gridNote: GridNote, + vm: GridViewModel, + selectedNotes: List, + showFullPathOfNotes: Boolean, + clickPosition: MutableState, + dropDownExpanded: MutableState, +) { + Box { + + // need this box for clickPosition + Box { + NoteActionsDropdown( + vm = vm, + gridNote = gridNote, + selectedNotes = selectedNotes, + dropDownExpanded = dropDownExpanded, + clickPosition = clickPosition + ) + } + Column( + modifier = Modifier.padding(10.dp), + verticalArrangement = Arrangement.Top, + horizontalAlignment = Alignment.Start, + ) { + val title = if (showFullPathOfNotes || !gridNote.isUnique) { + gridNote.note.relativePath + } else { + gridNote.note.nameWithoutExtension() + } + Text( + text = title, + modifier = Modifier.padding(bottom = 6.dp), + overflow = TextOverflow.Ellipsis, + style = MaterialTheme.typography.titleMedium.copy( + fontWeight = FontWeight.Bold, + ), + color = MaterialTheme.colorScheme.tertiary + ) + + if (gridNote.note.fileExtension() is FileExtension.Md) { +// MarkdownText( +// markdown = gridNote.note.content, +// disableLinkMovementMethod = true, +// isTextSelectable = false, +// onLinkClicked = { } +// ) + Text( + text = gridNote.note.content, + modifier = Modifier, + overflow = TextOverflow.Ellipsis, + color = MaterialTheme.colorScheme.onSurface + ) + } else { + Text( + text = gridNote.note.content, + modifier = Modifier, + overflow = TextOverflow.Ellipsis, + color = MaterialTheme.colorScheme.onSurface + ) + } + } + } +} // https://stackoverflow.com/questions/73079388/android-jetpack-compose-keyboard-not-close // https://medium.com/@debdut.saha.1/top-app-bar-animation-using-nestedscrollconnection-like-facebook-jetpack-compose-b446c109ee52 @@ -430,4 +476,4 @@ private fun rememberNestedScrollConnection( } } -} \ No newline at end of file +} diff --git a/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/app/grid/ListView.kt b/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/app/grid/ListView.kt new file mode 100644 index 0000000..30e0d61 --- /dev/null +++ b/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/app/grid/ListView.kt @@ -0,0 +1,202 @@ +package io.github.wiiznokes.gitnote.ui.screen.app.grid + +import androidx.compose.foundation.background +import androidx.compose.foundation.combinedClickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.rounded.Description +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.surfaceColorAtElevation +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.input.pointer.pointerInteropFilter +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import androidx.compose.material3.Text +import io.github.wiiznokes.gitnote.R +import io.github.wiiznokes.gitnote.data.room.Note +import io.github.wiiznokes.gitnote.ui.component.CustomDropDown +import io.github.wiiznokes.gitnote.ui.component.CustomDropDownModel +import io.github.wiiznokes.gitnote.ui.model.EditType +import io.github.wiiznokes.gitnote.ui.model.GridNote +import io.github.wiiznokes.gitnote.ui.viewmodel.GridViewModel +import java.text.DateFormat +import java.util.Date +import androidx.paging.compose.LazyPagingItems +import androidx.compose.runtime.MutableState +import androidx.compose.ui.geometry.Offset + +@Composable +internal fun NoteListView( + gridNotes: LazyPagingItems, + listState: LazyListState, + modifier: Modifier = Modifier, + topSpacerHeight: Dp, + selectedNotes: List, + showFullPathOfNotes: Boolean, + onEditClick: (Note, EditType) -> Unit, + vm: GridViewModel, +) { + LazyColumn( + modifier = modifier.fillMaxWidth(), + state = listState + ) { + item { + Spacer(modifier = Modifier.height(topSpacerHeight)) + } + + items( + count = gridNotes.itemCount, + key = { index -> + val note = gridNotes[index]!! + note.note.id + } + ) { index -> + val gridNote = gridNotes[index]!! + NoteListRow( + gridNote = gridNote, + vm = vm, + onEditClick = onEditClick, + selectedNotes = selectedNotes, + showFullPathOfNotes = showFullPathOfNotes, + ) + } + + item { + Spacer(modifier = Modifier.height(topBarHeight + 10.dp)) + } + } +} + +@Composable +private fun NoteListRow( + gridNote: GridNote, + vm: GridViewModel, + onEditClick: (Note, EditType) -> Unit, + selectedNotes: List, + showFullPathOfNotes: Boolean, +) { + val dropDownExpanded = remember { mutableStateOf(false) } + val clickPosition = remember { mutableStateOf(androidx.compose.ui.geometry.Offset.Zero) } + + val formattedDate = remember(gridNote.note.lastModifiedTimeMillis) { + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT) + .format(Date(gridNote.note.lastModifiedTimeMillis)) + } + + val title = gridNote.note.relativePath + + val rowBackground = + if (gridNote.selected) MaterialTheme.colorScheme.surfaceColorAtElevation(6.dp) + else MaterialTheme.colorScheme.surface + + Column( + modifier = Modifier + .fillMaxWidth() + .background(rowBackground) + .combinedClickable( + onLongClick = { dropDownExpanded.value = true }, + onClick = { + if (selectedNotes.isEmpty()) { + onEditClick(gridNote.note, EditType.Update) + } else { + vm.selectNote(gridNote.note, add = !gridNote.selected) + } + } + ) + .pointerInteropFilter { + clickPosition.value = Offset(it.x, it.y) + false + } + ) { + Box { + NoteActionsDropdown( + vm = vm, + gridNote = gridNote, + selectedNotes = selectedNotes, + dropDownExpanded = dropDownExpanded, + clickPosition = clickPosition + ) + + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 12.dp, vertical = 8.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(10.dp) + ) { + Icon( + imageVector = Icons.Rounded.Description, + contentDescription = null, + tint = MaterialTheme.colorScheme.onSurface + ) + + Column( + verticalArrangement = Arrangement.Center + ) { + Text( + text = title, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + style = MaterialTheme.typography.titleMedium.copy( + fontWeight = FontWeight.Bold, + ), + color = MaterialTheme.colorScheme.onSurface + ) + Text( + text = formattedDate, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + } + } + } + + HorizontalDivider( + thickness = 1.dp, + color = MaterialTheme.colorScheme.surfaceColorAtElevation(80.dp) + ) + } +} + +@Composable +internal fun NoteActionsDropdown( + vm: GridViewModel, + gridNote: GridNote, + selectedNotes: List, + dropDownExpanded: MutableState, + clickPosition: MutableState, +) { + CustomDropDown( + expanded = dropDownExpanded, + shape = MaterialTheme.shapes.medium, + options = listOf( + CustomDropDownModel( + text = stringResource(R.string.delete_this_note), + onClick = { vm.deleteNote(gridNote.note) }), + if (selectedNotes.isEmpty()) CustomDropDownModel( + text = stringResource(R.string.select_multiple_notes), + onClick = { vm.selectNote(gridNote.note, true) }) else null, + ), + clickPosition = clickPosition + ) +} diff --git a/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/app/grid/TopGrid.kt b/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/app/grid/TopGrid.kt index fe95cf1..dd16187 100644 --- a/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/app/grid/TopGrid.kt +++ b/app/src/main/java/io/github/wiiznokes/gitnote/ui/screen/app/grid/TopGrid.kt @@ -29,6 +29,8 @@ import androidx.compose.material.icons.filled.CloudUpload import androidx.compose.material.icons.rounded.Close import androidx.compose.material.icons.rounded.Menu import androidx.compose.material.icons.rounded.MoreVert +import androidx.compose.material.icons.rounded.ViewList +import androidx.compose.material.icons.rounded.ViewModule import androidx.compose.material3.DrawerState import androidx.compose.material3.Icon import androidx.compose.material3.IconButton @@ -66,6 +68,7 @@ import io.github.wiiznokes.gitnote.manager.SyncState import io.github.wiiznokes.gitnote.ui.component.CustomDropDown import io.github.wiiznokes.gitnote.ui.component.CustomDropDownModel import io.github.wiiznokes.gitnote.ui.component.SimpleIcon +import io.github.wiiznokes.gitnote.ui.model.NoteViewType import io.github.wiiznokes.gitnote.ui.viewmodel.GridViewModel import kotlinx.coroutines.delay import kotlinx.coroutines.launch @@ -141,6 +144,7 @@ private fun SearchBar( } val query = vm.query.collectAsState() + val noteViewType = vm.prefs.noteViewType.getAsState() if (query.value.isNotEmpty()) { BackHandler { clearQuery() @@ -190,24 +194,43 @@ private fun SearchBar( ) } }, - trailingIcon = if (queryTextField.text.isEmpty()) { - { - Row( - verticalAlignment = Alignment.CenterVertically - ) { + trailingIcon = { + Row( + verticalAlignment = Alignment.CenterVertically + ) { + if (queryTextField.text.isEmpty()) { val syncState = vm.syncState.collectAsState() - - SyncStateIcon(syncState.value, { + SyncStateIcon(syncState.value) { vm.consumeOkSyncState() - }) + } + } + + IconButton( + onClick = { vm.toggleViewType() } + ) { + SimpleIcon( + imageVector = if (noteViewType.value == NoteViewType.Grid) { + Icons.Rounded.ViewList + } else { + Icons.Rounded.ViewModule + }, + tint = MaterialTheme.colorScheme.onSurface, + contentDescription = stringResource( + if (noteViewType.value == NoteViewType.Grid) { + R.string.switch_to_list_view + } else { + R.string.switch_to_grid_view + } + ) + ) + } + if (queryTextField.text.isEmpty()) { Box { val expanded = remember { mutableStateOf(false) } IconButton( - onClick = { - expanded.value = true - } + onClick = { expanded.value = true } ) { SimpleIcon( imageVector = Icons.Rounded.MoreVert, @@ -244,19 +267,15 @@ private fun SearchBar( ) ) } - } - } - } else { - { - IconButton( - onClick = { - clearQuery() + } else { + IconButton( + onClick = { clearQuery() } + ) { + SimpleIcon( + imageVector = Icons.Rounded.Close, + tint = MaterialTheme.colorScheme.onSurface + ) } - ) { - SimpleIcon( - imageVector = Icons.Rounded.Close, - tint = MaterialTheme.colorScheme.onSurface - ) } } } @@ -410,4 +429,4 @@ private fun SyncStateIcon( modifier = modifier, ) } -} \ No newline at end of file +} diff --git a/app/src/main/java/io/github/wiiznokes/gitnote/ui/viewmodel/GridViewModel.kt b/app/src/main/java/io/github/wiiznokes/gitnote/ui/viewmodel/GridViewModel.kt index a2205fc..71a4754 100644 --- a/app/src/main/java/io/github/wiiznokes/gitnote/ui/viewmodel/GridViewModel.kt +++ b/app/src/main/java/io/github/wiiznokes/gitnote/ui/viewmodel/GridViewModel.kt @@ -17,6 +17,7 @@ import io.github.wiiznokes.gitnote.data.room.RepoDatabase import io.github.wiiznokes.gitnote.helper.NameValidation import io.github.wiiznokes.gitnote.manager.StorageManager import io.github.wiiznokes.gitnote.ui.model.FileExtension +import io.github.wiiznokes.gitnote.ui.model.NoteViewType import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -157,6 +158,17 @@ class GridViewModel : ViewModel() { _selectedNotes.emit(emptyList()) } + fun toggleViewType() { + viewModelScope.launch { + val next = if (prefs.noteViewType.get() == NoteViewType.Grid) { + NoteViewType.List + } else { + NoteViewType.Grid + } + prefs.noteViewType.update(next) + } + } + fun deleteSelectedNotes() { CoroutineScope(Dispatchers.IO).launch { val currentSelectedNotes = selectedNotes.value @@ -254,4 +266,3 @@ class GridViewModel : ViewModel() { } } } - diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 57ab7ab..88a605f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -69,6 +69,9 @@ Folder already exist No modification Grid + List + Switch to list view + Switch to grid view About Choose method Create local repository