Improve FeedOptionView
This commit is contained in:
parent
43bbb87280
commit
efba776db3
4
app/proguard-rules.pro
vendored
4
app/proguard-rules.pro
vendored
|
@ -14,11 +14,11 @@
|
|||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
-renamesourcefileattribute SourceFile
|
||||
|
||||
-dontobfuscate
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ package me.ash.reader.ui.component
|
|||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.ModalBottomSheetDefaults
|
||||
|
@ -12,6 +13,7 @@ import androidx.compose.material3.Surface
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.zIndex
|
||||
|
||||
|
@ -55,7 +57,8 @@ fun BottomDrawer(
|
|||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.size(38.dp, 4.dp)
|
||||
.size(30.dp, 4.dp)
|
||||
.clip(CircleShape)
|
||||
.background(MaterialTheme.colorScheme.outline.copy(alpha = 0.2f))
|
||||
.zIndex(1f)
|
||||
) {}
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
package me.ash.reader.ui.component
|
||||
|
||||
import androidx.compose.foundation.horizontalScroll
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.text.KeyboardActionScope
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusManager
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@Composable
|
||||
fun ClipboardTextField(
|
||||
modifier: Modifier = Modifier,
|
||||
readOnly: Boolean = false,
|
||||
value: String = "",
|
||||
onValueChange: (String) -> Unit = {},
|
||||
placeholder: String = "",
|
||||
errorText: String = "",
|
||||
imeAction: ImeAction = ImeAction.Done,
|
||||
focusManager: FocusManager? = null,
|
||||
onConfirm: (String) -> Unit = {},
|
||||
) {
|
||||
Column(modifier = modifier) {
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
TextField(
|
||||
readOnly = readOnly,
|
||||
value = value,
|
||||
onValueChange = onValueChange,
|
||||
placeholder = placeholder,
|
||||
errorMessage = errorText,
|
||||
keyboardActions = KeyboardActions(
|
||||
onDone = if (imeAction == ImeAction.Done)
|
||||
action(focusManager, onConfirm, value) else null,
|
||||
onGo = if (imeAction == ImeAction.Go)
|
||||
action(focusManager, onConfirm, value) else null,
|
||||
onNext = if (imeAction == ImeAction.Next)
|
||||
action(focusManager, onConfirm, value) else null,
|
||||
onPrevious = if (imeAction == ImeAction.Previous)
|
||||
action(focusManager, onConfirm, value) else null,
|
||||
onSearch = if (imeAction == ImeAction.Search)
|
||||
action(focusManager, onConfirm, value) else null,
|
||||
onSend = if (imeAction == ImeAction.Send)
|
||||
action(focusManager, onConfirm, value) else null,
|
||||
),
|
||||
keyboardOptions = KeyboardOptions(
|
||||
imeAction = imeAction
|
||||
),
|
||||
)
|
||||
if (errorText.isNotEmpty()) {
|
||||
SelectionContainer {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.padding(start = 16.dp)
|
||||
.horizontalScroll(rememberScrollState()),
|
||||
text = errorText,
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
maxLines = 1,
|
||||
softWrap = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
}
|
||||
}
|
||||
|
||||
private fun action(
|
||||
focusManager: FocusManager?,
|
||||
onConfirm: (String) -> Unit,
|
||||
value: String
|
||||
): KeyboardActionScope.() -> Unit = {
|
||||
focusManager?.clearFocus()
|
||||
onConfirm(value)
|
||||
}
|
|
@ -44,7 +44,7 @@ fun SelectionChip(
|
|||
val focusManager = LocalFocusManager.current
|
||||
|
||||
FilterChip(
|
||||
modifier = modifier,
|
||||
modifier = modifier.defaultMinSize(minHeight = 36.dp),
|
||||
colors = ChipDefaults.filterChipColors(
|
||||
backgroundColor = MaterialTheme.colorScheme.surfaceVariant,
|
||||
contentColor = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
|
|
90
app/src/main/java/me/ash/reader/ui/component/TextField.kt
Normal file
90
app/src/main/java/me/ash/reader/ui/component/TextField.kt
Normal file
|
@ -0,0 +1,90 @@
|
|||
package me.ash.reader.ui.component
|
||||
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.TextFieldDefaults
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.Close
|
||||
import androidx.compose.material.icons.rounded.ContentPaste
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.remember
|
||||
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.platform.LocalClipboardManager
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import kotlinx.coroutines.delay
|
||||
import me.ash.reader.R
|
||||
|
||||
@Composable
|
||||
fun TextField(
|
||||
readOnly: Boolean,
|
||||
value: String,
|
||||
onValueChange: (String) -> Unit,
|
||||
placeholder: String,
|
||||
errorMessage: String,
|
||||
keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
|
||||
keyboardActions: KeyboardActions = KeyboardActions(),
|
||||
) {
|
||||
val clipboardManager = LocalClipboardManager.current
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
delay(100) // ???
|
||||
focusRequester.requestFocus()
|
||||
}
|
||||
|
||||
androidx.compose.material.TextField(
|
||||
modifier = Modifier.focusRequester(focusRequester),
|
||||
colors = TextFieldDefaults.textFieldColors(
|
||||
backgroundColor = Color.Transparent,
|
||||
cursorColor = MaterialTheme.colorScheme.onSurface,
|
||||
textColor = MaterialTheme.colorScheme.onSurface,
|
||||
focusedIndicatorColor = MaterialTheme.colorScheme.primary,
|
||||
),
|
||||
enabled = !readOnly,
|
||||
value = value,
|
||||
onValueChange = {
|
||||
if (!readOnly) onValueChange(it)
|
||||
},
|
||||
placeholder = {
|
||||
Text(
|
||||
text = placeholder,
|
||||
color = MaterialTheme.colorScheme.outline.copy(alpha = 0.7f)
|
||||
)
|
||||
},
|
||||
isError = errorMessage.isNotEmpty(),
|
||||
singleLine = true,
|
||||
trailingIcon = {
|
||||
if (value.isNotEmpty()) {
|
||||
IconButton(onClick = {
|
||||
if (!readOnly) onValueChange("")
|
||||
}) {
|
||||
Icon(
|
||||
imageVector = Icons.Rounded.Close,
|
||||
contentDescription = stringResource(R.string.clear),
|
||||
tint = MaterialTheme.colorScheme.outline.copy(alpha = 0.5f),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
IconButton(onClick = {
|
||||
onValueChange(clipboardManager.getText()?.text ?: "")
|
||||
}) {
|
||||
Icon(
|
||||
imageVector = Icons.Rounded.ContentPaste,
|
||||
contentDescription = stringResource(R.string.paste),
|
||||
tint = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
keyboardOptions = keyboardOptions,
|
||||
keyboardActions = keyboardActions,
|
||||
)
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
package me.ash.reader.ui.component
|
||||
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import com.google.accompanist.pager.ExperimentalPagerApi
|
||||
import me.ash.reader.R
|
||||
|
||||
@OptIn(ExperimentalPagerApi::class)
|
||||
@Composable
|
||||
fun TextFieldDialog(
|
||||
modifier: Modifier = Modifier,
|
||||
properties: DialogProperties = DialogProperties(),
|
||||
visible: Boolean = false,
|
||||
readOnly: Boolean = false,
|
||||
title: String = "",
|
||||
icon: ImageVector? = null,
|
||||
value: String = "",
|
||||
placeholder: String = "",
|
||||
errorText: String = "",
|
||||
dismissText: String = stringResource(R.string.cancel),
|
||||
confirmText: String = stringResource(R.string.confirm),
|
||||
onValueChange: (String) -> Unit = {},
|
||||
onDismissRequest: () -> Unit = {},
|
||||
onConfirm: (String) -> Unit = {},
|
||||
imeAction: ImeAction = ImeAction.Done,
|
||||
) {
|
||||
val focusManager = LocalFocusManager.current
|
||||
|
||||
Dialog(
|
||||
modifier = modifier,
|
||||
visible = visible,
|
||||
onDismissRequest = onDismissRequest,
|
||||
icon = {
|
||||
icon?.let {
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
contentDescription = title,
|
||||
)
|
||||
}
|
||||
},
|
||||
title = {
|
||||
Text(text = title, maxLines = 1, overflow = TextOverflow.Ellipsis)
|
||||
},
|
||||
text = {
|
||||
ClipboardTextField(
|
||||
modifier = modifier,
|
||||
readOnly = readOnly,
|
||||
value = value,
|
||||
onValueChange = onValueChange,
|
||||
placeholder = placeholder,
|
||||
errorText = errorText,
|
||||
imeAction = imeAction,
|
||||
focusManager = focusManager,
|
||||
onConfirm = onConfirm,
|
||||
)
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(
|
||||
enabled = value.isNotBlank(),
|
||||
onClick = {
|
||||
focusManager.clearFocus()
|
||||
onConfirm(value)
|
||||
}
|
||||
) {
|
||||
Text(
|
||||
text = confirmText,
|
||||
color = if (value.isNotBlank()) {
|
||||
Color.Unspecified
|
||||
} else {
|
||||
MaterialTheme.colorScheme.outline.copy(alpha = 0.7f)
|
||||
}
|
||||
)
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = onDismissRequest) {
|
||||
Text(text = dismissText)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
|
@ -67,6 +67,10 @@ fun HomePage(
|
|||
|
||||
BackHandler(true) {
|
||||
val currentPage = viewState.pagerState.currentPage
|
||||
if (currentPage == 0) {
|
||||
context.findActivity()?.moveTaskToBack(false)
|
||||
return@BackHandler
|
||||
}
|
||||
homeViewModel.dispatch(
|
||||
HomeViewAction.ScrollToPage(
|
||||
scope = scope,
|
||||
|
|
|
@ -2,7 +2,7 @@ package me.ash.reader.ui.page.home.drawer.feed
|
|||
|
||||
import android.widget.Toast
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.DeleteOutline
|
||||
import androidx.compose.material.icons.outlined.DeleteForever
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
|
@ -36,7 +36,7 @@ fun DeleteFeedDialog(
|
|||
},
|
||||
icon = {
|
||||
Icon(
|
||||
imageVector = Icons.Rounded.DeleteOutline,
|
||||
imageVector = Icons.Outlined.DeleteForever,
|
||||
contentDescription = stringResource(R.string.subscribe),
|
||||
)
|
||||
},
|
||||
|
|
|
@ -1,16 +1,19 @@
|
|||
package me.ash.reader.ui.page.home.drawer.feed
|
||||
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.DeleteOutline
|
||||
import androidx.compose.material.icons.outlined.CreateNewFolder
|
||||
import androidx.compose.material.icons.rounded.RssFeed
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
|
@ -18,7 +21,7 @@ import androidx.compose.ui.unit.dp
|
|||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import me.ash.reader.R
|
||||
import me.ash.reader.ui.component.BottomDrawer
|
||||
import me.ash.reader.ui.component.Subtitle
|
||||
import me.ash.reader.ui.component.TextFieldDialog
|
||||
import me.ash.reader.ui.ext.collectAsStateValue
|
||||
import me.ash.reader.ui.ext.roundClick
|
||||
import me.ash.reader.ui.page.home.feeds.subscribe.ResultView
|
||||
|
@ -27,10 +30,10 @@ import me.ash.reader.ui.page.home.feeds.subscribe.ResultView
|
|||
@Composable
|
||||
fun FeedOptionDrawer(
|
||||
modifier: Modifier = Modifier,
|
||||
viewModel: FeedOptionViewModel = hiltViewModel(),
|
||||
feedOptionViewModel: FeedOptionViewModel = hiltViewModel(),
|
||||
content: @Composable () -> Unit = {},
|
||||
) {
|
||||
val viewState = viewModel.viewState.collectAsStateValue()
|
||||
val viewState = feedOptionViewModel.viewState.collectAsStateValue()
|
||||
val feed = viewState.feed
|
||||
|
||||
BottomDrawer(
|
||||
|
@ -65,53 +68,24 @@ fun FeedOptionDrawer(
|
|||
groups = viewState.groups,
|
||||
selectedAllowNotificationPreset = viewState.feed?.isNotification ?: false,
|
||||
selectedParseFullContentPreset = viewState.feed?.isFullContent ?: false,
|
||||
showUnsubscribe = true,
|
||||
selectedGroupId = viewState.feed?.groupId ?: "",
|
||||
newGroupContent = viewState.newGroupContent,
|
||||
onNewGroupValueChange = {
|
||||
viewModel.dispatch(FeedOptionViewAction.InputNewGroup(it))
|
||||
},
|
||||
newGroupSelected = viewState.newGroupSelected,
|
||||
changeNewGroupSelected = {
|
||||
viewModel.dispatch(FeedOptionViewAction.SelectedNewGroup(it))
|
||||
},
|
||||
allowNotificationPresetOnClick = {
|
||||
viewModel.dispatch(FeedOptionViewAction.ChangeAllowNotificationPreset)
|
||||
feedOptionViewModel.dispatch(FeedOptionViewAction.ChangeAllowNotificationPreset)
|
||||
},
|
||||
parseFullContentPresetOnClick = {
|
||||
viewModel.dispatch(FeedOptionViewAction.ChangeParseFullContentPreset)
|
||||
feedOptionViewModel.dispatch(FeedOptionViewAction.ChangeParseFullContentPreset)
|
||||
},
|
||||
unsubscribeOnClick = {
|
||||
feedOptionViewModel.dispatch(FeedOptionViewAction.ShowDeleteDialog)
|
||||
},
|
||||
onGroupClick = {
|
||||
viewModel.dispatch(FeedOptionViewAction.SelectedGroup(it))
|
||||
feedOptionViewModel.dispatch(FeedOptionViewAction.SelectedGroup(it))
|
||||
},
|
||||
onKeyboardAction = { },
|
||||
)
|
||||
Spacer(modifier = Modifier.height(20.dp))
|
||||
Subtitle(text = stringResource(R.string.options))
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
Button(
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
containerColor = Color.Transparent,
|
||||
contentColor = MaterialTheme.colorScheme.error,
|
||||
),
|
||||
border = BorderStroke(
|
||||
width = 1.dp,
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
),
|
||||
onClick = {
|
||||
viewModel.dispatch(FeedOptionViewAction.ShowDeleteDialog)
|
||||
onAddNewGroup = {
|
||||
feedOptionViewModel.dispatch(FeedOptionViewAction.ShowNewGroupDialog)
|
||||
}
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.size(ButtonDefaults.IconSize),
|
||||
imageVector = Icons.Rounded.DeleteOutline,
|
||||
contentDescription = stringResource(R.string.delete),
|
||||
)
|
||||
Spacer(Modifier.size(ButtonDefaults.IconSpacing))
|
||||
Text(
|
||||
text = stringResource(R.string.unsubscribe),
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
) {
|
||||
|
@ -119,4 +93,21 @@ fun FeedOptionDrawer(
|
|||
}
|
||||
|
||||
DeleteFeedDialog(feedName = feed?.name ?: "")
|
||||
|
||||
TextFieldDialog(
|
||||
visible = viewState.newGroupDialogVisible,
|
||||
title = stringResource(R.string.create_new_group),
|
||||
icon = Icons.Outlined.CreateNewFolder,
|
||||
value = viewState.newGroupContent,
|
||||
placeholder = stringResource(R.string.name),
|
||||
onValueChange = {
|
||||
feedOptionViewModel.dispatch(FeedOptionViewAction.InputNewGroup(it))
|
||||
},
|
||||
onDismissRequest = {
|
||||
feedOptionViewModel.dispatch(FeedOptionViewAction.HideNewGroupDialog)
|
||||
},
|
||||
onConfirm = {
|
||||
feedOptionViewModel.dispatch(FeedOptionViewAction.AddNewGroup)
|
||||
}
|
||||
)
|
||||
}
|
|
@ -49,12 +49,14 @@ class FeedOptionViewModel @Inject constructor(
|
|||
is FeedOptionViewAction.Hide -> hide(action.scope)
|
||||
is FeedOptionViewAction.SelectedGroup -> selectedGroup(action.groupId)
|
||||
is FeedOptionViewAction.InputNewGroup -> inputNewGroup(action.content)
|
||||
is FeedOptionViewAction.SelectedNewGroup -> selectedNewGroup(action.selected)
|
||||
is FeedOptionViewAction.ChangeAllowNotificationPreset -> changeAllowNotificationPreset()
|
||||
is FeedOptionViewAction.ChangeParseFullContentPreset -> changeParseFullContentPreset()
|
||||
is FeedOptionViewAction.ShowDeleteDialog -> showDeleteDialog()
|
||||
is FeedOptionViewAction.HideDeleteDialog -> hideDeleteDialog()
|
||||
is FeedOptionViewAction.Delete -> delete(action.callback)
|
||||
is FeedOptionViewAction.AddNewGroup -> addNewGroup()
|
||||
is FeedOptionViewAction.ShowNewGroupDialog -> changeNewGroupDialogVisible(true)
|
||||
is FeedOptionViewAction.HideNewGroupDialog -> changeNewGroupDialogVisible(false)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -81,6 +83,15 @@ class FeedOptionViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun changeNewGroupDialogVisible(visible: Boolean) {
|
||||
_viewState.update {
|
||||
it.copy(
|
||||
newGroupDialogVisible = visible,
|
||||
newGroupContent = "",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun inputNewGroup(content: String) {
|
||||
_viewState.update {
|
||||
it.copy(
|
||||
|
@ -89,6 +100,15 @@ class FeedOptionViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun addNewGroup() {
|
||||
if (_viewState.value.newGroupContent.isNotBlank()) {
|
||||
viewModelScope.launch {
|
||||
selectedGroup(rssRepository.get().addGroup(_viewState.value.newGroupContent))
|
||||
changeNewGroupDialogVisible(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun selectedGroup(groupId: String) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
_viewState.value.feed?.let {
|
||||
|
@ -102,14 +122,6 @@ class FeedOptionViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun selectedNewGroup(selected: Boolean) {
|
||||
_viewState.update {
|
||||
it.copy(
|
||||
newGroupSelected = selected,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun changeParseFullContentPreset() {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
_viewState.value.feed?.let {
|
||||
|
@ -170,7 +182,7 @@ data class FeedOptionViewState(
|
|||
val feed: Feed? = null,
|
||||
val selectedGroupId: String = "",
|
||||
val newGroupContent: String = "",
|
||||
val newGroupSelected: Boolean = false,
|
||||
val newGroupDialogVisible: Boolean = false,
|
||||
val groups: List<Group> = emptyList(),
|
||||
val deleteDialogVisible: Boolean = false,
|
||||
)
|
||||
|
@ -196,14 +208,14 @@ sealed class FeedOptionViewAction {
|
|||
val content: String
|
||||
) : FeedOptionViewAction()
|
||||
|
||||
data class SelectedNewGroup(
|
||||
val selected: Boolean
|
||||
) : FeedOptionViewAction()
|
||||
|
||||
data class Delete(
|
||||
val callback: () -> Unit = {}
|
||||
) : FeedOptionViewAction()
|
||||
|
||||
object ShowDeleteDialog : FeedOptionViewAction()
|
||||
object HideDeleteDialog : FeedOptionViewAction()
|
||||
|
||||
object ShowNewGroupDialog : FeedOptionViewAction()
|
||||
object HideNewGroupDialog : FeedOptionViewAction()
|
||||
object AddNewGroup : FeedOptionViewAction()
|
||||
}
|
||||
|
|
|
@ -10,9 +10,9 @@ import androidx.compose.foundation.lazy.LazyColumn
|
|||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.KeyboardArrowRight
|
||||
import androidx.compose.material.icons.outlined.Settings
|
||||
import androidx.compose.material.icons.rounded.Add
|
||||
import androidx.compose.material.icons.rounded.Refresh
|
||||
import androidx.compose.material.icons.rounded.Tune
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
|
@ -106,7 +106,8 @@ fun FeedsPage(
|
|||
navigationIcon = {
|
||||
FeedbackIconButton(
|
||||
isHaptic = false,
|
||||
imageVector = Icons.Rounded.Tune,
|
||||
modifier = Modifier.size(20.dp),
|
||||
imageVector = Icons.Outlined.Settings,
|
||||
contentDescription = stringResource(R.string.settings),
|
||||
tint = MaterialTheme.colorScheme.onSurface,
|
||||
) {
|
||||
|
|
|
@ -3,18 +3,27 @@ package me.ash.reader.ui.page.home.feeds.subscribe
|
|||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import androidx.compose.animation.animateContentSize
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyRow
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Add
|
||||
import androidx.compose.material.icons.outlined.Article
|
||||
import androidx.compose.material.icons.outlined.Notifications
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
|
@ -24,7 +33,6 @@ import com.google.accompanist.flowlayout.MainAxisAlignment
|
|||
import me.ash.reader.R
|
||||
import me.ash.reader.data.entity.Group
|
||||
import me.ash.reader.ui.component.SelectionChip
|
||||
import me.ash.reader.ui.component.SelectionEditorChip
|
||||
import me.ash.reader.ui.component.Subtitle
|
||||
import me.ash.reader.ui.ext.roundClick
|
||||
|
||||
|
@ -35,41 +43,39 @@ fun ResultView(
|
|||
groups: List<Group> = emptyList(),
|
||||
selectedAllowNotificationPreset: Boolean = false,
|
||||
selectedParseFullContentPreset: Boolean = false,
|
||||
showUnsubscribe: Boolean = false,
|
||||
selectedGroupId: String = "",
|
||||
newGroupContent: String = "",
|
||||
newGroupSelected: Boolean,
|
||||
onNewGroupValueChange: (String) -> Unit = {},
|
||||
changeNewGroupSelected: (Boolean) -> Unit = {},
|
||||
allowNotificationPresetOnClick: () -> Unit = {},
|
||||
parseFullContentPresetOnClick: () -> Unit = {},
|
||||
unsubscribeOnClick: () -> Unit = {},
|
||||
onGroupClick: (groupId: String) -> Unit = {},
|
||||
onKeyboardAction: () -> Unit = {},
|
||||
onAddNewGroup: () -> Unit = {},
|
||||
) {
|
||||
LaunchedEffect(Unit) {
|
||||
if (groups.isNotEmpty()) onGroupClick(groups.first().id)
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = modifier.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
Link(
|
||||
text = link
|
||||
)
|
||||
Link(text = link)
|
||||
Spacer(modifier = Modifier.height(26.dp))
|
||||
|
||||
Preset(
|
||||
selectedAllowNotificationPreset = selectedAllowNotificationPreset,
|
||||
selectedParseFullContentPreset = selectedParseFullContentPreset,
|
||||
showUnsubscribe = showUnsubscribe,
|
||||
allowNotificationPresetOnClick = allowNotificationPresetOnClick,
|
||||
parseFullContentPresetOnClick = parseFullContentPresetOnClick,
|
||||
unsubscribeOnClick = unsubscribeOnClick,
|
||||
)
|
||||
Spacer(modifier = Modifier.height(26.dp))
|
||||
|
||||
AddToGroup(
|
||||
groups = groups,
|
||||
selectedGroupId = selectedGroupId,
|
||||
newGroupContent = newGroupContent,
|
||||
newGroupSelected = newGroupSelected,
|
||||
onNewGroupValueChange = onNewGroupValueChange,
|
||||
changeNewGroupSelected = changeNewGroupSelected,
|
||||
onGroupClick = onGroupClick,
|
||||
onKeyboardAction = onKeyboardAction,
|
||||
onAddNewGroup = onAddNewGroup,
|
||||
)
|
||||
Spacer(modifier = Modifier.height(6.dp))
|
||||
}
|
||||
|
@ -105,8 +111,10 @@ private fun Link(
|
|||
private fun Preset(
|
||||
selectedAllowNotificationPreset: Boolean = false,
|
||||
selectedParseFullContentPreset: Boolean = false,
|
||||
showUnsubscribe: Boolean = false,
|
||||
allowNotificationPresetOnClick: () -> Unit = {},
|
||||
parseFullContentPresetOnClick: () -> Unit = {},
|
||||
unsubscribeOnClick: () -> Unit = {},
|
||||
) {
|
||||
Subtitle(text = stringResource(R.string.preset))
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
|
@ -147,6 +155,15 @@ private fun Preset(
|
|||
) {
|
||||
parseFullContentPresetOnClick()
|
||||
}
|
||||
if (showUnsubscribe) {
|
||||
SelectionChip(
|
||||
modifier = Modifier.animateContentSize(),
|
||||
content = stringResource(R.string.unsubscribe),
|
||||
selected = false,
|
||||
) {
|
||||
unsubscribeOnClick()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -154,15 +171,27 @@ private fun Preset(
|
|||
private fun AddToGroup(
|
||||
groups: List<Group>,
|
||||
selectedGroupId: String,
|
||||
newGroupContent: String,
|
||||
newGroupSelected: Boolean,
|
||||
onNewGroupValueChange: (String) -> Unit = {},
|
||||
changeNewGroupSelected: (Boolean) -> Unit = {},
|
||||
onGroupClick: (groupId: String) -> Unit = {},
|
||||
onKeyboardAction: () -> Unit = {},
|
||||
onAddNewGroup: () -> Unit = {},
|
||||
) {
|
||||
Subtitle(text = stringResource(R.string.add_to_group))
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
|
||||
if (groups.size > 6) {
|
||||
LazyRow {
|
||||
items(groups) {
|
||||
SelectionChip(
|
||||
modifier = Modifier.animateContentSize(),
|
||||
content = it.name,
|
||||
selected = it.id == selectedGroupId,
|
||||
) {
|
||||
onGroupClick(it.id)
|
||||
}
|
||||
Spacer(modifier = Modifier.width(10.dp))
|
||||
}
|
||||
item { NewGroupButton(onAddNewGroup) }
|
||||
}
|
||||
} else {
|
||||
FlowRow(
|
||||
mainAxisAlignment = MainAxisAlignment.Start,
|
||||
crossAxisSpacing = 10.dp,
|
||||
|
@ -172,21 +201,31 @@ private fun AddToGroup(
|
|||
SelectionChip(
|
||||
modifier = Modifier.animateContentSize(),
|
||||
content = it.name,
|
||||
selected = !newGroupSelected && it.id == selectedGroupId,
|
||||
selected = it.id == selectedGroupId,
|
||||
) {
|
||||
changeNewGroupSelected(false)
|
||||
onGroupClick(it.id)
|
||||
}
|
||||
}
|
||||
|
||||
SelectionEditorChip(
|
||||
modifier = Modifier.animateContentSize(),
|
||||
content = newGroupContent,
|
||||
onValueChange = onNewGroupValueChange,
|
||||
selected = newGroupSelected,
|
||||
onKeyboardAction = onKeyboardAction,
|
||||
) {
|
||||
changeNewGroupSelected(true)
|
||||
NewGroupButton(onAddNewGroup)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun NewGroupButton(onAddNewGroup: () -> Unit) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(36.dp)
|
||||
.clip(CircleShape)
|
||||
.background(MaterialTheme.colorScheme.surfaceVariant)
|
||||
.clickable { onAddNewGroup() },
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.size(20.dp),
|
||||
imageVector = Icons.Outlined.Add,
|
||||
contentDescription = stringResource(R.string.create_new_group),
|
||||
tint = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.7f),
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,125 +0,0 @@
|
|||
package me.ash.reader.ui.page.home.feeds.subscribe
|
||||
|
||||
import androidx.compose.foundation.horizontalScroll
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||
import androidx.compose.material.TextField
|
||||
import androidx.compose.material.TextFieldDefaults
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.Close
|
||||
import androidx.compose.material.icons.rounded.ContentPaste
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.remember
|
||||
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.platform.LocalClipboardManager
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.unit.dp
|
||||
import kotlinx.coroutines.delay
|
||||
import me.ash.reader.R
|
||||
|
||||
@Composable
|
||||
fun SearchView(
|
||||
modifier: Modifier = Modifier,
|
||||
readOnly: Boolean = false,
|
||||
inputLink: String = "",
|
||||
errorMessage: String = "",
|
||||
onLinkValueChange: (String) -> Unit = {},
|
||||
onKeyboardAction: () -> Unit = {},
|
||||
) {
|
||||
val focusManager = LocalFocusManager.current
|
||||
val clipboardManager = LocalClipboardManager.current
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
delay(100) // ???
|
||||
focusRequester.requestFocus()
|
||||
}
|
||||
|
||||
Column(modifier = modifier) {
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
TextField(
|
||||
modifier = Modifier.focusRequester(focusRequester),
|
||||
colors = TextFieldDefaults.textFieldColors(
|
||||
backgroundColor = Color.Transparent,
|
||||
cursorColor = MaterialTheme.colorScheme.onSurface,
|
||||
textColor = MaterialTheme.colorScheme.onSurface,
|
||||
focusedIndicatorColor = MaterialTheme.colorScheme.primary,
|
||||
),
|
||||
enabled = !readOnly,
|
||||
value = inputLink,
|
||||
onValueChange = {
|
||||
if (!readOnly) onLinkValueChange(it)
|
||||
},
|
||||
placeholder = {
|
||||
Text(
|
||||
text = stringResource(R.string.feed_or_site_url),
|
||||
color = MaterialTheme.colorScheme.outline.copy(alpha = 0.7f)
|
||||
)
|
||||
},
|
||||
isError = errorMessage.isNotEmpty(),
|
||||
singleLine = true,
|
||||
trailingIcon = {
|
||||
if (inputLink.isNotEmpty()) {
|
||||
IconButton(onClick = {
|
||||
if (!readOnly) onLinkValueChange("")
|
||||
}) {
|
||||
Icon(
|
||||
imageVector = Icons.Rounded.Close,
|
||||
contentDescription = stringResource(R.string.clear),
|
||||
tint = MaterialTheme.colorScheme.outline.copy(alpha = 0.5f),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
IconButton(onClick = {
|
||||
onLinkValueChange(clipboardManager.getText()?.text ?: "")
|
||||
}) {
|
||||
Icon(
|
||||
imageVector = Icons.Rounded.ContentPaste,
|
||||
contentDescription = stringResource(R.string.paste),
|
||||
tint = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
keyboardActions = KeyboardActions(
|
||||
onSearch = {
|
||||
focusManager.clearFocus()
|
||||
onKeyboardAction()
|
||||
}
|
||||
),
|
||||
keyboardOptions = KeyboardOptions(
|
||||
imeAction = ImeAction.Search
|
||||
),
|
||||
)
|
||||
if (errorMessage.isNotEmpty()) {
|
||||
SelectionContainer {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.padding(start = 16.dp)
|
||||
.horizontalScroll(rememberScrollState()),
|
||||
text = errorMessage,
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
maxLines = 1,
|
||||
softWrap = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ import androidx.activity.result.contract.ActivityResultContracts
|
|||
import androidx.compose.animation.*
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.CreateNewFolder
|
||||
import androidx.compose.material.icons.rounded.RssFeed
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
|
@ -18,12 +19,16 @@ import androidx.compose.ui.graphics.Color
|
|||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import me.ash.reader.R
|
||||
import me.ash.reader.ui.component.ClipboardTextField
|
||||
import me.ash.reader.ui.component.Dialog
|
||||
import me.ash.reader.ui.ext.*
|
||||
import me.ash.reader.ui.component.TextFieldDialog
|
||||
import me.ash.reader.ui.ext.collectAsStateValue
|
||||
|
||||
@OptIn(
|
||||
androidx.compose.ui.ExperimentalComposeUiApi::class,
|
||||
|
@ -45,15 +50,9 @@ fun SubscribeDialog(
|
|||
}
|
||||
}
|
||||
}
|
||||
val readYouString = stringResource(R.string.read_you)
|
||||
val defaultString = stringResource(R.string.defaults)
|
||||
|
||||
LaunchedEffect(viewState.visible) {
|
||||
if (viewState.visible) {
|
||||
val defaultGroupId = context.dataStore
|
||||
.get(DataStoreKeys.CurrentAccountId)!!
|
||||
.spacerDollar(readYouString + defaultString)
|
||||
subscribeViewModel.dispatch(SubscribeViewAction.SelectedGroup(defaultGroupId))
|
||||
subscribeViewModel.dispatch(SubscribeViewAction.Init)
|
||||
} else {
|
||||
subscribeViewModel.dispatch(SubscribeViewAction.Reset)
|
||||
|
@ -77,11 +76,13 @@ fun SubscribeDialog(
|
|||
},
|
||||
title = {
|
||||
Text(
|
||||
if (viewState.isSearchPage) {
|
||||
text = if (viewState.isSearchPage) {
|
||||
viewState.title
|
||||
} else {
|
||||
viewState.feed?.name ?: stringResource(R.string.unknown)
|
||||
}
|
||||
},
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
},
|
||||
text = {
|
||||
|
@ -93,14 +94,17 @@ fun SubscribeDialog(
|
|||
}
|
||||
) { targetExpanded ->
|
||||
if (targetExpanded) {
|
||||
SearchView(
|
||||
ClipboardTextField(
|
||||
readOnly = viewState.lockLinkInput,
|
||||
inputLink = viewState.linkContent,
|
||||
errorMessage = viewState.errorMessage,
|
||||
onLinkValueChange = {
|
||||
value = viewState.linkContent,
|
||||
onValueChange = {
|
||||
subscribeViewModel.dispatch(SubscribeViewAction.InputLink(it))
|
||||
},
|
||||
onKeyboardAction = {
|
||||
placeholder = stringResource(R.string.feed_or_site_url),
|
||||
errorText = viewState.errorMessage,
|
||||
imeAction = ImeAction.Search,
|
||||
focusManager = focusManager,
|
||||
onConfirm = {
|
||||
subscribeViewModel.dispatch(SubscribeViewAction.Search)
|
||||
},
|
||||
)
|
||||
|
@ -111,14 +115,6 @@ fun SubscribeDialog(
|
|||
selectedAllowNotificationPreset = viewState.allowNotificationPreset,
|
||||
selectedParseFullContentPreset = viewState.parseFullContentPreset,
|
||||
selectedGroupId = viewState.selectedGroupId,
|
||||
newGroupContent = viewState.newGroupContent,
|
||||
onNewGroupValueChange = {
|
||||
subscribeViewModel.dispatch(SubscribeViewAction.InputNewGroup(it))
|
||||
},
|
||||
newGroupSelected = viewState.newGroupSelected,
|
||||
changeNewGroupSelected = {
|
||||
subscribeViewModel.dispatch(SubscribeViewAction.SelectedNewGroup(it))
|
||||
},
|
||||
allowNotificationPresetOnClick = {
|
||||
subscribeViewModel.dispatch(SubscribeViewAction.ChangeAllowNotificationPreset)
|
||||
},
|
||||
|
@ -128,8 +124,8 @@ fun SubscribeDialog(
|
|||
onGroupClick = {
|
||||
subscribeViewModel.dispatch(SubscribeViewAction.SelectedGroup(it))
|
||||
},
|
||||
onKeyboardAction = {
|
||||
subscribeViewModel.dispatch(SubscribeViewAction.Subscribe)
|
||||
onAddNewGroup = {
|
||||
subscribeViewModel.dispatch(SubscribeViewAction.ShowNewGroupDialog)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -138,7 +134,7 @@ fun SubscribeDialog(
|
|||
confirmButton = {
|
||||
if (viewState.isSearchPage) {
|
||||
TextButton(
|
||||
enabled = viewState.linkContent.isNotEmpty()
|
||||
enabled = viewState.linkContent.isNotBlank()
|
||||
&& viewState.title != stringResource(R.string.searching),
|
||||
onClick = {
|
||||
focusManager.clearFocus()
|
||||
|
@ -147,7 +143,7 @@ fun SubscribeDialog(
|
|||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.search),
|
||||
color = if (viewState.linkContent.isNotEmpty()) {
|
||||
color = if (viewState.linkContent.isNotBlank()) {
|
||||
Color.Unspecified
|
||||
} else {
|
||||
MaterialTheme.colorScheme.outline.copy(alpha = 0.7f)
|
||||
|
@ -188,4 +184,21 @@ fun SubscribeDialog(
|
|||
}
|
||||
},
|
||||
)
|
||||
|
||||
TextFieldDialog(
|
||||
visible = viewState.newGroupDialogVisible,
|
||||
title = stringResource(R.string.create_new_group),
|
||||
icon = Icons.Outlined.CreateNewFolder,
|
||||
value = viewState.newGroupContent,
|
||||
placeholder = stringResource(R.string.name),
|
||||
onValueChange = {
|
||||
subscribeViewModel.dispatch(SubscribeViewAction.InputNewGroup(it))
|
||||
},
|
||||
onDismissRequest = {
|
||||
subscribeViewModel.dispatch(SubscribeViewAction.HideNewGroupDialog)
|
||||
},
|
||||
onConfirm = {
|
||||
subscribeViewModel.dispatch(SubscribeViewAction.AddNewGroup)
|
||||
}
|
||||
)
|
||||
}
|
|
@ -40,6 +40,8 @@ class SubscribeViewModel @Inject constructor(
|
|||
is SubscribeViewAction.Reset -> reset()
|
||||
is SubscribeViewAction.Show -> changeVisible(true)
|
||||
is SubscribeViewAction.Hide -> changeVisible(false)
|
||||
is SubscribeViewAction.ShowNewGroupDialog -> changeNewGroupDialogVisible(true)
|
||||
is SubscribeViewAction.HideNewGroupDialog -> changeNewGroupDialogVisible(false)
|
||||
is SubscribeViewAction.SwitchPage -> switchPage(action.isSearchPage)
|
||||
is SubscribeViewAction.ImportFromInputStream -> importFromInputStream(action.inputStream)
|
||||
is SubscribeViewAction.InputLink -> inputLink(action.content)
|
||||
|
@ -50,7 +52,7 @@ class SubscribeViewModel @Inject constructor(
|
|||
changeParseFullContentPreset()
|
||||
is SubscribeViewAction.SelectedGroup -> selectedGroup(action.groupId)
|
||||
is SubscribeViewAction.InputNewGroup -> inputNewGroup(action.content)
|
||||
is SubscribeViewAction.SelectedNewGroup -> selectedNewGroup(action.selected)
|
||||
is SubscribeViewAction.AddNewGroup -> addNewGroup()
|
||||
is SubscribeViewAction.Subscribe -> subscribe()
|
||||
}
|
||||
}
|
||||
|
@ -90,15 +92,8 @@ class SubscribeViewModel @Inject constructor(
|
|||
val articles = _viewState.value.articles
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
val groupId = async {
|
||||
if (
|
||||
_viewState.value.newGroupSelected &&
|
||||
_viewState.value.newGroupContent.isNotBlank()
|
||||
) {
|
||||
rssRepository.get().addGroup(_viewState.value.newGroupContent)
|
||||
} else {
|
||||
_viewState.value.selectedGroupId
|
||||
}
|
||||
}
|
||||
rssRepository.get().subscribe(
|
||||
feed.copy(
|
||||
groupId = groupId.await(),
|
||||
|
@ -118,13 +113,19 @@ class SubscribeViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun selectedNewGroup(selected: Boolean) {
|
||||
private fun addNewGroup() {
|
||||
if (_viewState.value.newGroupContent.isNotBlank()) {
|
||||
viewModelScope.launch {
|
||||
selectedGroup(rssRepository.get().addGroup(_viewState.value.newGroupContent))
|
||||
changeNewGroupDialogVisible(false)
|
||||
_viewState.update {
|
||||
it.copy(
|
||||
newGroupSelected = selected,
|
||||
newGroupContent = "",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun changeParseFullContentPreset() {
|
||||
_viewState.update {
|
||||
|
@ -224,6 +225,14 @@ class SubscribeViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun changeNewGroupDialogVisible(visible: Boolean) {
|
||||
_viewState.update {
|
||||
it.copy(
|
||||
newGroupDialogVisible = visible,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun switchPage(isSearchPage: Boolean) {
|
||||
_viewState.update {
|
||||
it.copy(
|
||||
|
@ -245,8 +254,8 @@ data class SubscribeViewState(
|
|||
val allowNotificationPreset: Boolean = false,
|
||||
val parseFullContentPreset: Boolean = false,
|
||||
val selectedGroupId: String = "",
|
||||
val newGroupDialogVisible: Boolean = false,
|
||||
val newGroupContent: String = "",
|
||||
val newGroupSelected: Boolean = false,
|
||||
val groups: Flow<List<Group>> = emptyFlow(),
|
||||
val isSearchPage: Boolean = true,
|
||||
)
|
||||
|
@ -258,6 +267,10 @@ sealed class SubscribeViewAction {
|
|||
object Show : SubscribeViewAction()
|
||||
object Hide : SubscribeViewAction()
|
||||
|
||||
object ShowNewGroupDialog : SubscribeViewAction()
|
||||
object HideNewGroupDialog : SubscribeViewAction()
|
||||
object AddNewGroup : SubscribeViewAction()
|
||||
|
||||
data class SwitchPage(
|
||||
val isSearchPage: Boolean
|
||||
) : SubscribeViewAction()
|
||||
|
@ -270,7 +283,7 @@ sealed class SubscribeViewAction {
|
|||
val content: String
|
||||
) : SubscribeViewAction()
|
||||
|
||||
object Search: SubscribeViewAction()
|
||||
object Search : SubscribeViewAction()
|
||||
|
||||
object ChangeAllowNotificationPreset : SubscribeViewAction()
|
||||
object ChangeParseFullContentPreset : SubscribeViewAction()
|
||||
|
@ -283,9 +296,5 @@ sealed class SubscribeViewAction {
|
|||
val content: String
|
||||
) : SubscribeViewAction()
|
||||
|
||||
data class SelectedNewGroup(
|
||||
val selected: Boolean
|
||||
) : SubscribeViewAction()
|
||||
|
||||
object Subscribe : SubscribeViewAction()
|
||||
}
|
||||
|
|
|
@ -31,13 +31,14 @@
|
|||
<string name="allow_notification">允许通知</string>
|
||||
<string name="parse_full_content">全文解析</string>
|
||||
<string name="add_to_group">添加到组</string>
|
||||
<string name="new_group">新建分组</string>
|
||||
<string name="create_new_group">新建分组</string>
|
||||
<string name="name">名称</string>
|
||||
<string name="open_with">打开 %1$s</string>
|
||||
<string name="options">选项</string>
|
||||
<string name="delete">删除</string>
|
||||
<string name="has_been_deleted">“%1$s” 已被删除</string>
|
||||
<string name="has_been_deleted">\"%1$s\" 已被删除</string>
|
||||
<string name="unsubscribe">取消订阅</string>
|
||||
<string name="unsubscribe_tip">不再订阅 “%1$s”,同时删除其所有已归档的文章。</string>
|
||||
<string name="unsubscribe_tip">不再订阅 \"%1$s\",同时删除其所有已归档的文章。</string>
|
||||
<string name="today">今天</string>
|
||||
<string name="yesterday">昨天</string>
|
||||
<string name="date_at_time">%1$s %2$s</string>
|
||||
|
|
|
@ -31,13 +31,14 @@
|
|||
<string name="allow_notification">Allow Notification</string>
|
||||
<string name="parse_full_content">Parse Full Content</string>
|
||||
<string name="add_to_group">Add to Group</string>
|
||||
<string name="new_group">New Group</string>
|
||||
<string name="create_new_group">Create New Group</string>
|
||||
<string name="name">Name</string>
|
||||
<string name="open_with">Open %1$s</string>
|
||||
<string name="options">Options</string>
|
||||
<string name="delete">Delete</string>
|
||||
<string name="has_been_deleted">"%1$s" has been deleted</string>
|
||||
<string name="has_been_deleted">\"%1$s\" has been deleted</string>
|
||||
<string name="unsubscribe">Unsubscribe</string>
|
||||
<string name="unsubscribe_tip">Unsubscribe "%1$s" and delete all its archived articles.</string>
|
||||
<string name="unsubscribe_tip">Unsubscribe \"%1$s\" and delete all its archived articles.</string>
|
||||
<string name="today">Today</string>
|
||||
<string name="yesterday">Yesterday</string>
|
||||
<string name="date_at_time">%1$s At %2$s</string>
|
||||
|
|
Loading…
Reference in New Issue
Block a user