Android: Add EditInfo sheet

Signed-off-by: Remy Chantenay <remy.chantenay@gmail.com>
This commit is contained in:
Remy Chantenay
2023-12-14 13:08:10 +01:00
parent 79baee56c6
commit 0b3aa22770
16 changed files with 434 additions and 16 deletions

View File

@ -0,0 +1,15 @@
mutation UpdatePage($input: UpdatePageInput!) {
updatePage(input: $input) {
... on UpdatePageSuccess {
updatedPage {
title
author
description
}
}
... on UpdatePageError {
errorCodes
}
}
}

View File

@ -15,6 +15,7 @@ import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import app.omnivore.omnivore.ui.auth.LoginViewModel
import app.omnivore.omnivore.ui.components.LabelsViewModel
import app.omnivore.omnivore.ui.editinfo.EditInfoViewModel
import app.omnivore.omnivore.ui.library.LibraryViewModel
import app.omnivore.omnivore.ui.library.SearchViewModel
import app.omnivore.omnivore.ui.root.RootView
@ -39,6 +40,7 @@ class MainActivity : ComponentActivity() {
val searchViewModel: SearchViewModel by viewModels()
val labelsViewModel: LabelsViewModel by viewModels()
val saveViewModel: SaveViewModel by viewModels()
val editInfoViewModel: EditInfoViewModel by viewModels()
val context = this
@ -65,7 +67,8 @@ class MainActivity : ComponentActivity() {
libraryViewModel,
settingsViewModel,
labelsViewModel,
saveViewModel)
saveViewModel,
editInfoViewModel)
}
}
}

View File

@ -226,7 +226,7 @@ interface SavedItemDao {
object SavedItemQueryConstants {
const val columns = "savedItemId, slug, publisherURLString, title, author, imageURLString, isArchived, pageURLString, contentReader, savedAt, readingProgress, wordsCount"
const val columns = "savedItemId, slug, publisherURLString, title, author, descriptionText, imageURLString, isArchived, pageURLString, contentReader, savedAt, readingProgress, wordsCount"
const val libraryColumns = "SavedItem.savedItemId, " +
"SavedItem.slug, " +
"SavedItem.createdAt, " +
@ -234,6 +234,7 @@ object SavedItemQueryConstants {
"SavedItem.publisherURLString, " +
"SavedItem.title, " +
"SavedItem.author, " +
"SavedItem.descriptionText, " +
"SavedItem.imageURLString, " +
"SavedItem.isArchived, " +
"SavedItem.pageURLString, " +

View File

@ -0,0 +1,146 @@
package app.omnivore.omnivore.ui.editinfo
import android.widget.Toast
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.unit.dp
import androidx.lifecycle.MutableLiveData
import app.omnivore.omnivore.R
@Composable
fun EditInfoSheetContent(
savedItemId: String?,
title: String?,
author: String?,
description: String?,
viewModel: EditInfoViewModel,
onCancel: () -> Unit,
onUpdated: () -> Unit
) {
val context = LocalContext.current
var titleTextFieldValue by remember { mutableStateOf(TextFieldValue(title ?: "")) }
var authorTextFieldValue by remember { mutableStateOf(TextFieldValue(author ?: "")) }
var descriptionTextFieldValue by remember { mutableStateOf(TextFieldValue(description ?: "")) }
fun showToast(msg: String) {
Toast.makeText(
context,
msg,
Toast.LENGTH_SHORT
).show()
}
val state: EditInfoState by viewModel.state.observeAsState(EditInfoState.DEFAULT)
val isUpdating = MutableLiveData(false)
when (state) {
EditInfoState.DEFAULT -> {
isUpdating.value = false
}
EditInfoState.UPDATING -> {
isUpdating.value = true
}
EditInfoState.ERROR -> {
isUpdating.value = false
showToast(viewModel.message ?: context.getString(R.string.edit_info_sheet_error))
viewModel.resetState()
}
EditInfoState.UPDATED -> {
isUpdating.value = false
showToast(viewModel.message ?: context.getString(R.string.edit_info_sheet_success))
onUpdated()
viewModel.resetState()
}
}
Surface(
modifier = Modifier
.fillMaxSize()
.background(MaterialTheme.colorScheme.background),
) {
Column(
verticalArrangement = Arrangement.Top,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.fillMaxSize()
.padding(horizontal = 16.dp)
) {
Row(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
) {
TextButton(onClick = onCancel) {
Text(text = stringResource(R.string.edit_info_sheet_action_cancel))
}
Text(stringResource(R.string.edit_info_sheet_title), fontWeight = FontWeight.ExtraBold)
TextButton(onClick = {
val newTitle = titleTextFieldValue.text
val newAuthor = authorTextFieldValue.text.ifEmpty { null }
val newDescription = descriptionTextFieldValue.text.ifEmpty { null }
savedItemId?.let {
viewModel.editInfo(it, newTitle, newAuthor, newDescription)
}
}) {
Text(stringResource(R.string.edit_info_sheet_action_save))
}
}
if (isUpdating.value == true) {
Spacer(modifier = Modifier.width(16.dp))
CircularProgressIndicator(
modifier = Modifier
.height(16.dp)
.width(16.dp),
strokeWidth = 2.dp,
color = MaterialTheme.colorScheme.primary
)
}
OutlinedTextField(
value = titleTextFieldValue,
label = { Text(stringResource(R.string.edit_info_sheet_text_field_label_title)) },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text),
onValueChange = { titleTextFieldValue = it },
modifier = Modifier.padding(top = 24.dp).fillMaxWidth()
)
OutlinedTextField(
value = authorTextFieldValue,
label = { Text(stringResource(R.string.edit_info_sheet_text_field_label_author)) },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text),
onValueChange = { authorTextFieldValue = it },
modifier = Modifier.padding(top = 24.dp).fillMaxWidth()
)
OutlinedTextField(
value = descriptionTextFieldValue,
label = { Text(stringResource(R.string.edit_info_sheet_text_field_label_description)) },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text),
singleLine = false, minLines = 1, maxLines = 5,
onValueChange = { descriptionTextFieldValue = it },
modifier = Modifier.padding(top = 24.dp).fillMaxWidth()
)
}
}
}

View File

@ -0,0 +1,108 @@
package app.omnivore.omnivore.ui.editinfo
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import app.omnivore.omnivore.Constants
import app.omnivore.omnivore.DatastoreKeys
import app.omnivore.omnivore.DatastoreRepository
import app.omnivore.omnivore.R
import app.omnivore.omnivore.dataService.DataService
import app.omnivore.omnivore.graphql.generated.UpdatePageMutation
import app.omnivore.omnivore.graphql.generated.type.UpdatePageInput
import app.omnivore.omnivore.ui.ResourceProvider
import com.apollographql.apollo3.ApolloClient
import com.apollographql.apollo3.api.Optional
import com.pspdfkit.internal.sa
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import javax.inject.Inject
enum class EditInfoState {
DEFAULT(),
UPDATING(),
ERROR(),
UPDATED()
}
@HiltViewModel
class EditInfoViewModel @Inject constructor(
private val dataService: DataService,
private val datastoreRepo: DatastoreRepository,
private val resourceProvider: ResourceProvider
) : ViewModel() {
val state = MutableLiveData(EditInfoState.DEFAULT)
var isLoading by mutableStateOf(false)
private set
var message by mutableStateOf<String?>(null)
private set
private fun getAuthToken(): String? = runBlocking {
datastoreRepo.getString(DatastoreKeys.omnivoreAuthToken)
}
fun editInfo(itemId: String, title: String, author: String?, description: String?) {
viewModelScope.launch {
isLoading = true
state.postValue(EditInfoState.UPDATING)
val authToken = getAuthToken()
if (authToken == null) {
message = resourceProvider.getString(R.string.edit_info_view_model_error_not_logged_in)
isLoading = false
return@launch
}
val apolloClient = ApolloClient.Builder()
.serverUrl("${Constants.apiURL}/api/graphql")
.addHttpHeader("Authorization", value = authToken)
.build()
try {
val response = apolloClient.mutation(
UpdatePageMutation(
UpdatePageInput(
pageId = itemId,
title = Optional.present(title),
byline = Optional.presentIfNotNull(author),
description = Optional.presentIfNotNull(description)
)
)
).execute()
withContext(Dispatchers.IO) {
val savedItem = dataService.db.savedItemDao().findById(itemId) ?: return@withContext
val updatedSavedItem = savedItem.copy(title = title, author = author, descriptionText = description)
dataService.db.savedItemDao().update(updatedSavedItem)
}
isLoading = false
val success = (response.data?.updatePage?.onUpdatePageSuccess?.updatedPage != null)
if (success) {
message = resourceProvider.getString(R.string.edit_info_sheet_success)
state.postValue(EditInfoState.UPDATED)
} else {
message = resourceProvider.getString(R.string.edit_info_sheet_error)
state.postValue(EditInfoState.ERROR)
}
} catch (e: java.lang.Exception) {
message = resourceProvider.getString(R.string.edit_info_sheet_error)
state.postValue(EditInfoState.ERROR)
}
}
}
fun resetState() {
state.postValue(EditInfoState.DEFAULT)
}
}

View File

@ -9,6 +9,7 @@ import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.material.icons.outlined.Delete
import androidx.compose.material.icons.outlined.Info
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.livedata.observeAsState
@ -49,7 +50,7 @@ fun LibraryNavigationBar(
savedItemViewModel.actionsMenuItemLiveData.postValue(null)
}) {
Icon(
imageVector = androidx.compose.material.icons.Icons.Filled.ArrowBack,
imageVector = Icons.Filled.ArrowBack,
modifier = Modifier,
contentDescription = "Back"
)
@ -75,6 +76,12 @@ fun LibraryNavigationBar(
)
}
}
IconButton(onClick = { savedItemViewModel.handleSavedItemAction(it.savedItem.savedItemId, SavedItemAction.EditInfo) }) {
Icon(
Icons.Outlined.Info,
contentDescription = null
)
}
IconButton(onClick = { savedItemViewModel.handleSavedItemAction(it.savedItem.savedItemId, SavedItemAction.EditLabels) }) {
Icon(
painter = painterResource(id = R.drawable.tag),

View File

@ -28,8 +28,10 @@ import app.omnivore.omnivore.Routes
import app.omnivore.omnivore.persistence.entities.SavedItemLabel
import app.omnivore.omnivore.persistence.entities.SavedItemWithLabelsAndHighlights
import app.omnivore.omnivore.ui.components.AddLinkSheetContent
import app.omnivore.omnivore.ui.editinfo.EditInfoSheetContent
import app.omnivore.omnivore.ui.components.LabelsSelectionSheetContent
import app.omnivore.omnivore.ui.components.LabelsViewModel
import app.omnivore.omnivore.ui.editinfo.EditInfoViewModel
import app.omnivore.omnivore.ui.savedItemViews.SavedItemCard
import app.omnivore.omnivore.ui.reader.PDFReaderActivity
import app.omnivore.omnivore.ui.reader.WebReaderLoadingContainerActivity
@ -45,11 +47,13 @@ fun LibraryView(
libraryViewModel: LibraryViewModel,
labelsViewModel: LabelsViewModel,
saveViewModel: SaveViewModel,
editInfoViewModel: EditInfoViewModel,
navController: NavHostController
) {
val scaffoldState: ScaffoldState = rememberScaffoldState()
val showLabelsSelectionSheet: Boolean by libraryViewModel.showLabelsSelectionSheetLiveData.observeAsState(false)
val showAddLinkSheet: Boolean by libraryViewModel.showAddLinkSheetLiveData.observeAsState(false)
val showEditInfoSheet: Boolean by libraryViewModel.showEditInfoSheetLiveData.observeAsState(false)
val coroutineScope = rememberCoroutineScope()
val modalBottomSheetState = rememberModalBottomSheetState(
@ -57,7 +61,7 @@ fun LibraryView(
confirmStateChange = { it != ModalBottomSheetValue.Hidden }
)
if (showLabelsSelectionSheet || showAddLinkSheet) {
if (showLabelsSelectionSheet || showAddLinkSheet || showEditInfoSheet) {
coroutineScope.launch {
modalBottomSheetState.show()
}
@ -78,7 +82,7 @@ fun LibraryView(
sheetBackgroundColor = Color.Transparent,
sheetState = modalBottomSheetState,
sheetContent = {
BottomSheetContent(libraryViewModel, labelsViewModel, saveViewModel)
BottomSheetContent(libraryViewModel, labelsViewModel, saveViewModel,editInfoViewModel)
Spacer(modifier = Modifier.weight(1.0F))
}
) {
@ -103,9 +107,13 @@ fun LibraryView(
}
@Composable
fun BottomSheetContent(libraryViewModel: LibraryViewModel, labelsViewModel: LabelsViewModel, saveViewModel: SaveViewModel) {
fun BottomSheetContent(libraryViewModel: LibraryViewModel,
labelsViewModel: LabelsViewModel,
saveViewModel: SaveViewModel,
editInfoViewModel: EditInfoViewModel) {
val showLabelsSelectionSheet: Boolean by libraryViewModel.showLabelsSelectionSheetLiveData.observeAsState(false)
val showAddLinkSheet: Boolean by libraryViewModel.showAddLinkSheetLiveData.observeAsState(false)
val showEditInfoSheet: Boolean by libraryViewModel.showEditInfoSheetLiveData.observeAsState(false)
val currentSavedItemData = libraryViewModel.currentSavedItemUnderEdit()
val labels: List<SavedItemLabel> by libraryViewModel.savedItemLabelsLiveData.observeAsState(listOf())
@ -118,7 +126,7 @@ fun BottomSheetContent(libraryViewModel: LibraryViewModel, labelsViewModel: Labe
initialSelectedLabels = currentSavedItemData.labels,
onCancel = {
libraryViewModel.showLabelsSelectionSheetLiveData.value = false
libraryViewModel.labelsSelectionCurrentItemLiveData.value = null
libraryViewModel.currentItemLiveData.value = null
},
isLibraryMode = false,
onSave = {
@ -128,7 +136,7 @@ fun BottomSheetContent(libraryViewModel: LibraryViewModel, labelsViewModel: Labe
labels = it
)
}
libraryViewModel.labelsSelectionCurrentItemLiveData.value = null
libraryViewModel.currentItemLiveData.value = null
libraryViewModel.showLabelsSelectionSheetLiveData.value = false
},
onCreateLabel = { newLabelName, labelHexValue ->
@ -144,7 +152,7 @@ fun BottomSheetContent(libraryViewModel: LibraryViewModel, labelsViewModel: Labe
isLibraryMode = true,
onSave = {
libraryViewModel.updateAppliedLabels(it)
libraryViewModel.labelsSelectionCurrentItemLiveData.value = null
libraryViewModel.currentItemLiveData.value = null
libraryViewModel.showLabelsSelectionSheetLiveData.value = false
},
onCreateLabel = { newLabelName, labelHexValue ->
@ -167,6 +175,25 @@ fun BottomSheetContent(libraryViewModel: LibraryViewModel, labelsViewModel: Labe
}
)
}
} else if (showEditInfoSheet) {
BottomSheetUI {
EditInfoSheetContent(
savedItemId = currentSavedItemData?.savedItem?.savedItemId,
title = currentSavedItemData?.savedItem?.title,
author = currentSavedItemData?.savedItem?.author,
description = currentSavedItemData?.savedItem?.descriptionText,
viewModel = editInfoViewModel,
onCancel = {
libraryViewModel.showEditInfoSheetLiveData.value = false
libraryViewModel.currentItemLiveData.value = null
},
onUpdated = {
libraryViewModel.showEditInfoSheetLiveData.value = false
libraryViewModel.currentItemLiveData.value = null
libraryViewModel.refresh()
}
)
}
}
}

View File

@ -62,8 +62,9 @@ class LibraryViewModel @Inject constructor(
val appliedFilterLiveData = MutableLiveData(SavedItemFilter.INBOX)
val appliedSortFilterLiveData = MutableLiveData(SavedItemSortFilter.NEWEST)
val showLabelsSelectionSheetLiveData = MutableLiveData(false)
val showEditInfoSheetLiveData = MutableLiveData(false)
val showAddLinkSheetLiveData = MutableLiveData(false)
val labelsSelectionCurrentItemLiveData = MutableLiveData<String?>(null)
val currentItemLiveData = MutableLiveData<String?>(null)
val savedItemLabelsLiveData = dataService.db.savedItemLabelDao().getSavedItemLabelsLiveData()
val activeLabelsLiveData = MutableLiveData<List<SavedItemLabel>>(listOf())
@ -296,9 +297,13 @@ class LibraryViewModel @Inject constructor(
}
}
SavedItemAction.EditLabels -> {
labelsSelectionCurrentItemLiveData.value = itemID
currentItemLiveData.value = itemID
showLabelsSelectionSheetLiveData.value = true
}
SavedItemAction.EditInfo -> {
currentItemLiveData.value = itemID
showEditInfoSheetLiveData.value = true
}
else -> {
}
@ -350,7 +355,7 @@ class LibraryViewModel @Inject constructor(
}
fun currentSavedItemUnderEdit(): SavedItemWithLabelsAndHighlights? {
labelsSelectionCurrentItemLiveData.value?.let { itemID ->
currentItemLiveData.value?.let { itemID ->
return itemsLiveData.value?.first { it.savedItem.savedItemId == itemID }
}
@ -376,4 +381,5 @@ enum class SavedItemAction {
Archive,
Unarchive,
EditLabels,
EditInfo,
}

View File

@ -167,6 +167,9 @@ class SearchViewModel @Inject constructor(
SavedItemAction.EditLabels -> {
// TODO
}
SavedItemAction.EditInfo -> {
// TODO
}
}
actionsMenuItemLiveData.postValue(null)
}

View File

@ -44,7 +44,9 @@ import kotlinx.coroutines.launch
import kotlin.math.roundToInt
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import app.omnivore.omnivore.ui.editinfo.EditInfoSheetContent
import app.omnivore.omnivore.ui.components.LabelsViewModel
import app.omnivore.omnivore.ui.editinfo.EditInfoViewModel
import app.omnivore.omnivore.ui.notebook.EditNoteModal
@AndroidEntryPoint
@ -52,6 +54,7 @@ class WebReaderLoadingContainerActivity: ComponentActivity() {
val viewModel: WebReaderViewModel by viewModels()
private val notebookViewModel: NotebookViewModel by viewModels()
private val labelsViewModel: LabelsViewModel by viewModels()
private val editInfoViewModel: EditInfoViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -89,6 +92,7 @@ class WebReaderLoadingContainerActivity: ComponentActivity() {
webReaderViewModel = viewModel,
notebookViewModel = notebookViewModel,
labelsViewModel = labelsViewModel,
editInfoViewModel = editInfoViewModel,
)
}
}
@ -119,7 +123,8 @@ enum class BottomSheetState(
EDITNOTE(),
HIGHLIGHTNOTE(),
LABELS(),
LINK()
LINK(),
EDIT_INFO(),
}
@ -129,7 +134,8 @@ fun WebReaderLoadingContainer(slug: String? = null, requestID: String? = null,
onLibraryIconTap: (() -> Unit)? = null,
webReaderViewModel: WebReaderViewModel,
notebookViewModel: NotebookViewModel,
labelsViewModel: LabelsViewModel) {
labelsViewModel: LabelsViewModel,
editInfoViewModel: EditInfoViewModel) {
val currentThemeKey = webReaderViewModel.currentThemeKey.observeAsState()
val currentTheme = Themes.values().find { it.themeKey == currentThemeKey.value }
val onBackPressedDispatcher = LocalOnBackPressedDispatcherOwner.current?.onBackPressedDispatcher
@ -181,7 +187,7 @@ fun WebReaderLoadingContainer(slug: String? = null, requestID: String? = null,
}
}
BottomSheetState.NOTEBOOK, BottomSheetState.EDITNOTE,
BottomSheetState.HIGHLIGHTNOTE, BottomSheetState.LABELS,
BottomSheetState.HIGHLIGHTNOTE, BottomSheetState.LABELS, BottomSheetState.EDIT_INFO,
BottomSheetState.LINK, -> {
showMenu()
}
@ -282,6 +288,28 @@ fun WebReaderLoadingContainer(slug: String? = null, requestID: String? = null,
)
}
}
BottomSheetState.EDIT_INFO -> {
BottomSheetUI(title = stringResource(R.string.web_reader_loading_container_bottom_sheet_edit_info)) {
EditInfoSheetContent(
savedItemId = webReaderParams?.item?.savedItemId,
title = webReaderParams?.item?.title,
author = webReaderParams?.item?.author,
description = webReaderParams?.item?.descriptionText,
viewModel = editInfoViewModel,
onCancel = {
coroutineScope.launch {
webReaderViewModel.resetBottomSheet()
}
},
onUpdated = {
coroutineScope.launch {
webReaderViewModel.updateItemTitle()
webReaderViewModel.resetBottomSheet()
}
}
)
}
}
BottomSheetState.LINK -> {
BottomSheetUI(title = stringResource(R.string.web_reader_loading_container_bottom_sheet_open_link)) {
OpenLinkView(webReaderViewModel)

View File

@ -296,6 +296,9 @@ class WebReaderViewModel @Inject constructor(
SavedItemAction.EditLabels -> {
bottomSheetStateLiveData.postValue(BottomSheetState.LABELS)
}
SavedItemAction.EditInfo -> {
bottomSheetStateLiveData.postValue(BottomSheetState.EDIT_INFO)
}
}
}
@ -525,6 +528,25 @@ class WebReaderViewModel @Inject constructor(
}
}
fun updateItemTitle() {
viewModelScope.launch {
slug?.let {
loadItemFromDB(it)
}
webReaderParamsLiveData.value?.item?.title?.let {
updateItemTitleInWebView(it)
}
}
}
private fun updateItemTitleInWebView(title: String) {
val script = "var event = new Event('updateTitle');event.title = '${title}';document.dispatchEvent(event);"
CoroutineScope(Dispatchers.Main).launch {
enqueueScript(script)
}
}
fun createNewSavedItemLabel(labelName: String, hexColorValue: String) {
viewModelScope.launch {
withContext(Dispatchers.IO) {

View File

@ -17,6 +17,7 @@ import app.omnivore.omnivore.Routes
import app.omnivore.omnivore.ui.auth.LoginViewModel
import app.omnivore.omnivore.ui.auth.WelcomeScreen
import app.omnivore.omnivore.ui.components.LabelsViewModel
import app.omnivore.omnivore.ui.editinfo.EditInfoViewModel
import app.omnivore.omnivore.ui.library.LibraryView
import app.omnivore.omnivore.ui.library.SearchView
import app.omnivore.omnivore.ui.library.LibraryViewModel
@ -34,6 +35,7 @@ fun RootView(
settingsViewModel: SettingsViewModel,
labelsViewModel: LabelsViewModel,
saveViewModel: SaveViewModel,
editInfoViewModel: EditInfoViewModel,
) {
val hasAuthToken: Boolean by loginViewModel.hasAuthTokenLiveData.observeAsState(false)
val systemUiController = rememberSystemUiController()
@ -59,7 +61,8 @@ fun RootView(
libraryViewModel = libraryViewModel,
settingsViewModel = settingsViewModel,
labelsViewModel = labelsViewModel,
saveViewModel = saveViewModel
saveViewModel = saveViewModel,
editInfoViewModel = editInfoViewModel,
)
} else {
WelcomeScreen(viewModel = loginViewModel)
@ -82,6 +85,7 @@ fun PrimaryNavigator(
settingsViewModel: SettingsViewModel,
labelsViewModel: LabelsViewModel,
saveViewModel: SaveViewModel,
editInfoViewModel: EditInfoViewModel,
) {
val navController = rememberNavController()
@ -92,6 +96,7 @@ fun PrimaryNavigator(
navController = navController,
labelsViewModel = labelsViewModel,
saveViewModel = saveViewModel,
editInfoViewModel = editInfoViewModel,
)
}

View File

@ -4,6 +4,7 @@ import android.content.Context
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.CheckCircle
import androidx.compose.material.icons.outlined.Delete
import androidx.compose.material.icons.outlined.Info
import androidx.compose.material.icons.outlined.List
import androidx.compose.material.icons.outlined.Share
import androidx.compose.material3.DropdownMenu
@ -31,6 +32,19 @@ fun SavedItemContextMenu(
expanded = isExpanded,
onDismissRequest = onDismiss
) {
DropdownMenuItem(
text = { Text(stringResource(R.string.saved_item_context_menu_action_edit_info)) },
onClick = {
actionHandler(SavedItemAction.EditInfo)
onDismiss()
},
leadingIcon = {
Icon(
Icons.Outlined.Info,
contentDescription = null
)
}
)
DropdownMenuItem(
text = { Text(stringResource(R.string.saved_item_context_menu_action_edit_labels)) },
onClick = {

View File

@ -152,6 +152,7 @@
<string name="web_reader_loading_container_error_msg">我们无法取得您的内容。</string>
<string name="web_reader_loading_container_bottom_sheet_reader_preferences">阅读器偏好设定</string>
<string name="web_reader_loading_container_bottom_sheet_notebook">笔记</string>
<string name="web_reader_loading_container_bottom_sheet_edit_info">编辑信息</string>
<string name="web_reader_loading_container_bottom_sheet_open_link">开启链接</string>
<!-- OpenLinkView -->
@ -178,6 +179,7 @@
<string name="save_view_model_page_saved_error">保存您的页面时出错</string>
<!-- SavedItemContextMenu -->
<string name="saved_item_context_menu_action_edit_info">编辑信息</string>
<string name="saved_item_context_menu_action_edit_labels">编辑标签</string>
<string name="saved_item_context_menu_action_archive">封存</string>
<string name="saved_item_context_menu_action_unarchive">取消封存</string>
@ -222,4 +224,17 @@
<string name="library_sort_oldest">从旧到新</string>
<string name="library_sort_recently_read">最近阅读</string>
<string name="library_sort_recently_published">最近发布</string>
<!-- EditInfoViewModel -->
<string name="edit_info_view_model_error_not_logged_in">您尚未登入。请在保存前登入。</string>
<!-- EditInfoSheet -->
<string name="edit_info_sheet_title">编辑信息</string>
<string name="edit_info_sheet_text_field_label_title">标题</string>
<string name="edit_info_sheet_text_field_label_author">作者</string>
<string name="edit_info_sheet_text_field_label_description">说明</string>
<string name="edit_info_sheet_action_save">节省</string>
<string name="edit_info_sheet_action_cancel">取消</string>
<string name="edit_info_sheet_error">编辑文章时出错</string>
<string name="edit_info_sheet_success">文章信息更新成功</string>
</resources>

View File

@ -151,6 +151,7 @@
<string name="web_reader_loading_container_error_msg">我們無法取得您的內容。</string>
<string name="web_reader_loading_container_bottom_sheet_reader_preferences">閱讀器偏好設定</string>
<string name="web_reader_loading_container_bottom_sheet_notebook">筆記本</string>
<string name="web_reader_loading_container_bottom_sheet_edit_info">编辑信息</string>
<string name="web_reader_loading_container_bottom_sheet_open_link">開啟連結</string>
<!-- OpenLinkView -->
@ -177,6 +178,7 @@
<string name="save_view_model_page_saved_error">儲存您的頁面時出錯</string>
<!-- SavedItemContextMenu -->
<string name="saved_item_context_menu_action_edit_info">编辑信息</string>
<string name="saved_item_context_menu_action_edit_labels">編輯標籤</string>
<string name="saved_item_context_menu_action_archive">封存</string>
<string name="saved_item_context_menu_action_unarchive">取消封存</string>

View File

@ -151,6 +151,8 @@
<string name="web_reader_loading_container_error_msg">We were unable to fetch your content.</string>
<string name="web_reader_loading_container_bottom_sheet_reader_preferences">Reader Preferences</string>
<string name="web_reader_loading_container_bottom_sheet_notebook">Notebook</string>
<string name="web_reader_loading_container_bottom_sheet_edit_info">Edit Info</string>
<string name="web_reader_loading_container_bottom_sheet_e">Notebook</string>
<string name="web_reader_loading_container_bottom_sheet_open_link">Open Link</string>
<!-- OpenLinkView -->
@ -177,6 +179,7 @@
<string name="save_view_model_page_saved_error">There was an error saving your page</string>
<!-- SavedItemContextMenu -->
<string name="saved_item_context_menu_action_edit_info">Edit Info</string>
<string name="saved_item_context_menu_action_edit_labels">Edit Labels</string>
<string name="saved_item_context_menu_action_archive">Archive</string>
<string name="saved_item_context_menu_action_unarchive">Unarchive</string>
@ -214,4 +217,17 @@
<string name="add_link_sheet_invalid_url_error">Invalid URL</string>
<string name="add_link_sheet_save_url_error">Error while saving link!</string>
<string name="add_link_sheet_save_url_success">Link successfully saved!</string>
<!-- EditInfoViewModel -->
<string name="edit_info_view_model_error_not_logged_in">You are not logged in. Please login before saving.</string>
<!-- EditInfoSheet -->
<string name="edit_info_sheet_title">Edit Info</string>
<string name="edit_info_sheet_text_field_label_title">Title</string>
<string name="edit_info_sheet_text_field_label_author">Author</string>
<string name="edit_info_sheet_text_field_label_description">Description</string>
<string name="edit_info_sheet_action_save">Save</string>
<string name="edit_info_sheet_action_cancel">Cancel</string>
<string name="edit_info_sheet_error">Error while editing article!</string>
<string name="edit_info_sheet_success">Article info successfully updated!</string>
</resources>