diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/persistence/entities/SavedItem.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/persistence/entities/SavedItem.kt index e67b14357..7ffd74805 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/persistence/entities/SavedItem.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/persistence/entities/SavedItem.kt @@ -156,8 +156,6 @@ interface SavedItemDao { "LEFT OUTER JOIN Highlight on highlight.highlightId = SavedItemAndHighlightCrossRef.highlightId " + "WHERE SavedItem.savedItemId = :savedItemId " + - "AND SavedItem.serverSyncStatus != 2 " + - "AND Highlight.serverSyncStatus != 2 " + "GROUP BY SavedItem.savedItemId " ) diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/notebook/NotebookView.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/notebook/NotebookView.kt index cb7834c6c..d33f96401 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/notebook/NotebookView.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/notebook/NotebookView.kt @@ -46,7 +46,10 @@ import com.google.accompanist.systemuicontroller.rememberSystemUiController import dagger.hilt.android.AndroidEntryPoint import dev.jeziellago.compose.markdowntext.MarkdownText import kotlinx.coroutines.launch - +import android.util.Log +import androidx.compose.ui.platform.LocalDensity +import app.omnivore.omnivore.persistence.entities.Highlight +import kotlin.math.roundToInt @AndroidEntryPoint class NotebookActivity: ComponentActivity() { @@ -102,61 +105,105 @@ class NotebookActivity: ComponentActivity() { } } +fun notebookMD(notes: List, highlights: List): String { + var result = "" + + if (notes.isNotEmpty()) { + result += "## Notes\n" + notes.forEach { + result += it.annotation + "\n" + } + result += "\n" + } + + if (highlights.isNotEmpty()) { + result += "## Highlights\n" + highlights.forEach { + result += "> ${it.quote}\n" + if ((it.annotation?: "").isNotEmpty()) { + result += it.annotation + "\n" + } + } + result += "\n" + } + + return result +} + @OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterialApi::class) @Composable fun NotebookView(savedItemId: String, viewModel: NotebookViewModel) { - val onBackPressedDispatcher = LocalOnBackPressedDispatcherOwner.current?.onBackPressedDispatcher + var isMenuOpen by remember { + mutableStateOf(false) + } val savedItem = viewModel.getLibraryItemById(savedItemId).observeAsState() val scrollState = rememberScrollState() val modalBottomSheetState = rememberModalBottomSheetState( ModalBottomSheetValue.Hidden, ) + val coroutineScope = rememberCoroutineScope() + val snackBarHostState = remember { SnackbarHostState() } + val clipboard: ClipboardManager? = + LocalContext.current.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager? + val notes = savedItem.value?.highlights?.filter { it.type == "NOTE" } ?: listOf() val highlights = savedItem.value?.highlights?.filter { it.type == "HIGHLIGHT" } ?: listOf() ModalBottomSheetLayout( - modifier = Modifier.statusBarsPadding(), sheetBackgroundColor = Color.Transparent, sheetState = modalBottomSheetState, sheetContent = { - EditNoteModal() + // EditNoteModal() + Spacer(modifier = Modifier.weight(1.0F)) } ) { Scaffold( topBar = { TopAppBar( title = { Text("Notebook") }, - modifier = Modifier.statusBarsPadding(), colors = TopAppBarDefaults.topAppBarColors( containerColor = MaterialTheme.colorScheme.background ), - navigationIcon = { - IconButton(onClick = { - onBackPressedDispatcher?.onBackPressed() - }) { - Icon( - imageVector = androidx.compose.material.icons.Icons.Filled.ArrowBack, - modifier = Modifier, - contentDescription = "Back" - ) + actions = { + Box { + IconButton(onClick = { + isMenuOpen = true + }) { + Icon( + imageVector = Icons.Default.MoreVert, + contentDescription = null + ) + } + if (isMenuOpen) { + DropdownMenu( + expanded = isMenuOpen, + onDismissRequest = { isMenuOpen = false } + ) { + DropdownMenuItem( + text = { Text("Copy") }, + onClick = { + val clip = ClipData.newPlainText("notebook", notebookMD(notes, highlights)) + clipboard?.let { + it + clipboard?.setPrimaryClip(clip) + } ?: run { + coroutineScope.launch { + snackBarHostState + .showSnackbar("Notebook copied") + } + } + isMenuOpen = false + } + ) + } + } } - }, -// actions = { -// IconButton(onClick = { -// -// }) { -// Icon( -// imageVector = Icons.Default.MoreVert, -// contentDescription = null -// ) -// } -// } + } ) } ) { paddingValues -> Column( modifier = Modifier - .padding(paddingValues) .verticalScroll(scrollState) .fillMaxSize() ) { @@ -166,7 +213,6 @@ fun NotebookView(savedItemId: String, viewModel: NotebookViewModel) { } HighlightsList(it) } - Spacer(Modifier.weight(100f)) } } } diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebReaderLoadingContainer.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebReaderLoadingContainer.kt index 9005bec2a..8410aeb59 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebReaderLoadingContainer.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebReaderLoadingContainer.kt @@ -24,7 +24,6 @@ import androidx.compose.runtime.livedata.observeAsState import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp @@ -34,7 +33,8 @@ import androidx.core.view.WindowInsetsCompat import app.omnivore.omnivore.MainActivity import app.omnivore.omnivore.R import app.omnivore.omnivore.ui.components.WebReaderLabelsSelectionSheet -import app.omnivore.omnivore.ui.notebook.NotebookActivity +import app.omnivore.omnivore.ui.notebook.NotebookView +import app.omnivore.omnivore.ui.notebook.NotebookViewModel import app.omnivore.omnivore.ui.savedItemViews.SavedItemContextMenu import app.omnivore.omnivore.ui.theme.OmnivoreTheme import com.google.accompanist.systemuicontroller.rememberSystemUiController @@ -46,6 +46,7 @@ import kotlin.math.roundToInt @AndroidEntryPoint class WebReaderLoadingContainerActivity: ComponentActivity() { val viewModel: WebReaderViewModel by viewModels() + val notebookViewModel: NotebookViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -79,6 +80,7 @@ class WebReaderLoadingContainerActivity: ComponentActivity() { slug = slug, onLibraryIconTap = if (requestID != null) { { startMainActivity() } } else null, webReaderViewModel = viewModel, + notebookViewModel = notebookViewModel, ) } } @@ -101,13 +103,25 @@ class WebReaderLoadingContainerActivity: ComponentActivity() { } } +enum class BottomSheetState( +) { + NONE(), + PREFERENCES(), + NOTEBOOK(), + HIGHLIGHTNOTE() +} + + @OptIn(ExperimentalMaterialApi::class, ExperimentalMaterial3Api::class) @Composable -fun WebReaderLoadingContainer(slug: String? = null, requestID: String? = null, onLibraryIconTap: (() -> Unit)? = null, webReaderViewModel: WebReaderViewModel) { +fun WebReaderLoadingContainer(slug: String? = null, requestID: String? = null, + onLibraryIconTap: (() -> Unit)? = null, + webReaderViewModel: WebReaderViewModel, + notebookViewModel: NotebookViewModel) { val onBackPressedDispatcher = LocalOnBackPressedDispatcherOwner.current?.onBackPressedDispatcher var isMenuExpanded by remember { mutableStateOf(false) } - var showWebPreferencesDialog by remember { mutableStateOf(false ) } + var bottomSheetState by remember { mutableStateOf(app.omnivore.omnivore.ui.reader.BottomSheetState.NONE)} val currentThemeKey = webReaderViewModel.currentThemeKey.observeAsState() val currentTheme = Themes.values().find { it.themeKey == currentThemeKey.value } @@ -121,7 +135,6 @@ fun WebReaderLoadingContainer(slug: String? = null, requestID: String? = null, o webReaderViewModel.maxToolbarHeightPx = with(LocalDensity.current) { maxToolbarHeight.roundToPx().toFloat() } webReaderViewModel.loadItem(slug = slug, requestID = requestID) - val context = LocalContext.current val coroutineScope = rememberCoroutineScope() val styledContent = webReaderParams?.let { @@ -140,13 +153,29 @@ fun WebReaderLoadingContainer(slug: String? = null, requestID: String? = null, o val themeTintColor = Color(currentTheme?.foregroundColor ?: 0xFFFFFFFF) ModalBottomSheetLayout( - modifier = Modifier.statusBarsPadding(), + modifier = Modifier + .statusBarsPadding(), sheetBackgroundColor = Color.Transparent, sheetState = modalBottomSheetState, sheetContent = { - if (showWebPreferencesDialog) { - BottomSheetUI("Reader Preferences") { - ReaderPreferencesView(webReaderViewModel) + when (bottomSheetState) { + BottomSheetState.PREFERENCES -> { + BottomSheetUI("Reader Preferences") { + ReaderPreferencesView(webReaderViewModel) + } + } + BottomSheetState.NOTEBOOK -> { + webReaderParams?.let { params -> + BottomSheetUI(title = "Notebook") { + NotebookView(savedItemId = params.item.savedItemId, viewModel = notebookViewModel) + } + } + } + BottomSheetState.HIGHLIGHTNOTE -> { + + } + BottomSheetState.NONE -> { + } } Spacer(modifier = Modifier.weight(1.0F)) @@ -186,9 +215,10 @@ fun WebReaderLoadingContainer(slug: String? = null, requestID: String? = null, o } webReaderParams?.let { IconButton(onClick = { - val intent = Intent(context, NotebookActivity::class.java) - intent.putExtra("SAVED_ITEM_ID", it.item.savedItemId) - context.startActivity(intent) + bottomSheetState = BottomSheetState.NOTEBOOK + coroutineScope.launch { + modalBottomSheetState.show() + } }) { Icon( painter = painterResource(id = R.drawable.notebook), @@ -198,7 +228,7 @@ fun WebReaderLoadingContainer(slug: String? = null, requestID: String? = null, o } } IconButton(onClick = { - showWebPreferencesDialog = true + bottomSheetState = BottomSheetState.PREFERENCES coroutineScope.launch { modalBottomSheetState.show() } @@ -219,7 +249,7 @@ fun WebReaderLoadingContainer(slug: String? = null, requestID: String? = null, o webReaderParams?.let { params -> SavedItemContextMenu( isExpanded = isMenuExpanded, - isArchived = webReaderParams!!.item.isArchived, + isArchived = params.item.isArchived, onDismiss = { isMenuExpanded = false }, actionHandler = { webReaderViewModel.handleSavedItemAction( @@ -269,8 +299,6 @@ fun WebReaderLoadingContainer(slug: String? = null, requestID: String? = null, o @OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterialApi::class) @Composable fun BottomSheetUI(title: String?, content: @Composable () -> Unit) { - val onBackPressedDispatcher = LocalOnBackPressedDispatcherOwner.current?.onBackPressedDispatcher - Box( modifier = Modifier .wrapContentHeight()