Fix page list: drag reorder, navigation, delete renumbering, state
Drag reorder: - Separated long-press drag from tap/menu: long-press-and-drag reorders, tap opens page, overflow menu (three dots) for delete/export - Fixed combinedClickable conflict that prevented drag from working Page navigation: - View-all-pages state persisted in SharedPreferences, restored on startup - Cleared when navigating back to editor from page list Delete renumbering: - After deleting a page, remaining pages are renumbered sequentially Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -8,25 +8,24 @@ import net.metacircular.engpad.data.db.EngPadDatabase
|
||||
class EngPadApp : Application() {
|
||||
val database: EngPadDatabase by lazy { EngPadDatabase.getInstance(this) }
|
||||
|
||||
fun getLastNotebookId(): Long {
|
||||
val prefs = getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
||||
return prefs.getLong(KEY_LAST_NOTEBOOK, 0)
|
||||
}
|
||||
private val prefs by lazy { getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE) }
|
||||
|
||||
fun setLastNotebookId(id: Long) {
|
||||
getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE).edit {
|
||||
putLong(KEY_LAST_NOTEBOOK, id)
|
||||
}
|
||||
}
|
||||
fun getLastNotebookId(): Long = prefs.getLong(KEY_LAST_NOTEBOOK, 0)
|
||||
|
||||
fun clearLastNotebookId() {
|
||||
getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE).edit {
|
||||
fun setLastNotebookId(id: Long) = prefs.edit { putLong(KEY_LAST_NOTEBOOK, id) }
|
||||
|
||||
fun clearLastNotebookId() = prefs.edit {
|
||||
remove(KEY_LAST_NOTEBOOK)
|
||||
remove(KEY_VIEW_ALL_PAGES)
|
||||
}
|
||||
}
|
||||
|
||||
fun isViewAllPages(): Boolean = prefs.getBoolean(KEY_VIEW_ALL_PAGES, false)
|
||||
|
||||
fun setViewAllPages(value: Boolean) = prefs.edit { putBoolean(KEY_VIEW_ALL_PAGES, value) }
|
||||
|
||||
companion object {
|
||||
private const val PREFS_NAME = "engpad_prefs"
|
||||
private const val KEY_LAST_NOTEBOOK = "last_notebook_id"
|
||||
private const val KEY_VIEW_ALL_PAGES = "view_all_pages"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ class MainActivity : ComponentActivity() {
|
||||
val app = application as EngPadApp
|
||||
val database = app.database
|
||||
val lastNotebookId = app.getLastNotebookId()
|
||||
val wasViewAllPages = app.isViewAllPages()
|
||||
setContent {
|
||||
EngPadTheme {
|
||||
val navController = rememberNavController()
|
||||
@@ -20,8 +21,10 @@ class MainActivity : ComponentActivity() {
|
||||
navController = navController,
|
||||
database = database,
|
||||
lastNotebookId = lastNotebookId,
|
||||
lastViewAllPages = wasViewAllPages,
|
||||
onNotebookOpened = { app.setLastNotebookId(it) },
|
||||
onNotebookClosed = { app.clearLastNotebookId() },
|
||||
onViewAllPagesChanged = { app.setViewAllPages(it) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,15 +35,22 @@ fun EngPadNavGraph(
|
||||
navController: NavHostController,
|
||||
database: EngPadDatabase,
|
||||
lastNotebookId: Long = 0,
|
||||
lastViewAllPages: Boolean = false,
|
||||
onNotebookOpened: (Long) -> Unit = {},
|
||||
onNotebookClosed: () -> Unit = {},
|
||||
onViewAllPagesChanged: (Boolean) -> Unit = {},
|
||||
) {
|
||||
// Auto-navigate to last notebook on startup
|
||||
var autoNavigated by remember { mutableStateOf(false) }
|
||||
LaunchedEffect(lastNotebookId) {
|
||||
if (!autoNavigated && lastNotebookId > 0) {
|
||||
autoNavigated = true
|
||||
if (lastViewAllPages) {
|
||||
navController.navigate(Routes.editor(lastNotebookId))
|
||||
navController.navigate(Routes.pages(lastNotebookId))
|
||||
} else {
|
||||
navController.navigate(Routes.editor(lastNotebookId))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,6 +118,7 @@ fun EngPadNavGraph(
|
||||
navController.popBackStack(Routes.NOTEBOOKS, false)
|
||||
},
|
||||
onViewAllPages = {
|
||||
onViewAllPagesChanged(true)
|
||||
navController.navigate(Routes.pages(notebookId))
|
||||
},
|
||||
)
|
||||
@@ -145,7 +153,7 @@ fun EngPadNavGraph(
|
||||
pageSize = pageSize,
|
||||
database = database,
|
||||
onPageClick = { pageId, _ ->
|
||||
// Pass selected page ID back to the editor
|
||||
onViewAllPagesChanged(false)
|
||||
navController.previousBackStackEntry
|
||||
?.savedStateHandle
|
||||
?.set("selectedPageId", pageId)
|
||||
|
||||
@@ -3,8 +3,7 @@ package net.metacircular.engpad.ui.pages
|
||||
import android.graphics.Paint as AndroidPaint
|
||||
import android.graphics.Path as AndroidPath
|
||||
import androidx.compose.foundation.Canvas
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
@@ -12,6 +11,7 @@ import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.lazy.grid.GridCells
|
||||
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||
import androidx.compose.foundation.lazy.grid.items
|
||||
@@ -24,6 +24,7 @@ import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.FloatingActionButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.TopAppBar
|
||||
@@ -31,8 +32,8 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.mutableStateListOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
@@ -78,7 +79,6 @@ fun PageListScreen(
|
||||
|
||||
var pageToDelete by remember { mutableStateOf<Page?>(null) }
|
||||
|
||||
// Maintain an observable mutable list for drag reorder
|
||||
val reorderablePages = remember { mutableStateListOf<Page>() }
|
||||
LaunchedEffect(pages) {
|
||||
reorderablePages.clear()
|
||||
@@ -151,7 +151,6 @@ fun PageListScreen(
|
||||
},
|
||||
modifier = Modifier.longPressDraggableHandle(
|
||||
onDragStopped = {
|
||||
// Persist the new order
|
||||
viewModel.reorderPages(
|
||||
notebookId,
|
||||
reorderablePages.map { it.id },
|
||||
@@ -183,7 +182,6 @@ fun PageListScreen(
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
private fun PageThumbnail(
|
||||
page: Page,
|
||||
@@ -205,25 +203,22 @@ private fun PageThumbnail(
|
||||
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier
|
||||
modifier = modifier
|
||||
.graphicsLayer {
|
||||
if (isDragging) {
|
||||
scaleX = 1.05f
|
||||
scaleY = 1.05f
|
||||
alpha = 0.8f
|
||||
}
|
||||
}
|
||||
.then(modifier),
|
||||
},
|
||||
) {
|
||||
Box {
|
||||
Card(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.combinedClickable(
|
||||
onClick = onClick,
|
||||
onLongClick = { if (!isDragging) showMenu = true },
|
||||
),
|
||||
.clickable(onClick = onClick),
|
||||
) {
|
||||
Box {
|
||||
Canvas(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
@@ -259,6 +254,17 @@ private fun PageThumbnail(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Overflow menu button (top-right corner)
|
||||
Box(modifier = Modifier.align(Alignment.TopEnd)) {
|
||||
Surface(
|
||||
onClick = { showMenu = true },
|
||||
color = androidx.compose.ui.graphics.Color.Transparent,
|
||||
modifier = Modifier.size(32.dp),
|
||||
) {
|
||||
Box(contentAlignment = Alignment.Center) {
|
||||
Text("\u22EE", style = MaterialTheme.typography.bodyLarge)
|
||||
}
|
||||
}
|
||||
DropdownMenu(
|
||||
expanded = showMenu,
|
||||
@@ -266,20 +272,17 @@ private fun PageThumbnail(
|
||||
) {
|
||||
DropdownMenuItem(
|
||||
text = { Text("Export as JPG") },
|
||||
onClick = {
|
||||
showMenu = false
|
||||
onExportJpg()
|
||||
},
|
||||
onClick = { showMenu = false; onExportJpg() },
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = { Text("Delete page") },
|
||||
onClick = {
|
||||
showMenu = false
|
||||
onDelete()
|
||||
},
|
||||
onClick = { showMenu = false; onDelete() },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Text(
|
||||
text = "Page ${page.pageNumber}",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
|
||||
@@ -28,6 +28,9 @@ class PageListViewModel(
|
||||
fun deletePage(pageId: Long) {
|
||||
viewModelScope.launch {
|
||||
repository.deletePage(pageId)
|
||||
// Renumber remaining pages sequentially
|
||||
val remaining = repository.getPagesList(notebookId)
|
||||
repository.reorderPages(notebookId, remaining.map { it.id })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user