Rename UiState
This commit is contained in:
parent
dcbb41f3ab
commit
efdff0e49c
|
@ -15,11 +15,10 @@ import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
|||
import me.ash.reader.data.model.Filter
|
||||
import me.ash.reader.data.preference.LocalDarkTheme
|
||||
import me.ash.reader.ui.ext.*
|
||||
import me.ash.reader.ui.page.home.HomeViewAction
|
||||
import me.ash.reader.ui.page.home.HomeViewModel
|
||||
import me.ash.reader.ui.page.home.feeds.FeedsPage
|
||||
import me.ash.reader.ui.page.home.flow.FlowPage
|
||||
import me.ash.reader.ui.page.home.read.ReadPage
|
||||
import me.ash.reader.ui.page.home.reading.ReadingPage
|
||||
import me.ash.reader.ui.page.settings.SettingsPage
|
||||
import me.ash.reader.ui.page.settings.color.ColorAndStylePage
|
||||
import me.ash.reader.ui.page.settings.color.DarkThemePage
|
||||
|
@ -37,7 +36,7 @@ fun HomeEntry(
|
|||
homeViewModel: HomeViewModel = hiltViewModel(),
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val filterState = homeViewModel.filterState.collectAsStateValue()
|
||||
val filterUiState = homeViewModel.filterUiState.collectAsStateValue()
|
||||
val navController = rememberAnimatedNavController()
|
||||
|
||||
val intent by rememberSaveable { mutableStateOf(context.findActivity()?.intent) }
|
||||
|
@ -57,16 +56,14 @@ fun HomeEntry(
|
|||
// Other initial pages
|
||||
}
|
||||
|
||||
homeViewModel.dispatch(
|
||||
HomeViewAction.ChangeFilter(
|
||||
filterState.copy(
|
||||
filter = when (context.initialFilter) {
|
||||
0 -> Filter.Starred
|
||||
1 -> Filter.Unread
|
||||
2 -> Filter.All
|
||||
else -> Filter.All
|
||||
}
|
||||
)
|
||||
homeViewModel.changeFilter(
|
||||
filterUiState.copy(
|
||||
filter = when (context.initialFilter) {
|
||||
0 -> Filter.Starred
|
||||
1 -> Filter.Unread
|
||||
2 -> Filter.All
|
||||
else -> Filter.All
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -114,7 +111,7 @@ fun HomeEntry(
|
|||
)
|
||||
}
|
||||
animatedComposable(route = "${RouteName.READING}/{articleId}") {
|
||||
ReadPage(navController = navController)
|
||||
ReadingPage(navController = navController)
|
||||
}
|
||||
|
||||
// Settings
|
||||
|
|
|
@ -7,8 +7,8 @@ import dagger.hilt.android.lifecycle.HiltViewModel
|
|||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.*
|
||||
import me.ash.reader.data.entity.Feed
|
||||
import me.ash.reader.data.model.Filter
|
||||
import me.ash.reader.data.entity.Group
|
||||
import me.ash.reader.data.model.Filter
|
||||
import me.ash.reader.data.module.ApplicationScope
|
||||
import me.ash.reader.data.repository.RssRepository
|
||||
import me.ash.reader.data.repository.StringsRepository
|
||||
|
@ -24,30 +24,20 @@ class HomeViewModel @Inject constructor(
|
|||
private val applicationScope: CoroutineScope,
|
||||
private val workManager: WorkManager,
|
||||
) : ViewModel() {
|
||||
private val _homeUiState = MutableStateFlow(HomeUiState())
|
||||
val homeUiState: StateFlow<HomeUiState> = _homeUiState.asStateFlow()
|
||||
|
||||
private val _viewState = MutableStateFlow(HomeViewState())
|
||||
val viewState: StateFlow<HomeViewState> = _viewState.asStateFlow()
|
||||
|
||||
private val _filterState = MutableStateFlow(FilterState())
|
||||
val filterState = _filterState.asStateFlow()
|
||||
private val _filterUiState = MutableStateFlow(FilterState())
|
||||
val filterUiState = _filterUiState.asStateFlow()
|
||||
|
||||
val syncWorkLiveData = workManager.getWorkInfoByIdLiveData(SyncWorker.UUID)
|
||||
|
||||
fun dispatch(action: HomeViewAction) {
|
||||
when (action) {
|
||||
is HomeViewAction.Sync -> sync()
|
||||
is HomeViewAction.ChangeFilter -> changeFilter(action.filterState)
|
||||
is HomeViewAction.FetchArticles -> fetchArticles()
|
||||
is HomeViewAction.InputSearchContent -> inputSearchContent(action.content)
|
||||
}
|
||||
}
|
||||
|
||||
private fun sync() {
|
||||
fun sync() {
|
||||
rssRepository.get().doSync()
|
||||
}
|
||||
|
||||
private fun changeFilter(filterState: FilterState) {
|
||||
_filterState.update {
|
||||
fun changeFilter(filterState: FilterState) {
|
||||
_filterUiState.update {
|
||||
it.copy(
|
||||
group = filterState.group,
|
||||
feed = filterState.feed,
|
||||
|
@ -57,24 +47,24 @@ class HomeViewModel @Inject constructor(
|
|||
fetchArticles()
|
||||
}
|
||||
|
||||
private fun fetchArticles() {
|
||||
_viewState.update {
|
||||
fun fetchArticles() {
|
||||
_homeUiState.update {
|
||||
it.copy(
|
||||
pagingData = Pager(PagingConfig(pageSize = 50)) {
|
||||
if (_viewState.value.searchContent.isNotBlank()) {
|
||||
if (_homeUiState.value.searchContent.isNotBlank()) {
|
||||
rssRepository.get().searchArticles(
|
||||
content = _viewState.value.searchContent.trim(),
|
||||
groupId = _filterState.value.group?.id,
|
||||
feedId = _filterState.value.feed?.id,
|
||||
isStarred = _filterState.value.filter.isStarred(),
|
||||
isUnread = _filterState.value.filter.isUnread(),
|
||||
content = _homeUiState.value.searchContent.trim(),
|
||||
groupId = _filterUiState.value.group?.id,
|
||||
feedId = _filterUiState.value.feed?.id,
|
||||
isStarred = _filterUiState.value.filter.isStarred(),
|
||||
isUnread = _filterUiState.value.filter.isUnread(),
|
||||
)
|
||||
} else {
|
||||
rssRepository.get().pullArticles(
|
||||
groupId = _filterState.value.group?.id,
|
||||
feedId = _filterState.value.feed?.id,
|
||||
isStarred = _filterState.value.filter.isStarred(),
|
||||
isUnread = _filterState.value.filter.isUnread(),
|
||||
groupId = _filterUiState.value.group?.id,
|
||||
feedId = _filterUiState.value.feed?.id,
|
||||
isStarred = _filterUiState.value.filter.isStarred(),
|
||||
isUnread = _filterUiState.value.filter.isUnread(),
|
||||
)
|
||||
}
|
||||
}.flow.map {
|
||||
|
@ -94,8 +84,8 @@ class HomeViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun inputSearchContent(content: String) {
|
||||
_viewState.update {
|
||||
fun inputSearchContent(content: String) {
|
||||
_homeUiState.update {
|
||||
it.copy(
|
||||
searchContent = content,
|
||||
)
|
||||
|
@ -110,21 +100,7 @@ data class FilterState(
|
|||
val filter: Filter = Filter.All,
|
||||
)
|
||||
|
||||
data class HomeViewState(
|
||||
data class HomeUiState(
|
||||
val pagingData: Flow<PagingData<FlowItemView>> = emptyFlow(),
|
||||
val searchContent: String = "",
|
||||
)
|
||||
|
||||
sealed class HomeViewAction {
|
||||
object Sync : HomeViewAction()
|
||||
|
||||
data class ChangeFilter(
|
||||
val filterState: FilterState
|
||||
) : HomeViewAction()
|
||||
|
||||
object FetchArticles : HomeViewAction()
|
||||
|
||||
data class InputSearchContent(
|
||||
val content: String,
|
||||
) : HomeViewAction()
|
||||
}
|
|
@ -21,7 +21,6 @@ import androidx.compose.ui.unit.dp
|
|||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import me.ash.reader.data.entity.Feed
|
||||
import me.ash.reader.ui.component.FeedIcon
|
||||
import me.ash.reader.ui.page.home.feeds.drawer.feed.FeedOptionViewAction
|
||||
import me.ash.reader.ui.page.home.feeds.drawer.feed.FeedOptionViewModel
|
||||
import kotlin.math.ln
|
||||
|
||||
|
@ -55,7 +54,7 @@ fun FeedItem(
|
|||
},
|
||||
onLongClick = {
|
||||
view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP)
|
||||
feedOptionViewModel.dispatch(FeedOptionViewAction.Show(scope, feed.id))
|
||||
feedOptionViewModel.showDrawer(scope, feed.id)
|
||||
}
|
||||
)
|
||||
.padding(vertical = 14.dp),
|
||||
|
|
|
@ -39,12 +39,10 @@ import me.ash.reader.ui.ext.findActivity
|
|||
import me.ash.reader.ui.ext.getCurrentVersion
|
||||
import me.ash.reader.ui.page.common.RouteName
|
||||
import me.ash.reader.ui.page.home.FilterState
|
||||
import me.ash.reader.ui.page.home.HomeViewAction
|
||||
import me.ash.reader.ui.page.home.HomeViewModel
|
||||
import me.ash.reader.ui.page.home.feeds.drawer.feed.FeedOptionDrawer
|
||||
import me.ash.reader.ui.page.home.feeds.drawer.group.GroupOptionDrawer
|
||||
import me.ash.reader.ui.page.home.feeds.subscribe.SubscribeDialog
|
||||
import me.ash.reader.ui.page.home.feeds.subscribe.SubscribeViewAction
|
||||
import me.ash.reader.ui.page.home.feeds.subscribe.SubscribeViewModel
|
||||
|
||||
@OptIn(
|
||||
|
@ -67,8 +65,8 @@ fun FeedsPage(
|
|||
val filterBarPadding = LocalFeedsFilterBarPadding.current
|
||||
val filterBarTonalElevation = LocalFeedsFilterBarTonalElevation.current
|
||||
|
||||
val feedsViewState = feedsViewModel.viewState.collectAsStateValue()
|
||||
val filterState = homeViewModel.filterState.collectAsStateValue()
|
||||
val feedsUiState = feedsViewModel.feedsUiState.collectAsStateValue()
|
||||
val filterUiState = homeViewModel.filterUiState.collectAsStateValue()
|
||||
|
||||
val newVersion = LocalNewVersionNumber.current
|
||||
val skipVersion = LocalSkipVersionNumber.current
|
||||
|
@ -92,22 +90,22 @@ fun FeedsPage(
|
|||
val launcher = rememberLauncherForActivityResult(
|
||||
ActivityResultContracts.CreateDocument()
|
||||
) { result ->
|
||||
feedsViewModel.dispatch(FeedsViewAction.ExportAsString { string ->
|
||||
feedsViewModel.exportAsOpml { string ->
|
||||
result?.let { uri ->
|
||||
context.contentResolver.openOutputStream(uri)?.let { outputStream ->
|
||||
outputStream.write(string.toByteArray())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
feedsViewModel.dispatch(FeedsViewAction.FetchAccount)
|
||||
feedsViewModel.fetchAccount()
|
||||
}
|
||||
|
||||
LaunchedEffect(filterState) {
|
||||
snapshotFlow { filterState }.collect {
|
||||
feedsViewModel.dispatch(FeedsViewAction.FetchData(it))
|
||||
LaunchedEffect(filterUiState) {
|
||||
snapshotFlow { filterUiState }.collect {
|
||||
feedsViewModel.fetchData(it)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -138,14 +136,14 @@ fun FeedsPage(
|
|||
contentDescription = stringResource(R.string.refresh),
|
||||
tint = MaterialTheme.colorScheme.onSurface,
|
||||
) {
|
||||
if (!isSyncing) homeViewModel.dispatch(HomeViewAction.Sync)
|
||||
if (!isSyncing) homeViewModel.sync()
|
||||
}
|
||||
FeedbackIconButton(
|
||||
imageVector = Icons.Rounded.Add,
|
||||
contentDescription = stringResource(R.string.subscribe),
|
||||
tint = MaterialTheme.colorScheme.onSurface,
|
||||
) {
|
||||
subscribeViewModel.dispatch(SubscribeViewAction.Show)
|
||||
subscribeViewModel.showDrawer()
|
||||
}
|
||||
},
|
||||
content = {
|
||||
|
@ -159,15 +157,15 @@ fun FeedsPage(
|
|||
}
|
||||
)
|
||||
},
|
||||
text = feedsViewState.account?.name ?: stringResource(R.string.read_you),
|
||||
text = feedsUiState.account?.name ?: stringResource(R.string.read_you),
|
||||
desc = if (isSyncing) stringResource(R.string.syncing) else "",
|
||||
)
|
||||
}
|
||||
item {
|
||||
Banner(
|
||||
title = filterState.filter.getName(),
|
||||
desc = feedsViewState.importantCount.ifEmpty { stringResource(R.string.loading) },
|
||||
icon = filterState.filter.iconOutline,
|
||||
title = filterUiState.filter.getName(),
|
||||
desc = feedsUiState.importantCount.ifEmpty { stringResource(R.string.loading) },
|
||||
icon = filterUiState.filter.iconOutline,
|
||||
action = {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.KeyboardArrowRight,
|
||||
|
@ -178,7 +176,7 @@ fun FeedsPage(
|
|||
filterChange(
|
||||
navController = navController,
|
||||
homeViewModel = homeViewModel,
|
||||
filterState = filterState.copy(
|
||||
filterState = filterUiState.copy(
|
||||
group = null,
|
||||
feed = null,
|
||||
)
|
||||
|
@ -193,7 +191,7 @@ fun FeedsPage(
|
|||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
}
|
||||
itemsIndexed(feedsViewState.groupWithFeedList) { index, groupWithFeed ->
|
||||
itemsIndexed(feedsUiState.groupWithFeedList) { index, groupWithFeed ->
|
||||
// Crossfade(targetState = groupWithFeed) { groupWithFeed ->
|
||||
Column {
|
||||
GroupItem(
|
||||
|
@ -205,7 +203,7 @@ fun FeedsPage(
|
|||
filterChange(
|
||||
navController = navController,
|
||||
homeViewModel = homeViewModel,
|
||||
filterState = filterState.copy(
|
||||
filterState = filterUiState.copy(
|
||||
group = groupWithFeed.group,
|
||||
feed = null,
|
||||
)
|
||||
|
@ -215,14 +213,14 @@ fun FeedsPage(
|
|||
filterChange(
|
||||
navController = navController,
|
||||
homeViewModel = homeViewModel,
|
||||
filterState = filterState.copy(
|
||||
filterState = filterUiState.copy(
|
||||
group = null,
|
||||
feed = feed,
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
if (index != feedsViewState.groupWithFeedList.lastIndex) {
|
||||
if (index != feedsUiState.groupWithFeedList.lastIndex) {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
}
|
||||
}
|
||||
|
@ -236,7 +234,7 @@ fun FeedsPage(
|
|||
},
|
||||
bottomBar = {
|
||||
FilterBar(
|
||||
filter = filterState.filter,
|
||||
filter = filterUiState.filter,
|
||||
filterBarStyle = filterBarStyle.value,
|
||||
filterBarFilled = filterBarFilled.value,
|
||||
filterBarPadding = filterBarPadding.dp,
|
||||
|
@ -245,7 +243,7 @@ fun FeedsPage(
|
|||
filterChange(
|
||||
navController = navController,
|
||||
homeViewModel = homeViewModel,
|
||||
filterState = filterState.copy(filter = it),
|
||||
filterState = filterUiState.copy(filter = it),
|
||||
isNavigate = false,
|
||||
)
|
||||
}
|
||||
|
@ -263,7 +261,7 @@ private fun filterChange(
|
|||
filterState: FilterState,
|
||||
isNavigate: Boolean = true,
|
||||
) {
|
||||
homeViewModel.dispatch(HomeViewAction.ChangeFilter(filterState))
|
||||
homeViewModel.changeFilter(filterState)
|
||||
if (isNavigate) {
|
||||
navController.navigate(RouteName.FLOW) {
|
||||
launchSingleTop = true
|
||||
|
|
|
@ -31,21 +31,12 @@ class FeedsViewModel @Inject constructor(
|
|||
@DispatcherIO
|
||||
private val dispatcherIO: CoroutineDispatcher,
|
||||
) : ViewModel() {
|
||||
private val _viewState = MutableStateFlow(FeedsViewState())
|
||||
val viewState: StateFlow<FeedsViewState> = _viewState.asStateFlow()
|
||||
private val _feedsUiState = MutableStateFlow(FeedsUiState())
|
||||
val feedsUiState: StateFlow<FeedsUiState> = _feedsUiState.asStateFlow()
|
||||
|
||||
fun dispatch(action: FeedsViewAction) {
|
||||
when (action) {
|
||||
is FeedsViewAction.FetchAccount -> fetchAccount()
|
||||
is FeedsViewAction.FetchData -> fetchData(action.filterState)
|
||||
is FeedsViewAction.ExportAsString -> exportAsOpml(action.callback)
|
||||
is FeedsViewAction.ScrollToItem -> scrollToItem(action.index)
|
||||
}
|
||||
}
|
||||
|
||||
private fun fetchAccount() {
|
||||
fun fetchAccount() {
|
||||
viewModelScope.launch(dispatcherIO) {
|
||||
_viewState.update {
|
||||
_feedsUiState.update {
|
||||
it.copy(
|
||||
account = accountRepository.getCurrentAccount()
|
||||
)
|
||||
|
@ -53,7 +44,7 @@ class FeedsViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun exportAsOpml(callback: (String) -> Unit = {}) {
|
||||
fun exportAsOpml(callback: (String) -> Unit = {}) {
|
||||
viewModelScope.launch(dispatcherDefault) {
|
||||
try {
|
||||
callback(opmlRepository.saveToString())
|
||||
|
@ -63,7 +54,7 @@ class FeedsViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun fetchData(filterState: FilterState) {
|
||||
fun fetchData(filterState: FilterState) {
|
||||
viewModelScope.launch(dispatcherIO) {
|
||||
pullFeeds(
|
||||
isStarred = filterState.filter.isStarred(),
|
||||
|
@ -109,13 +100,25 @@ class FeedsViewModel @Inject constructor(
|
|||
}
|
||||
groupWithFeedList
|
||||
}.onEach { groupWithFeedList ->
|
||||
_viewState.update {
|
||||
_feedsUiState.update {
|
||||
it.copy(
|
||||
importantCount = groupWithFeedList.sumOf { it.group.important ?: 0 }.run {
|
||||
when {
|
||||
isStarred -> stringsRepository.getQuantityString(R.plurals.starred_desc, this, this)
|
||||
isUnread -> stringsRepository.getQuantityString(R.plurals.unread_desc, this, this)
|
||||
else -> stringsRepository.getQuantityString(R.plurals.all_desc, this, this)
|
||||
isStarred -> stringsRepository.getQuantityString(
|
||||
R.plurals.starred_desc,
|
||||
this,
|
||||
this
|
||||
)
|
||||
isUnread -> stringsRepository.getQuantityString(
|
||||
R.plurals.unread_desc,
|
||||
this,
|
||||
this
|
||||
)
|
||||
else -> stringsRepository.getQuantityString(
|
||||
R.plurals.all_desc,
|
||||
this,
|
||||
this
|
||||
)
|
||||
}
|
||||
},
|
||||
groupWithFeedList = groupWithFeedList,
|
||||
|
@ -126,15 +129,9 @@ class FeedsViewModel @Inject constructor(
|
|||
Log.e("RLog", "catch in articleRepository.pullFeeds(): ${it.message}")
|
||||
}.flowOn(dispatcherDefault).collect()
|
||||
}
|
||||
|
||||
private fun scrollToItem(index: Int) {
|
||||
viewModelScope.launch {
|
||||
_viewState.value.listState.scrollToItem(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class FeedsViewState(
|
||||
data class FeedsUiState(
|
||||
val account: Account? = null,
|
||||
val importantCount: String = "",
|
||||
val groupWithFeedList: List<GroupWithFeed> = emptyList(),
|
||||
|
@ -142,19 +139,3 @@ data class FeedsViewState(
|
|||
val listState: LazyListState = LazyListState(),
|
||||
val groupsVisible: Boolean = true,
|
||||
)
|
||||
|
||||
sealed class FeedsViewAction {
|
||||
data class FetchData(
|
||||
val filterState: FilterState,
|
||||
) : FeedsViewAction()
|
||||
|
||||
object FetchAccount : FeedsViewAction()
|
||||
|
||||
data class ExportAsString(
|
||||
val callback: (String) -> Unit = {}
|
||||
) : FeedsViewAction()
|
||||
|
||||
data class ScrollToItem(
|
||||
val index: Int
|
||||
) : FeedsViewAction()
|
||||
}
|
|
@ -28,7 +28,6 @@ import me.ash.reader.R
|
|||
import me.ash.reader.data.entity.Feed
|
||||
import me.ash.reader.data.entity.Group
|
||||
import me.ash.reader.ui.ext.alphaLN
|
||||
import me.ash.reader.ui.page.home.feeds.drawer.group.GroupOptionViewAction
|
||||
import me.ash.reader.ui.page.home.feeds.drawer.group.GroupOptionViewModel
|
||||
|
||||
@OptIn(androidx.compose.foundation.ExperimentalFoundationApi::class)
|
||||
|
@ -61,7 +60,7 @@ fun GroupItem(
|
|||
},
|
||||
onLongClick = {
|
||||
view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP)
|
||||
groupOptionViewModel.dispatch(GroupOptionViewAction.Show(scope, group.id))
|
||||
groupOptionViewModel.showDrawer(scope, group.id)
|
||||
}
|
||||
)
|
||||
.padding(top = 22.dp)
|
||||
|
|
|
@ -7,7 +7,6 @@ import androidx.compose.material3.Text
|
|||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
|
@ -18,19 +17,18 @@ import me.ash.reader.ui.ext.showToast
|
|||
|
||||
@Composable
|
||||
fun ClearFeedDialog(
|
||||
modifier: Modifier = Modifier,
|
||||
feedName: String,
|
||||
viewModel: FeedOptionViewModel = hiltViewModel(),
|
||||
feedOptionViewModel: FeedOptionViewModel = hiltViewModel(),
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val viewState = viewModel.viewState.collectAsStateValue()
|
||||
val feedOptionUiState = feedOptionViewModel.feedOptionUiState.collectAsStateValue()
|
||||
val scope = rememberCoroutineScope()
|
||||
val toastString = stringResource(R.string.clear_articles_in_feed_toast, feedName)
|
||||
|
||||
Dialog(
|
||||
visible = viewState.clearDialogVisible,
|
||||
visible = feedOptionUiState.clearDialogVisible,
|
||||
onDismissRequest = {
|
||||
viewModel.dispatch(FeedOptionViewAction.HideClearDialog)
|
||||
feedOptionViewModel.hideClearDialog()
|
||||
},
|
||||
icon = {
|
||||
Icon(
|
||||
|
@ -47,11 +45,11 @@ fun ClearFeedDialog(
|
|||
confirmButton = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
viewModel.dispatch(FeedOptionViewAction.Clear {
|
||||
viewModel.dispatch(FeedOptionViewAction.HideClearDialog)
|
||||
viewModel.dispatch(FeedOptionViewAction.Hide(scope))
|
||||
feedOptionViewModel.clearFeed {
|
||||
feedOptionViewModel.hideClearDialog()
|
||||
feedOptionViewModel.hideDrawer(scope)
|
||||
context.showToast(toastString)
|
||||
})
|
||||
}
|
||||
}
|
||||
) {
|
||||
Text(
|
||||
|
@ -62,7 +60,7 @@ fun ClearFeedDialog(
|
|||
dismissButton = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
viewModel.dispatch(FeedOptionViewAction.HideClearDialog)
|
||||
feedOptionViewModel.hideClearDialog()
|
||||
}
|
||||
) {
|
||||
Text(
|
||||
|
|
|
@ -7,7 +7,6 @@ import androidx.compose.material3.Text
|
|||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
|
@ -18,19 +17,18 @@ import me.ash.reader.ui.ext.showToast
|
|||
|
||||
@Composable
|
||||
fun DeleteFeedDialog(
|
||||
modifier: Modifier = Modifier,
|
||||
feedName: String,
|
||||
viewModel: FeedOptionViewModel = hiltViewModel(),
|
||||
feedOptionViewModel: FeedOptionViewModel = hiltViewModel(),
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val viewState = viewModel.viewState.collectAsStateValue()
|
||||
val feedOptionUiState = feedOptionViewModel.feedOptionUiState.collectAsStateValue()
|
||||
val scope = rememberCoroutineScope()
|
||||
val toastString = stringResource(R.string.delete_toast, feedName)
|
||||
|
||||
Dialog(
|
||||
visible = viewState.deleteDialogVisible,
|
||||
visible = feedOptionUiState.deleteDialogVisible,
|
||||
onDismissRequest = {
|
||||
viewModel.dispatch(FeedOptionViewAction.HideDeleteDialog)
|
||||
feedOptionViewModel.hideDeleteDialog()
|
||||
},
|
||||
icon = {
|
||||
Icon(
|
||||
|
@ -47,11 +45,11 @@ fun DeleteFeedDialog(
|
|||
confirmButton = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
viewModel.dispatch(FeedOptionViewAction.Delete {
|
||||
viewModel.dispatch(FeedOptionViewAction.HideDeleteDialog)
|
||||
viewModel.dispatch(FeedOptionViewAction.Hide(scope))
|
||||
feedOptionViewModel.delete {
|
||||
feedOptionViewModel.hideDeleteDialog()
|
||||
feedOptionViewModel.hideDrawer(scope)
|
||||
context.showToast(toastString)
|
||||
})
|
||||
}
|
||||
}
|
||||
) {
|
||||
Text(
|
||||
|
@ -62,7 +60,7 @@ fun DeleteFeedDialog(
|
|||
dismissButton = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
viewModel.dispatch(FeedOptionViewAction.HideDeleteDialog)
|
||||
feedOptionViewModel.hideDeleteDialog()
|
||||
}
|
||||
) {
|
||||
Text(
|
||||
|
|
|
@ -19,35 +19,34 @@ import androidx.compose.ui.unit.dp
|
|||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import kotlinx.coroutines.launch
|
||||
import me.ash.reader.R
|
||||
import me.ash.reader.ui.component.FeedIcon
|
||||
import me.ash.reader.ui.component.base.BottomDrawer
|
||||
import me.ash.reader.ui.component.base.TextFieldDialog
|
||||
import me.ash.reader.ui.ext.collectAsStateValue
|
||||
import me.ash.reader.ui.ext.roundClick
|
||||
import me.ash.reader.ui.ext.showToast
|
||||
import me.ash.reader.ui.component.FeedIcon
|
||||
import me.ash.reader.ui.page.home.feeds.subscribe.ResultView
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
fun FeedOptionDrawer(
|
||||
modifier: Modifier = Modifier,
|
||||
feedOptionViewModel: FeedOptionViewModel = hiltViewModel(),
|
||||
content: @Composable () -> Unit = {},
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val scope = rememberCoroutineScope()
|
||||
val viewState = feedOptionViewModel.viewState.collectAsStateValue()
|
||||
val feed = viewState.feed
|
||||
val toastString = stringResource(R.string.rename_toast, viewState.newName)
|
||||
val feedOptionUiState = feedOptionViewModel.feedOptionUiState.collectAsStateValue()
|
||||
val feed = feedOptionUiState.feed
|
||||
val toastString = stringResource(R.string.rename_toast, feedOptionUiState.newName)
|
||||
|
||||
BackHandler(viewState.drawerState.isVisible) {
|
||||
BackHandler(feedOptionUiState.drawerState.isVisible) {
|
||||
scope.launch {
|
||||
viewState.drawerState.hide()
|
||||
feedOptionUiState.drawerState.hide()
|
||||
}
|
||||
}
|
||||
|
||||
BottomDrawer(
|
||||
drawerState = viewState.drawerState,
|
||||
drawerState = feedOptionUiState.drawerState,
|
||||
sheetContent = {
|
||||
Column(modifier = Modifier.navigationBarsPadding()) {
|
||||
Column(
|
||||
|
@ -65,7 +64,7 @@ fun FeedOptionDrawer(
|
|||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Text(
|
||||
modifier = Modifier.roundClick {
|
||||
feedOptionViewModel.dispatch(FeedOptionViewAction.ShowRenameDialog)
|
||||
feedOptionViewModel.showRenameDialog()
|
||||
},
|
||||
text = feed?.name ?: stringResource(R.string.unknown),
|
||||
style = MaterialTheme.typography.headlineSmall,
|
||||
|
@ -77,32 +76,32 @@ fun FeedOptionDrawer(
|
|||
Spacer(modifier = Modifier.height(16.dp))
|
||||
ResultView(
|
||||
link = feed?.url ?: stringResource(R.string.unknown),
|
||||
groups = viewState.groups,
|
||||
selectedAllowNotificationPreset = viewState.feed?.isNotification ?: false,
|
||||
selectedParseFullContentPreset = viewState.feed?.isFullContent ?: false,
|
||||
groups = feedOptionUiState.groups,
|
||||
selectedAllowNotificationPreset = feedOptionUiState.feed?.isNotification ?: false,
|
||||
selectedParseFullContentPreset = feedOptionUiState.feed?.isFullContent ?: false,
|
||||
isMoveToGroup = true,
|
||||
showUnsubscribe = true,
|
||||
selectedGroupId = viewState.feed?.groupId ?: "",
|
||||
selectedGroupId = feedOptionUiState.feed?.groupId ?: "",
|
||||
allowNotificationPresetOnClick = {
|
||||
feedOptionViewModel.dispatch(FeedOptionViewAction.ChangeAllowNotificationPreset)
|
||||
feedOptionViewModel.changeAllowNotificationPreset()
|
||||
},
|
||||
parseFullContentPresetOnClick = {
|
||||
feedOptionViewModel.dispatch(FeedOptionViewAction.ChangeParseFullContentPreset)
|
||||
feedOptionViewModel.changeParseFullContentPreset()
|
||||
},
|
||||
clearArticlesOnClick = {
|
||||
feedOptionViewModel.dispatch(FeedOptionViewAction.ShowClearDialog)
|
||||
feedOptionViewModel.showClearDialog()
|
||||
},
|
||||
unsubscribeOnClick = {
|
||||
feedOptionViewModel.dispatch(FeedOptionViewAction.ShowDeleteDialog)
|
||||
feedOptionViewModel.showDeleteDialog()
|
||||
},
|
||||
onGroupClick = {
|
||||
feedOptionViewModel.dispatch(FeedOptionViewAction.SelectedGroup(it))
|
||||
feedOptionViewModel.selectedGroup(it)
|
||||
},
|
||||
onAddNewGroup = {
|
||||
feedOptionViewModel.dispatch(FeedOptionViewAction.ShowNewGroupDialog)
|
||||
feedOptionViewModel.showNewGroupDialog()
|
||||
},
|
||||
onFeedUrlClick = {
|
||||
feedOptionViewModel.dispatch(FeedOptionViewAction.ShowChangeUrlDialog)
|
||||
feedOptionViewModel.showFeedUrlDialog()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -116,56 +115,56 @@ fun FeedOptionDrawer(
|
|||
ClearFeedDialog(feedName = feed?.name ?: "")
|
||||
|
||||
TextFieldDialog(
|
||||
visible = viewState.newGroupDialogVisible,
|
||||
visible = feedOptionUiState.newGroupDialogVisible,
|
||||
title = stringResource(R.string.create_new_group),
|
||||
icon = Icons.Outlined.CreateNewFolder,
|
||||
value = viewState.newGroupContent,
|
||||
value = feedOptionUiState.newGroupContent,
|
||||
placeholder = stringResource(R.string.name),
|
||||
onValueChange = {
|
||||
feedOptionViewModel.dispatch(FeedOptionViewAction.InputNewGroup(it))
|
||||
feedOptionViewModel.inputNewGroup(it)
|
||||
},
|
||||
onDismissRequest = {
|
||||
feedOptionViewModel.dispatch(FeedOptionViewAction.HideNewGroupDialog)
|
||||
feedOptionViewModel.hideNewGroupDialog()
|
||||
},
|
||||
onConfirm = {
|
||||
feedOptionViewModel.dispatch(FeedOptionViewAction.AddNewGroup)
|
||||
feedOptionViewModel.addNewGroup()
|
||||
}
|
||||
)
|
||||
|
||||
TextFieldDialog(
|
||||
visible = viewState.renameDialogVisible,
|
||||
visible = feedOptionUiState.renameDialogVisible,
|
||||
title = stringResource(R.string.rename),
|
||||
icon = Icons.Outlined.Edit,
|
||||
value = viewState.newName,
|
||||
value = feedOptionUiState.newName,
|
||||
placeholder = stringResource(R.string.name),
|
||||
onValueChange = {
|
||||
feedOptionViewModel.dispatch(FeedOptionViewAction.InputNewName(it))
|
||||
feedOptionViewModel.inputNewName(it)
|
||||
},
|
||||
onDismissRequest = {
|
||||
feedOptionViewModel.dispatch(FeedOptionViewAction.HideRenameDialog)
|
||||
feedOptionViewModel.hideRenameDialog()
|
||||
},
|
||||
onConfirm = {
|
||||
feedOptionViewModel.dispatch(FeedOptionViewAction.Rename)
|
||||
feedOptionViewModel.dispatch(FeedOptionViewAction.Hide(scope))
|
||||
feedOptionViewModel.renameFeed()
|
||||
feedOptionViewModel.hideDrawer(scope)
|
||||
context.showToast(toastString)
|
||||
}
|
||||
)
|
||||
|
||||
TextFieldDialog(
|
||||
visible = viewState.changeUrlDialogVisible,
|
||||
visible = feedOptionUiState.changeUrlDialogVisible,
|
||||
title = stringResource(R.string.change_url),
|
||||
icon = Icons.Outlined.Edit,
|
||||
value = viewState.newUrl,
|
||||
value = feedOptionUiState.newUrl,
|
||||
placeholder = stringResource(R.string.feed_url_placeholder),
|
||||
onValueChange = {
|
||||
feedOptionViewModel.dispatch(FeedOptionViewAction.InputNewUrl(it))
|
||||
feedOptionViewModel.inputNewUrl(it)
|
||||
},
|
||||
onDismissRequest = {
|
||||
feedOptionViewModel.dispatch(FeedOptionViewAction.HideChangeUrlDialog)
|
||||
feedOptionViewModel.hideFeedUrlDialog()
|
||||
},
|
||||
onConfirm = {
|
||||
feedOptionViewModel.dispatch(FeedOptionViewAction.ChangeUrl)
|
||||
feedOptionViewModel.dispatch(FeedOptionViewAction.Hide(scope))
|
||||
feedOptionViewModel.changeFeedUrl()
|
||||
feedOptionViewModel.hideDrawer(scope)
|
||||
}
|
||||
)
|
||||
}
|
|
@ -21,9 +21,7 @@ import me.ash.reader.data.module.DispatcherMain
|
|||
import me.ash.reader.data.repository.RssRepository
|
||||
import javax.inject.Inject
|
||||
|
||||
@OptIn(
|
||||
ExperimentalMaterialApi::class
|
||||
)
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@HiltViewModel
|
||||
class FeedOptionViewModel @Inject constructor(
|
||||
private val rssRepository: RssRepository,
|
||||
|
@ -32,13 +30,13 @@ class FeedOptionViewModel @Inject constructor(
|
|||
@DispatcherIO
|
||||
private val dispatcherIO: CoroutineDispatcher,
|
||||
) : ViewModel() {
|
||||
private val _viewState = MutableStateFlow(FeedOptionViewState())
|
||||
val viewState: StateFlow<FeedOptionViewState> = _viewState.asStateFlow()
|
||||
private val _feedOptionUiState = MutableStateFlow(FeedOptionUiState())
|
||||
val feedOptionUiState: StateFlow<FeedOptionUiState> = _feedOptionUiState.asStateFlow()
|
||||
|
||||
init {
|
||||
viewModelScope.launch(dispatcherIO) {
|
||||
rssRepository.get().pullGroups().collect { groups ->
|
||||
_viewState.update {
|
||||
_feedOptionUiState.update {
|
||||
it.copy(
|
||||
groups = groups
|
||||
)
|
||||
|
@ -47,37 +45,9 @@ class FeedOptionViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
fun dispatch(action: FeedOptionViewAction) {
|
||||
when (action) {
|
||||
is FeedOptionViewAction.Show -> show(action.scope, action.feedId)
|
||||
is FeedOptionViewAction.Hide -> hide(action.scope)
|
||||
is FeedOptionViewAction.SelectedGroup -> selectedGroup(action.groupId)
|
||||
is FeedOptionViewAction.InputNewGroup -> inputNewGroup(action.content)
|
||||
is FeedOptionViewAction.ChangeAllowNotificationPreset -> changeAllowNotificationPreset()
|
||||
is FeedOptionViewAction.ChangeParseFullContentPreset -> changeParseFullContentPreset()
|
||||
is FeedOptionViewAction.ShowDeleteDialog -> showDeleteDialog()
|
||||
is FeedOptionViewAction.HideDeleteDialog -> hideDeleteDialog()
|
||||
is FeedOptionViewAction.Delete -> delete(action.callback)
|
||||
is FeedOptionViewAction.ShowClearDialog -> showClearDialog()
|
||||
is FeedOptionViewAction.HideClearDialog -> hideClearDialog()
|
||||
is FeedOptionViewAction.Clear -> clear(action.callback)
|
||||
is FeedOptionViewAction.AddNewGroup -> addNewGroup()
|
||||
is FeedOptionViewAction.ShowNewGroupDialog -> changeNewGroupDialogVisible(true)
|
||||
is FeedOptionViewAction.HideNewGroupDialog -> changeNewGroupDialogVisible(false)
|
||||
is FeedOptionViewAction.InputNewName -> inputNewName(action.content)
|
||||
is FeedOptionViewAction.Rename -> rename()
|
||||
is FeedOptionViewAction.ShowRenameDialog -> changeRenameDialogVisible(true)
|
||||
is FeedOptionViewAction.HideRenameDialog -> changeRenameDialogVisible(false)
|
||||
is FeedOptionViewAction.InputNewUrl -> inputNewUrl(action.content)
|
||||
is FeedOptionViewAction.ChangeUrl -> changeFeedUrl()
|
||||
is FeedOptionViewAction.HideChangeUrlDialog -> changeFeedUrlDialogVisible(false)
|
||||
is FeedOptionViewAction.ShowChangeUrlDialog -> changeFeedUrlDialogVisible(true)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun fetchFeed(feedId: String) {
|
||||
val feed = rssRepository.get().findFeedById(feedId)
|
||||
_viewState.update {
|
||||
_feedOptionUiState.update {
|
||||
it.copy(
|
||||
feed = feed,
|
||||
selectedGroupId = feed?.groupId ?: "",
|
||||
|
@ -85,48 +55,57 @@ class FeedOptionViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun show(scope: CoroutineScope, feedId: String) {
|
||||
fun showDrawer(scope: CoroutineScope, feedId: String) {
|
||||
scope.launch {
|
||||
fetchFeed(feedId)
|
||||
_viewState.value.drawerState.show()
|
||||
_feedOptionUiState.value.drawerState.show()
|
||||
}
|
||||
}
|
||||
|
||||
private fun hide(scope: CoroutineScope) {
|
||||
fun hideDrawer(scope: CoroutineScope) {
|
||||
scope.launch {
|
||||
_viewState.value.drawerState.hide()
|
||||
_feedOptionUiState.value.drawerState.hide()
|
||||
}
|
||||
}
|
||||
|
||||
private fun changeNewGroupDialogVisible(visible: Boolean) {
|
||||
_viewState.update {
|
||||
fun showNewGroupDialog() {
|
||||
_feedOptionUiState.update {
|
||||
it.copy(
|
||||
newGroupDialogVisible = visible,
|
||||
newGroupDialogVisible = true,
|
||||
newGroupContent = "",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun inputNewGroup(content: String) {
|
||||
_viewState.update {
|
||||
fun hideNewGroupDialog() {
|
||||
_feedOptionUiState.update {
|
||||
it.copy(
|
||||
newGroupDialogVisible = false,
|
||||
newGroupContent = "",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun inputNewGroup(content: String) {
|
||||
_feedOptionUiState.update {
|
||||
it.copy(
|
||||
newGroupContent = content
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun addNewGroup() {
|
||||
if (_viewState.value.newGroupContent.isNotBlank()) {
|
||||
fun addNewGroup() {
|
||||
if (_feedOptionUiState.value.newGroupContent.isNotBlank()) {
|
||||
viewModelScope.launch {
|
||||
selectedGroup(rssRepository.get().addGroup(_viewState.value.newGroupContent))
|
||||
changeNewGroupDialogVisible(false)
|
||||
selectedGroup(rssRepository.get().addGroup(_feedOptionUiState.value.newGroupContent))
|
||||
hideNewGroupDialog()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun selectedGroup(groupId: String) {
|
||||
fun selectedGroup(groupId: String) {
|
||||
viewModelScope.launch(dispatcherIO) {
|
||||
_viewState.value.feed?.let {
|
||||
_feedOptionUiState.value.feed?.let {
|
||||
rssRepository.get().updateFeed(
|
||||
it.copy(
|
||||
groupId = groupId
|
||||
|
@ -137,9 +116,9 @@ class FeedOptionViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun changeParseFullContentPreset() {
|
||||
fun changeParseFullContentPreset() {
|
||||
viewModelScope.launch(dispatcherIO) {
|
||||
_viewState.value.feed?.let {
|
||||
_feedOptionUiState.value.feed?.let {
|
||||
rssRepository.get().updateFeed(
|
||||
it.copy(
|
||||
isFullContent = !it.isFullContent
|
||||
|
@ -150,9 +129,9 @@ class FeedOptionViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun changeAllowNotificationPreset() {
|
||||
fun changeAllowNotificationPreset() {
|
||||
viewModelScope.launch(dispatcherIO) {
|
||||
_viewState.value.feed?.let {
|
||||
_feedOptionUiState.value.feed?.let {
|
||||
rssRepository.get().updateFeed(
|
||||
it.copy(
|
||||
isNotification = !it.isNotification
|
||||
|
@ -163,8 +142,8 @@ class FeedOptionViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun delete(callback: () -> Unit = {}) {
|
||||
_viewState.value.feed?.let {
|
||||
fun delete(callback: () -> Unit = {}) {
|
||||
_feedOptionUiState.value.feed?.let {
|
||||
viewModelScope.launch(dispatcherIO) {
|
||||
rssRepository.get().deleteFeed(it)
|
||||
withContext(dispatcherMain) {
|
||||
|
@ -174,40 +153,40 @@ class FeedOptionViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun hideDeleteDialog() {
|
||||
_viewState.update {
|
||||
fun hideDeleteDialog() {
|
||||
_feedOptionUiState.update {
|
||||
it.copy(
|
||||
deleteDialogVisible = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun showDeleteDialog() {
|
||||
_viewState.update {
|
||||
fun showDeleteDialog() {
|
||||
_feedOptionUiState.update {
|
||||
it.copy(
|
||||
deleteDialogVisible = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun showClearDialog() {
|
||||
_viewState.update {
|
||||
fun showClearDialog() {
|
||||
_feedOptionUiState.update {
|
||||
it.copy(
|
||||
clearDialogVisible = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun hideClearDialog() {
|
||||
_viewState.update {
|
||||
fun hideClearDialog() {
|
||||
_feedOptionUiState.update {
|
||||
it.copy(
|
||||
clearDialogVisible = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun clear(callback: () -> Unit = {}) {
|
||||
_viewState.value.feed?.let {
|
||||
fun clearFeed(callback: () -> Unit = {}) {
|
||||
_feedOptionUiState.value.feed?.let {
|
||||
viewModelScope.launch(dispatcherIO) {
|
||||
rssRepository.get().deleteArticles(feed = it)
|
||||
withContext(dispatcherMain) {
|
||||
|
@ -217,15 +196,15 @@ class FeedOptionViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun rename() {
|
||||
_viewState.value.feed?.let {
|
||||
fun renameFeed() {
|
||||
_feedOptionUiState.value.feed?.let {
|
||||
viewModelScope.launch {
|
||||
rssRepository.get().updateFeed(
|
||||
it.copy(
|
||||
name = _viewState.value.newName
|
||||
name = _feedOptionUiState.value.newName
|
||||
)
|
||||
)
|
||||
_viewState.update {
|
||||
_feedOptionUiState.update {
|
||||
it.copy(
|
||||
renameDialogVisible = false,
|
||||
)
|
||||
|
@ -234,49 +213,67 @@ class FeedOptionViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun changeRenameDialogVisible(visible: Boolean) {
|
||||
_viewState.update {
|
||||
fun showRenameDialog() {
|
||||
_feedOptionUiState.update {
|
||||
it.copy(
|
||||
renameDialogVisible = visible,
|
||||
newName = if (visible) _viewState.value.feed?.name ?: "" else "",
|
||||
renameDialogVisible = true,
|
||||
newName = _feedOptionUiState.value.feed?.name ?: "",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun inputNewName(content: String) {
|
||||
_viewState.update {
|
||||
fun hideRenameDialog() {
|
||||
_feedOptionUiState.update {
|
||||
it.copy(
|
||||
renameDialogVisible = false,
|
||||
newName = "",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun inputNewName(content: String) {
|
||||
_feedOptionUiState.update {
|
||||
it.copy(
|
||||
newName = content
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun changeFeedUrlDialogVisible(visible: Boolean) {
|
||||
_viewState.update {
|
||||
fun showFeedUrlDialog() {
|
||||
_feedOptionUiState.update {
|
||||
it.copy(
|
||||
changeUrlDialogVisible = visible,
|
||||
newUrl = if (visible) _viewState.value.feed?.url ?: "" else "",
|
||||
changeUrlDialogVisible = true,
|
||||
newUrl = _feedOptionUiState.value.feed?.url ?: "",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun inputNewUrl(content: String) {
|
||||
_viewState.update {
|
||||
fun hideFeedUrlDialog() {
|
||||
_feedOptionUiState.update {
|
||||
it.copy(
|
||||
changeUrlDialogVisible = false,
|
||||
newUrl = "",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun inputNewUrl(content: String) {
|
||||
_feedOptionUiState.update {
|
||||
it.copy(
|
||||
newUrl = content
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun changeFeedUrl() {
|
||||
_viewState.value.feed?.let {
|
||||
fun changeFeedUrl() {
|
||||
_feedOptionUiState.value.feed?.let {
|
||||
viewModelScope.launch {
|
||||
rssRepository.get().updateFeed(
|
||||
it.copy(
|
||||
url = _viewState.value.newUrl
|
||||
url = _feedOptionUiState.value.newUrl
|
||||
)
|
||||
)
|
||||
_viewState.update {
|
||||
_feedOptionUiState.update {
|
||||
it.copy(
|
||||
changeUrlDialogVisible = false,
|
||||
)
|
||||
|
@ -287,7 +284,7 @@ class FeedOptionViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
data class FeedOptionViewState(
|
||||
data class FeedOptionUiState(
|
||||
var drawerState: ModalBottomSheetState = ModalBottomSheetState(ModalBottomSheetValue.Hidden),
|
||||
val feed: Feed? = null,
|
||||
val selectedGroupId: String = "",
|
||||
|
@ -301,57 +298,3 @@ data class FeedOptionViewState(
|
|||
val newUrl: String = "",
|
||||
val changeUrlDialogVisible: Boolean = false,
|
||||
)
|
||||
|
||||
sealed class FeedOptionViewAction {
|
||||
data class Show(
|
||||
val scope: CoroutineScope,
|
||||
val feedId: String
|
||||
) : FeedOptionViewAction()
|
||||
|
||||
data class Hide(
|
||||
val scope: CoroutineScope,
|
||||
) : FeedOptionViewAction()
|
||||
|
||||
object ChangeAllowNotificationPreset : FeedOptionViewAction()
|
||||
object ChangeParseFullContentPreset : FeedOptionViewAction()
|
||||
|
||||
data class SelectedGroup(
|
||||
val groupId: String
|
||||
) : FeedOptionViewAction()
|
||||
|
||||
data class InputNewGroup(
|
||||
val content: String
|
||||
) : FeedOptionViewAction()
|
||||
|
||||
data class Delete(
|
||||
val callback: () -> Unit = {}
|
||||
) : FeedOptionViewAction()
|
||||
|
||||
object ShowDeleteDialog : FeedOptionViewAction()
|
||||
object HideDeleteDialog : FeedOptionViewAction()
|
||||
|
||||
data class Clear(
|
||||
val callback: () -> Unit = {}
|
||||
) : FeedOptionViewAction()
|
||||
|
||||
object ShowClearDialog : FeedOptionViewAction()
|
||||
object HideClearDialog : FeedOptionViewAction()
|
||||
|
||||
object ShowNewGroupDialog : FeedOptionViewAction()
|
||||
object HideNewGroupDialog : FeedOptionViewAction()
|
||||
object AddNewGroup : FeedOptionViewAction()
|
||||
|
||||
object ShowRenameDialog : FeedOptionViewAction()
|
||||
object HideRenameDialog : FeedOptionViewAction()
|
||||
object Rename : FeedOptionViewAction()
|
||||
data class InputNewName(
|
||||
val content: String
|
||||
) : FeedOptionViewAction()
|
||||
|
||||
object ShowChangeUrlDialog : FeedOptionViewAction()
|
||||
object HideChangeUrlDialog : FeedOptionViewAction()
|
||||
object ChangeUrl : FeedOptionViewAction()
|
||||
data class InputNewUrl(
|
||||
val content: String
|
||||
) : FeedOptionViewAction()
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ import androidx.compose.material3.Text
|
|||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
|
@ -18,20 +17,19 @@ import me.ash.reader.ui.ext.showToast
|
|||
|
||||
@Composable
|
||||
fun AllAllowNotificationDialog(
|
||||
modifier: Modifier = Modifier,
|
||||
groupName: String,
|
||||
viewModel: GroupOptionViewModel = hiltViewModel(),
|
||||
groupOptionViewModel: GroupOptionViewModel = hiltViewModel(),
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val viewState = viewModel.viewState.collectAsStateValue()
|
||||
val groupOptionUiState = groupOptionViewModel.groupOptionUiState.collectAsStateValue()
|
||||
val scope = rememberCoroutineScope()
|
||||
val allowToastString = stringResource(R.string.all_allow_notification_toast, groupName)
|
||||
val denyToastString = stringResource(R.string.all_deny_notification_toast, groupName)
|
||||
|
||||
Dialog(
|
||||
visible = viewState.allAllowNotificationDialogVisible,
|
||||
visible = groupOptionUiState.allAllowNotificationDialogVisible,
|
||||
onDismissRequest = {
|
||||
viewModel.dispatch(GroupOptionViewAction.HideAllAllowNotificationDialog)
|
||||
groupOptionViewModel.hideAllAllowNotificationDialog()
|
||||
},
|
||||
icon = {
|
||||
Icon(
|
||||
|
@ -48,11 +46,11 @@ fun AllAllowNotificationDialog(
|
|||
confirmButton = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
viewModel.dispatch(GroupOptionViewAction.AllAllowNotification(true) {
|
||||
viewModel.dispatch(GroupOptionViewAction.HideAllAllowNotificationDialog)
|
||||
viewModel.dispatch(GroupOptionViewAction.Hide(scope))
|
||||
groupOptionViewModel.allAllowNotification(true) {
|
||||
groupOptionViewModel.hideAllAllowNotificationDialog()
|
||||
groupOptionViewModel.hideDrawer(scope)
|
||||
context.showToast(allowToastString)
|
||||
})
|
||||
}
|
||||
}
|
||||
) {
|
||||
Text(
|
||||
|
@ -63,11 +61,11 @@ fun AllAllowNotificationDialog(
|
|||
dismissButton = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
viewModel.dispatch(GroupOptionViewAction.AllAllowNotification(false) {
|
||||
viewModel.dispatch(GroupOptionViewAction.HideAllAllowNotificationDialog)
|
||||
viewModel.dispatch(GroupOptionViewAction.Hide(scope))
|
||||
groupOptionViewModel.allAllowNotification(false) {
|
||||
groupOptionViewModel.hideAllAllowNotificationDialog()
|
||||
groupOptionViewModel.hideDrawer(scope)
|
||||
context.showToast(denyToastString)
|
||||
})
|
||||
}
|
||||
}
|
||||
) {
|
||||
Text(
|
||||
|
|
|
@ -7,7 +7,6 @@ import androidx.compose.material3.Text
|
|||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
|
@ -18,20 +17,21 @@ import me.ash.reader.ui.ext.showToast
|
|||
|
||||
@Composable
|
||||
fun AllMoveToGroupDialog(
|
||||
modifier: Modifier = Modifier,
|
||||
groupName: String,
|
||||
viewModel: GroupOptionViewModel = hiltViewModel(),
|
||||
groupOptionViewModel: GroupOptionViewModel = hiltViewModel(),
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val viewState = viewModel.viewState.collectAsStateValue()
|
||||
val groupOptionUiState = groupOptionViewModel.groupOptionUiState.collectAsStateValue()
|
||||
val scope = rememberCoroutineScope()
|
||||
val toastString =
|
||||
stringResource(R.string.all_move_to_group_toast, viewState.targetGroup?.name ?: "")
|
||||
val toastString = stringResource(
|
||||
R.string.all_move_to_group_toast,
|
||||
groupOptionUiState.targetGroup?.name ?: ""
|
||||
)
|
||||
|
||||
Dialog(
|
||||
visible = viewState.allMoveToGroupDialogVisible,
|
||||
visible = groupOptionUiState.allMoveToGroupDialogVisible,
|
||||
onDismissRequest = {
|
||||
viewModel.dispatch(GroupOptionViewAction.HideAllMoveToGroupDialog)
|
||||
groupOptionViewModel.hideAllMoveToGroupDialog()
|
||||
},
|
||||
icon = {
|
||||
Icon(
|
||||
|
@ -47,18 +47,18 @@ fun AllMoveToGroupDialog(
|
|||
text = stringResource(
|
||||
R.string.all_move_to_group_tips,
|
||||
groupName,
|
||||
viewState.targetGroup?.name ?: "",
|
||||
groupOptionUiState.targetGroup?.name ?: "",
|
||||
)
|
||||
)
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
viewModel.dispatch(GroupOptionViewAction.AllMoveToGroup {
|
||||
viewModel.dispatch(GroupOptionViewAction.HideAllMoveToGroupDialog)
|
||||
viewModel.dispatch(GroupOptionViewAction.Hide(scope))
|
||||
groupOptionViewModel.allMoveToGroup {
|
||||
groupOptionViewModel.hideAllMoveToGroupDialog()
|
||||
groupOptionViewModel.hideDrawer(scope)
|
||||
context.showToast(toastString)
|
||||
})
|
||||
}
|
||||
}
|
||||
) {
|
||||
Text(
|
||||
|
@ -69,7 +69,7 @@ fun AllMoveToGroupDialog(
|
|||
dismissButton = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
viewModel.dispatch(GroupOptionViewAction.HideAllMoveToGroupDialog)
|
||||
groupOptionViewModel.hideAllMoveToGroupDialog()
|
||||
}
|
||||
) {
|
||||
Text(
|
||||
|
|
|
@ -7,7 +7,6 @@ import androidx.compose.material3.Text
|
|||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
|
@ -18,20 +17,19 @@ import me.ash.reader.ui.ext.showToast
|
|||
|
||||
@Composable
|
||||
fun AllParseFullContentDialog(
|
||||
modifier: Modifier = Modifier,
|
||||
groupName: String,
|
||||
viewModel: GroupOptionViewModel = hiltViewModel(),
|
||||
groupOptionViewModel: GroupOptionViewModel = hiltViewModel(),
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val viewState = viewModel.viewState.collectAsStateValue()
|
||||
val groupOptionUiState = groupOptionViewModel.groupOptionUiState.collectAsStateValue()
|
||||
val scope = rememberCoroutineScope()
|
||||
val allowToastString = stringResource(R.string.all_parse_full_content_toast, groupName)
|
||||
val denyToastString = stringResource(R.string.all_deny_parse_full_content_toast, groupName)
|
||||
|
||||
Dialog(
|
||||
visible = viewState.allParseFullContentDialogVisible,
|
||||
visible = groupOptionUiState.allParseFullContentDialogVisible,
|
||||
onDismissRequest = {
|
||||
viewModel.dispatch(GroupOptionViewAction.HideAllParseFullContentDialog)
|
||||
groupOptionViewModel.hideAllParseFullContentDialog()
|
||||
},
|
||||
icon = {
|
||||
Icon(
|
||||
|
@ -48,11 +46,11 @@ fun AllParseFullContentDialog(
|
|||
confirmButton = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
viewModel.dispatch(GroupOptionViewAction.AllParseFullContent(true) {
|
||||
viewModel.dispatch(GroupOptionViewAction.HideAllParseFullContentDialog)
|
||||
viewModel.dispatch(GroupOptionViewAction.Hide(scope))
|
||||
groupOptionViewModel.allParseFullContent(true) {
|
||||
groupOptionViewModel.hideAllParseFullContentDialog()
|
||||
groupOptionViewModel.hideDrawer(scope)
|
||||
context.showToast(allowToastString)
|
||||
})
|
||||
}
|
||||
}
|
||||
) {
|
||||
Text(
|
||||
|
@ -63,11 +61,11 @@ fun AllParseFullContentDialog(
|
|||
dismissButton = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
viewModel.dispatch(GroupOptionViewAction.AllParseFullContent(false) {
|
||||
viewModel.dispatch(GroupOptionViewAction.HideAllParseFullContentDialog)
|
||||
viewModel.dispatch(GroupOptionViewAction.Hide(scope))
|
||||
groupOptionViewModel.allParseFullContent(false) {
|
||||
groupOptionViewModel.hideAllParseFullContentDialog()
|
||||
groupOptionViewModel.hideDrawer(scope)
|
||||
context.showToast(denyToastString)
|
||||
})
|
||||
}
|
||||
}
|
||||
) {
|
||||
Text(
|
||||
|
|
|
@ -7,7 +7,6 @@ import androidx.compose.material3.Text
|
|||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
|
@ -18,19 +17,18 @@ import me.ash.reader.ui.ext.showToast
|
|||
|
||||
@Composable
|
||||
fun ClearGroupDialog(
|
||||
modifier: Modifier = Modifier,
|
||||
groupName: String,
|
||||
viewModel: GroupOptionViewModel = hiltViewModel(),
|
||||
groupOptionViewModel: GroupOptionViewModel = hiltViewModel(),
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val viewState = viewModel.viewState.collectAsStateValue()
|
||||
val groupOptionUiState = groupOptionViewModel.groupOptionUiState.collectAsStateValue()
|
||||
val scope = rememberCoroutineScope()
|
||||
val toastString = stringResource(R.string.clear_articles_in_group_toast, groupName)
|
||||
|
||||
Dialog(
|
||||
visible = viewState.clearDialogVisible,
|
||||
visible = groupOptionUiState.clearDialogVisible,
|
||||
onDismissRequest = {
|
||||
viewModel.dispatch(GroupOptionViewAction.HideClearDialog)
|
||||
groupOptionViewModel.hideClearDialog()
|
||||
},
|
||||
icon = {
|
||||
Icon(
|
||||
|
@ -47,11 +45,11 @@ fun ClearGroupDialog(
|
|||
confirmButton = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
viewModel.dispatch(GroupOptionViewAction.Clear {
|
||||
viewModel.dispatch(GroupOptionViewAction.HideClearDialog)
|
||||
viewModel.dispatch(GroupOptionViewAction.Hide(scope))
|
||||
groupOptionViewModel.clear {
|
||||
groupOptionViewModel.hideClearDialog()
|
||||
groupOptionViewModel.hideDrawer(scope)
|
||||
context.showToast(toastString)
|
||||
})
|
||||
}
|
||||
}
|
||||
) {
|
||||
Text(
|
||||
|
@ -62,7 +60,7 @@ fun ClearGroupDialog(
|
|||
dismissButton = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
viewModel.dispatch(GroupOptionViewAction.HideClearDialog)
|
||||
groupOptionViewModel.hideClearDialog()
|
||||
}
|
||||
) {
|
||||
Text(
|
||||
|
|
|
@ -7,7 +7,6 @@ import androidx.compose.material3.Text
|
|||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
|
@ -18,19 +17,18 @@ import me.ash.reader.ui.ext.showToast
|
|||
|
||||
@Composable
|
||||
fun DeleteGroupDialog(
|
||||
modifier: Modifier = Modifier,
|
||||
groupName: String,
|
||||
viewModel: GroupOptionViewModel = hiltViewModel(),
|
||||
groupOptionViewModel: GroupOptionViewModel = hiltViewModel(),
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val viewState = viewModel.viewState.collectAsStateValue()
|
||||
val groupOptionUiState = groupOptionViewModel.groupOptionUiState.collectAsStateValue()
|
||||
val scope = rememberCoroutineScope()
|
||||
val toastString = stringResource(R.string.delete_toast, groupName)
|
||||
|
||||
Dialog(
|
||||
visible = viewState.deleteDialogVisible,
|
||||
visible = groupOptionUiState.deleteDialogVisible,
|
||||
onDismissRequest = {
|
||||
viewModel.dispatch(GroupOptionViewAction.HideDeleteDialog)
|
||||
groupOptionViewModel.hideDeleteDialog()
|
||||
},
|
||||
icon = {
|
||||
Icon(
|
||||
|
@ -47,11 +45,11 @@ fun DeleteGroupDialog(
|
|||
confirmButton = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
viewModel.dispatch(GroupOptionViewAction.Delete {
|
||||
viewModel.dispatch(GroupOptionViewAction.HideDeleteDialog)
|
||||
viewModel.dispatch(GroupOptionViewAction.Hide(scope))
|
||||
groupOptionViewModel.delete {
|
||||
groupOptionViewModel.hideDeleteDialog()
|
||||
groupOptionViewModel.hideDrawer(scope)
|
||||
context.showToast(toastString)
|
||||
})
|
||||
}
|
||||
}
|
||||
) {
|
||||
Text(
|
||||
|
@ -62,7 +60,7 @@ fun DeleteGroupDialog(
|
|||
dismissButton = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
viewModel.dispatch(GroupOptionViewAction.HideDeleteDialog)
|
||||
groupOptionViewModel.hideDeleteDialog()
|
||||
}
|
||||
) {
|
||||
Text(
|
||||
|
|
|
@ -42,24 +42,23 @@ import me.ash.reader.ui.ext.*
|
|||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
fun GroupOptionDrawer(
|
||||
modifier: Modifier = Modifier,
|
||||
groupOptionViewModel: GroupOptionViewModel = hiltViewModel(),
|
||||
content: @Composable () -> Unit = {},
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val scope = rememberCoroutineScope()
|
||||
val viewState = groupOptionViewModel.viewState.collectAsStateValue()
|
||||
val group = viewState.group
|
||||
val toastString = stringResource(R.string.rename_toast, viewState.newName)
|
||||
val groupOptionUiState = groupOptionViewModel.groupOptionUiState.collectAsStateValue()
|
||||
val group = groupOptionUiState.group
|
||||
val toastString = stringResource(R.string.rename_toast, groupOptionUiState.newName)
|
||||
|
||||
BackHandler(viewState.drawerState.isVisible) {
|
||||
BackHandler(groupOptionUiState.drawerState.isVisible) {
|
||||
scope.launch {
|
||||
viewState.drawerState.hide()
|
||||
groupOptionUiState.drawerState.hide()
|
||||
}
|
||||
}
|
||||
|
||||
BottomDrawer(
|
||||
drawerState = viewState.drawerState,
|
||||
drawerState = groupOptionUiState.drawerState,
|
||||
sheetContent = {
|
||||
Column(modifier = Modifier.navigationBarsPadding()) {
|
||||
Column(
|
||||
|
@ -75,7 +74,7 @@ fun GroupOptionDrawer(
|
|||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Text(
|
||||
modifier = Modifier.roundClick {
|
||||
groupOptionViewModel.dispatch(GroupOptionViewAction.ShowRenameDialog)
|
||||
groupOptionViewModel.showRenameDialog()
|
||||
},
|
||||
text = group?.name ?: stringResource(R.string.unknown),
|
||||
style = MaterialTheme.typography.headlineSmall,
|
||||
|
@ -106,15 +105,15 @@ fun GroupOptionDrawer(
|
|||
Spacer(modifier = Modifier.height(10.dp))
|
||||
Preset(groupOptionViewModel, group, context)
|
||||
|
||||
if (viewState.groups.size != 1) {
|
||||
if (groupOptionUiState.groups.size != 1) {
|
||||
Spacer(modifier = Modifier.height(26.dp))
|
||||
Subtitle(text = stringResource(R.string.move_to_group))
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
|
||||
if (viewState.groups.size > 6) {
|
||||
LazyRowGroups(viewState, group, groupOptionViewModel)
|
||||
if (groupOptionUiState.groups.size > 6) {
|
||||
LazyRowGroups(groupOptionUiState, group, groupOptionViewModel)
|
||||
} else {
|
||||
FlowRowGroups(viewState, group, groupOptionViewModel)
|
||||
FlowRowGroups(groupOptionUiState, group, groupOptionViewModel)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -132,20 +131,20 @@ fun GroupOptionDrawer(
|
|||
AllParseFullContentDialog(groupName = group?.name ?: "")
|
||||
AllMoveToGroupDialog(groupName = group?.name ?: "")
|
||||
TextFieldDialog(
|
||||
visible = viewState.renameDialogVisible,
|
||||
visible = groupOptionUiState.renameDialogVisible,
|
||||
title = stringResource(R.string.rename),
|
||||
icon = Icons.Outlined.Edit,
|
||||
value = viewState.newName,
|
||||
value = groupOptionUiState.newName,
|
||||
placeholder = stringResource(R.string.name),
|
||||
onValueChange = {
|
||||
groupOptionViewModel.dispatch(GroupOptionViewAction.InputNewName(it))
|
||||
groupOptionViewModel.inputNewName(it)
|
||||
},
|
||||
onDismissRequest = {
|
||||
groupOptionViewModel.dispatch(GroupOptionViewAction.HideRenameDialog)
|
||||
groupOptionViewModel.hideRenameDialog()
|
||||
},
|
||||
onConfirm = {
|
||||
groupOptionViewModel.dispatch(GroupOptionViewAction.Rename)
|
||||
groupOptionViewModel.dispatch(GroupOptionViewAction.Hide(scope))
|
||||
groupOptionViewModel.rename()
|
||||
groupOptionViewModel.hideDrawer(scope)
|
||||
context.showToast(toastString)
|
||||
}
|
||||
)
|
||||
|
@ -177,7 +176,7 @@ private fun Preset(
|
|||
)
|
||||
},
|
||||
) {
|
||||
groupOptionViewModel.dispatch(GroupOptionViewAction.ShowAllAllowNotificationDialog)
|
||||
groupOptionViewModel.showAllAllowNotificationDialog()
|
||||
}
|
||||
SelectionChip(
|
||||
modifier = Modifier.animateContentSize(),
|
||||
|
@ -193,14 +192,14 @@ private fun Preset(
|
|||
)
|
||||
},
|
||||
) {
|
||||
groupOptionViewModel.dispatch(GroupOptionViewAction.ShowAllParseFullContentDialog)
|
||||
groupOptionViewModel.showAllParseFullContentDialog()
|
||||
}
|
||||
SelectionChip(
|
||||
modifier = Modifier.animateContentSize(),
|
||||
content = stringResource(R.string.clear_articles),
|
||||
selected = false,
|
||||
) {
|
||||
groupOptionViewModel.dispatch(GroupOptionViewAction.ShowClearDialog)
|
||||
groupOptionViewModel.showClearDialog()
|
||||
}
|
||||
if (group?.id != context.currentAccountId.getDefaultGroupId()) {
|
||||
SelectionChip(
|
||||
|
@ -208,7 +207,7 @@ private fun Preset(
|
|||
content = stringResource(R.string.delete_group),
|
||||
selected = false,
|
||||
) {
|
||||
groupOptionViewModel.dispatch(GroupOptionViewAction.ShowDeleteDialog)
|
||||
groupOptionViewModel.showDeleteDialog()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -216,7 +215,7 @@ private fun Preset(
|
|||
|
||||
@Composable
|
||||
private fun FlowRowGroups(
|
||||
viewState: GroupOptionViewState,
|
||||
groupOptionUiState: GroupOptionUiState,
|
||||
group: Group?,
|
||||
groupOptionViewModel: GroupOptionViewModel
|
||||
) {
|
||||
|
@ -226,16 +225,14 @@ private fun FlowRowGroups(
|
|||
crossAxisSpacing = 10.dp,
|
||||
mainAxisSpacing = 10.dp,
|
||||
) {
|
||||
viewState.groups.forEach {
|
||||
groupOptionUiState.groups.forEach {
|
||||
if (it.id != group?.id) {
|
||||
SelectionChip(
|
||||
modifier = Modifier.animateContentSize(),
|
||||
content = it.name,
|
||||
selected = false,
|
||||
) {
|
||||
groupOptionViewModel.dispatch(
|
||||
GroupOptionViewAction.ShowAllMoveToGroupDialog(it)
|
||||
)
|
||||
groupOptionViewModel.showAllMoveToGroupDialog(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -244,21 +241,19 @@ private fun FlowRowGroups(
|
|||
|
||||
@Composable
|
||||
private fun LazyRowGroups(
|
||||
viewState: GroupOptionViewState,
|
||||
groupOptionUiState: GroupOptionUiState,
|
||||
group: Group?,
|
||||
groupOptionViewModel: GroupOptionViewModel
|
||||
) {
|
||||
LazyRow {
|
||||
items(viewState.groups) {
|
||||
items(groupOptionUiState.groups) {
|
||||
if (it.id != group?.id) {
|
||||
SelectionChip(
|
||||
modifier = Modifier.animateContentSize(),
|
||||
content = it.name,
|
||||
selected = false,
|
||||
) {
|
||||
groupOptionViewModel.dispatch(
|
||||
GroupOptionViewAction.ShowAllMoveToGroupDialog(it)
|
||||
)
|
||||
groupOptionViewModel.showAllMoveToGroupDialog(it)
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.width(10.dp))
|
||||
|
|
|
@ -29,13 +29,13 @@ class GroupOptionViewModel @Inject constructor(
|
|||
@DispatcherIO
|
||||
private val dispatcherIO: CoroutineDispatcher,
|
||||
) : ViewModel() {
|
||||
private val _viewState = MutableStateFlow(GroupOptionViewState())
|
||||
val viewState: StateFlow<GroupOptionViewState> = _viewState.asStateFlow()
|
||||
private val _groupOptionUiState = MutableStateFlow(GroupOptionUiState())
|
||||
val groupOptionUiState: StateFlow<GroupOptionUiState> = _groupOptionUiState.asStateFlow()
|
||||
|
||||
init {
|
||||
viewModelScope.launch(dispatcherIO) {
|
||||
rssRepository.get().pullGroups().collect { groups ->
|
||||
_viewState.update {
|
||||
_groupOptionUiState.update {
|
||||
it.copy(
|
||||
groups = groups
|
||||
)
|
||||
|
@ -44,70 +44,25 @@ class GroupOptionViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
fun dispatch(action: GroupOptionViewAction) {
|
||||
when (action) {
|
||||
is GroupOptionViewAction.Show -> show(action.scope, action.groupId)
|
||||
is GroupOptionViewAction.Hide -> hide(action.scope)
|
||||
is GroupOptionViewAction.ShowDeleteDialog -> changeDeleteDialogVisible(true)
|
||||
is GroupOptionViewAction.HideDeleteDialog -> changeDeleteDialogVisible(false)
|
||||
is GroupOptionViewAction.Delete -> delete(action.callback)
|
||||
|
||||
is GroupOptionViewAction.ShowClearDialog -> showClearDialog()
|
||||
is GroupOptionViewAction.HideClearDialog -> hideClearDialog()
|
||||
is GroupOptionViewAction.Clear -> clear(action.callback)
|
||||
|
||||
is GroupOptionViewAction.ShowAllAllowNotificationDialog ->
|
||||
changeAllAllowNotificationDialogVisible(true)
|
||||
is GroupOptionViewAction.HideAllAllowNotificationDialog ->
|
||||
changeAllAllowNotificationDialogVisible(false)
|
||||
is GroupOptionViewAction.AllAllowNotification ->
|
||||
allAllowNotification(action.isNotification, action.callback)
|
||||
|
||||
is GroupOptionViewAction.ShowAllParseFullContentDialog ->
|
||||
changeAllParseFullContentDialogVisible(true)
|
||||
is GroupOptionViewAction.HideAllParseFullContentDialog ->
|
||||
changeAllParseFullContentDialogVisible(false)
|
||||
is GroupOptionViewAction.AllParseFullContent ->
|
||||
allParseFullContent(action.isFullContent, action.callback)
|
||||
|
||||
is GroupOptionViewAction.ShowAllMoveToGroupDialog ->
|
||||
changeAllMoveToGroupDialogVisible(action.targetGroup, true)
|
||||
is GroupOptionViewAction.HideAllMoveToGroupDialog ->
|
||||
changeAllMoveToGroupDialogVisible(visible = false)
|
||||
is GroupOptionViewAction.AllMoveToGroup ->
|
||||
allMoveToGroup(action.callback)
|
||||
|
||||
is GroupOptionViewAction.InputNewName -> inputNewName(action.content)
|
||||
is GroupOptionViewAction.Rename -> rename()
|
||||
is GroupOptionViewAction.ShowRenameDialog -> changeRenameDialogVisible(true)
|
||||
is GroupOptionViewAction.HideRenameDialog -> changeRenameDialogVisible(false)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun fetchGroup(groupId: String) {
|
||||
val group = rssRepository.get().findGroupById(groupId)
|
||||
_viewState.update {
|
||||
it.copy(
|
||||
group = group,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun show(scope: CoroutineScope, groupId: String) {
|
||||
fun showDrawer(scope: CoroutineScope, groupId: String) {
|
||||
scope.launch {
|
||||
fetchGroup(groupId)
|
||||
_viewState.value.drawerState.show()
|
||||
_groupOptionUiState.update {
|
||||
it.copy(
|
||||
group = rssRepository.get().findGroupById(groupId),
|
||||
)
|
||||
}
|
||||
_groupOptionUiState.value.drawerState.show()
|
||||
}
|
||||
}
|
||||
|
||||
private fun hide(scope: CoroutineScope) {
|
||||
fun hideDrawer(scope: CoroutineScope) {
|
||||
scope.launch {
|
||||
_viewState.value.drawerState.hide()
|
||||
_groupOptionUiState.value.drawerState.hide()
|
||||
}
|
||||
}
|
||||
|
||||
private fun allAllowNotification(isNotification: Boolean, callback: () -> Unit = {}) {
|
||||
_viewState.value.group?.let {
|
||||
fun allAllowNotification(isNotification: Boolean, callback: () -> Unit = {}) {
|
||||
_groupOptionUiState.value.group?.let {
|
||||
viewModelScope.launch(dispatcherIO) {
|
||||
rssRepository.get().groupAllowNotification(it, isNotification)
|
||||
withContext(dispatcherMain) {
|
||||
|
@ -117,16 +72,24 @@ class GroupOptionViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun changeAllAllowNotificationDialogVisible(visible: Boolean) {
|
||||
_viewState.update {
|
||||
fun showAllAllowNotificationDialog() {
|
||||
_groupOptionUiState.update {
|
||||
it.copy(
|
||||
allAllowNotificationDialogVisible = visible,
|
||||
allAllowNotificationDialogVisible = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun allParseFullContent(isFullContent: Boolean, callback: () -> Unit = {}) {
|
||||
_viewState.value.group?.let {
|
||||
fun hideAllAllowNotificationDialog() {
|
||||
_groupOptionUiState.update {
|
||||
it.copy(
|
||||
allAllowNotificationDialogVisible = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun allParseFullContent(isFullContent: Boolean, callback: () -> Unit = {}) {
|
||||
_groupOptionUiState.value.group?.let {
|
||||
viewModelScope.launch(dispatcherIO) {
|
||||
rssRepository.get().groupParseFullContent(it, isFullContent)
|
||||
withContext(dispatcherMain) {
|
||||
|
@ -136,16 +99,24 @@ class GroupOptionViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun changeAllParseFullContentDialogVisible(visible: Boolean) {
|
||||
_viewState.update {
|
||||
fun showAllParseFullContentDialog() {
|
||||
_groupOptionUiState.update {
|
||||
it.copy(
|
||||
allParseFullContentDialogVisible = visible,
|
||||
allParseFullContentDialogVisible = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun delete(callback: () -> Unit = {}) {
|
||||
_viewState.value.group?.let {
|
||||
fun hideAllParseFullContentDialog() {
|
||||
_groupOptionUiState.update {
|
||||
it.copy(
|
||||
allParseFullContentDialogVisible = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun delete(callback: () -> Unit = {}) {
|
||||
_groupOptionUiState.value.group?.let {
|
||||
viewModelScope.launch(dispatcherIO) {
|
||||
rssRepository.get().deleteGroup(it)
|
||||
withContext(dispatcherMain) {
|
||||
|
@ -155,32 +126,40 @@ class GroupOptionViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun changeDeleteDialogVisible(visible: Boolean) {
|
||||
_viewState.update {
|
||||
fun showDeleteDialog() {
|
||||
_groupOptionUiState.update {
|
||||
it.copy(
|
||||
deleteDialogVisible = visible,
|
||||
deleteDialogVisible = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun showClearDialog() {
|
||||
_viewState.update {
|
||||
fun hideDeleteDialog() {
|
||||
_groupOptionUiState.update {
|
||||
it.copy(
|
||||
deleteDialogVisible = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun showClearDialog() {
|
||||
_groupOptionUiState.update {
|
||||
it.copy(
|
||||
clearDialogVisible = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun hideClearDialog() {
|
||||
_viewState.update {
|
||||
fun hideClearDialog() {
|
||||
_groupOptionUiState.update {
|
||||
it.copy(
|
||||
clearDialogVisible = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun clear(callback: () -> Unit = {}) {
|
||||
_viewState.value.group?.let {
|
||||
fun clear(callback: () -> Unit = {}) {
|
||||
_groupOptionUiState.value.group?.let {
|
||||
viewModelScope.launch(dispatcherIO) {
|
||||
rssRepository.get().deleteArticles(group = it)
|
||||
withContext(dispatcherMain) {
|
||||
|
@ -190,9 +169,9 @@ class GroupOptionViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun allMoveToGroup(callback: () -> Unit) {
|
||||
_viewState.value.group?.let { group ->
|
||||
_viewState.value.targetGroup?.let { targetGroup ->
|
||||
fun allMoveToGroup(callback: () -> Unit) {
|
||||
_groupOptionUiState.value.group?.let { group ->
|
||||
_groupOptionUiState.value.targetGroup?.let { targetGroup ->
|
||||
viewModelScope.launch(dispatcherIO) {
|
||||
rssRepository.get().groupMoveToTargetGroup(group, targetGroup)
|
||||
withContext(dispatcherMain) {
|
||||
|
@ -203,24 +182,33 @@ class GroupOptionViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun changeAllMoveToGroupDialogVisible(targetGroup: Group? = null, visible: Boolean) {
|
||||
_viewState.update {
|
||||
fun showAllMoveToGroupDialog(targetGroup: Group) {
|
||||
_groupOptionUiState.update {
|
||||
it.copy(
|
||||
targetGroup = if (visible) targetGroup else null,
|
||||
allMoveToGroupDialogVisible = visible,
|
||||
targetGroup = targetGroup,
|
||||
allMoveToGroupDialogVisible = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun rename() {
|
||||
_viewState.value.group?.let {
|
||||
fun hideAllMoveToGroupDialog() {
|
||||
_groupOptionUiState.update {
|
||||
it.copy(
|
||||
targetGroup = null,
|
||||
allMoveToGroupDialogVisible = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun rename() {
|
||||
_groupOptionUiState.value.group?.let {
|
||||
viewModelScope.launch {
|
||||
rssRepository.get().updateGroup(
|
||||
it.copy(
|
||||
name = _viewState.value.newName
|
||||
name = _groupOptionUiState.value.newName
|
||||
)
|
||||
)
|
||||
_viewState.update {
|
||||
_groupOptionUiState.update {
|
||||
it.copy(
|
||||
renameDialogVisible = false,
|
||||
)
|
||||
|
@ -229,17 +217,26 @@ class GroupOptionViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun changeRenameDialogVisible(visible: Boolean) {
|
||||
_viewState.update {
|
||||
fun showRenameDialog() {
|
||||
_groupOptionUiState.update {
|
||||
it.copy(
|
||||
renameDialogVisible = visible,
|
||||
newName = if (visible) _viewState.value.group?.name ?: "" else "",
|
||||
renameDialogVisible = true,
|
||||
newName = _groupOptionUiState.value.group?.name ?: "",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun inputNewName(content: String) {
|
||||
_viewState.update {
|
||||
fun hideRenameDialog() {
|
||||
_groupOptionUiState.update {
|
||||
it.copy(
|
||||
renameDialogVisible = false,
|
||||
newName = "",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun inputNewName(content: String) {
|
||||
_groupOptionUiState.update {
|
||||
it.copy(
|
||||
newName = content
|
||||
)
|
||||
|
@ -248,7 +245,7 @@ class GroupOptionViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
data class GroupOptionViewState(
|
||||
data class GroupOptionUiState(
|
||||
var drawerState: ModalBottomSheetState = ModalBottomSheetState(ModalBottomSheetValue.Hidden),
|
||||
val group: Group? = null,
|
||||
val targetGroup: Group? = null,
|
||||
|
@ -261,61 +258,3 @@ data class GroupOptionViewState(
|
|||
val newName: String = "",
|
||||
val renameDialogVisible: Boolean = false,
|
||||
)
|
||||
|
||||
sealed class GroupOptionViewAction {
|
||||
data class Show(
|
||||
val scope: CoroutineScope,
|
||||
val groupId: String
|
||||
) : GroupOptionViewAction()
|
||||
|
||||
data class Hide(
|
||||
val scope: CoroutineScope,
|
||||
) : GroupOptionViewAction()
|
||||
|
||||
data class Delete(
|
||||
val callback: () -> Unit = {}
|
||||
) : GroupOptionViewAction()
|
||||
|
||||
object ShowDeleteDialog : GroupOptionViewAction()
|
||||
object HideDeleteDialog : GroupOptionViewAction()
|
||||
|
||||
data class Clear(
|
||||
val callback: () -> Unit = {}
|
||||
) : GroupOptionViewAction()
|
||||
|
||||
object ShowClearDialog : GroupOptionViewAction()
|
||||
object HideClearDialog : GroupOptionViewAction()
|
||||
|
||||
data class AllParseFullContent(
|
||||
val isFullContent: Boolean,
|
||||
val callback: () -> Unit = {}
|
||||
) : GroupOptionViewAction()
|
||||
|
||||
object ShowAllParseFullContentDialog : GroupOptionViewAction()
|
||||
object HideAllParseFullContentDialog : GroupOptionViewAction()
|
||||
|
||||
data class AllAllowNotification(
|
||||
val isNotification: Boolean,
|
||||
val callback: () -> Unit = {}
|
||||
) : GroupOptionViewAction()
|
||||
|
||||
object ShowAllAllowNotificationDialog : GroupOptionViewAction()
|
||||
object HideAllAllowNotificationDialog : GroupOptionViewAction()
|
||||
|
||||
data class AllMoveToGroup(
|
||||
val callback: () -> Unit = {}
|
||||
) : GroupOptionViewAction()
|
||||
|
||||
data class ShowAllMoveToGroupDialog(
|
||||
val targetGroup: Group
|
||||
) : GroupOptionViewAction()
|
||||
|
||||
object HideAllMoveToGroupDialog : GroupOptionViewAction()
|
||||
|
||||
object ShowRenameDialog : GroupOptionViewAction()
|
||||
object HideRenameDialog : GroupOptionViewAction()
|
||||
object Rename : GroupOptionViewAction()
|
||||
data class InputNewName(
|
||||
val content: String
|
||||
) : GroupOptionViewAction()
|
||||
}
|
||||
|
|
|
@ -41,32 +41,32 @@ fun SubscribeDialog(
|
|||
) {
|
||||
val context = LocalContext.current
|
||||
val focusManager = LocalFocusManager.current
|
||||
val viewState = subscribeViewModel.viewState.collectAsStateValue()
|
||||
val groupsState = viewState.groups.collectAsState(initial = emptyList())
|
||||
val subscribeUiState = subscribeViewModel.subscribeUiState.collectAsStateValue()
|
||||
val groupsState = subscribeUiState.groups.collectAsState(initial = emptyList())
|
||||
val launcher = rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) {
|
||||
it?.let { uri ->
|
||||
context.contentResolver.openInputStream(uri)?.let { inputStream ->
|
||||
subscribeViewModel.dispatch(SubscribeViewAction.ImportFromInputStream(inputStream))
|
||||
subscribeViewModel.importFromInputStream(inputStream)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(viewState.visible) {
|
||||
if (viewState.visible) {
|
||||
subscribeViewModel.dispatch(SubscribeViewAction.Init)
|
||||
LaunchedEffect(subscribeUiState.visible) {
|
||||
if (subscribeUiState.visible) {
|
||||
subscribeViewModel.init()
|
||||
} else {
|
||||
subscribeViewModel.dispatch(SubscribeViewAction.Reset)
|
||||
subscribeViewModel.dispatch(SubscribeViewAction.SwitchPage(true))
|
||||
subscribeViewModel.reset()
|
||||
subscribeViewModel.switchPage(true)
|
||||
}
|
||||
}
|
||||
|
||||
Dialog(
|
||||
modifier = Modifier.padding(horizontal = 44.dp),
|
||||
visible = viewState.visible,
|
||||
visible = subscribeUiState.visible,
|
||||
properties = DialogProperties(usePlatformDefaultWidth = false),
|
||||
onDismissRequest = {
|
||||
focusManager.clearFocus()
|
||||
subscribeViewModel.dispatch(SubscribeViewAction.Hide)
|
||||
subscribeViewModel.hideDrawer()
|
||||
},
|
||||
icon = {
|
||||
Icon(
|
||||
|
@ -76,10 +76,10 @@ fun SubscribeDialog(
|
|||
},
|
||||
title = {
|
||||
Text(
|
||||
text = if (viewState.isSearchPage) {
|
||||
viewState.title
|
||||
text = if (subscribeUiState.isSearchPage) {
|
||||
subscribeUiState.title
|
||||
} else {
|
||||
viewState.feed?.name ?: stringResource(R.string.unknown)
|
||||
subscribeUiState.feed?.name ?: stringResource(R.string.unknown)
|
||||
},
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
|
@ -87,7 +87,7 @@ fun SubscribeDialog(
|
|||
},
|
||||
text = {
|
||||
AnimatedContent(
|
||||
targetState = viewState.isSearchPage,
|
||||
targetState = subscribeUiState.isSearchPage,
|
||||
transitionSpec = {
|
||||
slideInHorizontally { width -> width } + fadeIn() with
|
||||
slideOutHorizontally { width -> -width } + fadeOut()
|
||||
|
@ -95,55 +95,55 @@ fun SubscribeDialog(
|
|||
) { targetExpanded ->
|
||||
if (targetExpanded) {
|
||||
ClipboardTextField(
|
||||
readOnly = viewState.lockLinkInput,
|
||||
value = viewState.linkContent,
|
||||
readOnly = subscribeUiState.lockLinkInput,
|
||||
value = subscribeUiState.linkContent,
|
||||
onValueChange = {
|
||||
subscribeViewModel.dispatch(SubscribeViewAction.InputLink(it))
|
||||
subscribeViewModel.inputLink(it)
|
||||
},
|
||||
placeholder = stringResource(R.string.feed_or_site_url),
|
||||
errorText = viewState.errorMessage,
|
||||
errorText = subscribeUiState.errorMessage,
|
||||
imeAction = ImeAction.Search,
|
||||
focusManager = focusManager,
|
||||
onConfirm = {
|
||||
subscribeViewModel.dispatch(SubscribeViewAction.Search)
|
||||
subscribeViewModel.search()
|
||||
},
|
||||
)
|
||||
} else {
|
||||
ResultView(
|
||||
link = viewState.linkContent,
|
||||
link = subscribeUiState.linkContent,
|
||||
groups = groupsState.value,
|
||||
selectedAllowNotificationPreset = viewState.allowNotificationPreset,
|
||||
selectedParseFullContentPreset = viewState.parseFullContentPreset,
|
||||
selectedGroupId = viewState.selectedGroupId,
|
||||
selectedAllowNotificationPreset = subscribeUiState.allowNotificationPreset,
|
||||
selectedParseFullContentPreset = subscribeUiState.parseFullContentPreset,
|
||||
selectedGroupId = subscribeUiState.selectedGroupId,
|
||||
allowNotificationPresetOnClick = {
|
||||
subscribeViewModel.dispatch(SubscribeViewAction.ChangeAllowNotificationPreset)
|
||||
subscribeViewModel.changeAllowNotificationPreset()
|
||||
},
|
||||
parseFullContentPresetOnClick = {
|
||||
subscribeViewModel.dispatch(SubscribeViewAction.ChangeParseFullContentPreset)
|
||||
subscribeViewModel.changeParseFullContentPreset()
|
||||
},
|
||||
onGroupClick = {
|
||||
subscribeViewModel.dispatch(SubscribeViewAction.SelectedGroup(it))
|
||||
subscribeViewModel.selectedGroup(it)
|
||||
},
|
||||
onAddNewGroup = {
|
||||
subscribeViewModel.dispatch(SubscribeViewAction.ShowNewGroupDialog)
|
||||
subscribeViewModel.showNewGroupDialog()
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
if (viewState.isSearchPage) {
|
||||
if (subscribeUiState.isSearchPage) {
|
||||
TextButton(
|
||||
enabled = viewState.linkContent.isNotBlank()
|
||||
&& viewState.title != stringResource(R.string.searching),
|
||||
enabled = subscribeUiState.linkContent.isNotBlank()
|
||||
&& subscribeUiState.title != stringResource(R.string.searching),
|
||||
onClick = {
|
||||
focusManager.clearFocus()
|
||||
subscribeViewModel.dispatch(SubscribeViewAction.Search)
|
||||
subscribeViewModel.search()
|
||||
}
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.search),
|
||||
color = if (viewState.linkContent.isNotBlank()) {
|
||||
color = if (subscribeUiState.linkContent.isNotBlank()) {
|
||||
Color.Unspecified
|
||||
} else {
|
||||
MaterialTheme.colorScheme.outline.copy(alpha = 0.7f)
|
||||
|
@ -154,7 +154,7 @@ fun SubscribeDialog(
|
|||
TextButton(
|
||||
onClick = {
|
||||
focusManager.clearFocus()
|
||||
subscribeViewModel.dispatch(SubscribeViewAction.Subscribe)
|
||||
subscribeViewModel.subscribe()
|
||||
}
|
||||
) {
|
||||
Text(stringResource(R.string.subscribe))
|
||||
|
@ -162,12 +162,12 @@ fun SubscribeDialog(
|
|||
}
|
||||
},
|
||||
dismissButton = {
|
||||
if (viewState.isSearchPage) {
|
||||
if (subscribeUiState.isSearchPage) {
|
||||
TextButton(
|
||||
onClick = {
|
||||
focusManager.clearFocus()
|
||||
launcher.launch("*/*")
|
||||
subscribeViewModel.dispatch(SubscribeViewAction.Hide)
|
||||
subscribeViewModel.hideDrawer()
|
||||
}
|
||||
) {
|
||||
Text(text = stringResource(R.string.import_from_opml))
|
||||
|
@ -176,7 +176,7 @@ fun SubscribeDialog(
|
|||
TextButton(
|
||||
onClick = {
|
||||
focusManager.clearFocus()
|
||||
subscribeViewModel.dispatch(SubscribeViewAction.Hide)
|
||||
subscribeViewModel.hideDrawer()
|
||||
}
|
||||
) {
|
||||
Text(text = stringResource(R.string.cancel))
|
||||
|
@ -186,19 +186,19 @@ fun SubscribeDialog(
|
|||
)
|
||||
|
||||
TextFieldDialog(
|
||||
visible = viewState.newGroupDialogVisible,
|
||||
visible = subscribeUiState.newGroupDialogVisible,
|
||||
title = stringResource(R.string.create_new_group),
|
||||
icon = Icons.Outlined.CreateNewFolder,
|
||||
value = viewState.newGroupContent,
|
||||
value = subscribeUiState.newGroupContent,
|
||||
placeholder = stringResource(R.string.name),
|
||||
onValueChange = {
|
||||
subscribeViewModel.dispatch(SubscribeViewAction.InputNewGroup(it))
|
||||
subscribeViewModel.inputNewGroup(it)
|
||||
},
|
||||
onDismissRequest = {
|
||||
subscribeViewModel.dispatch(SubscribeViewAction.HideNewGroupDialog)
|
||||
subscribeViewModel.hideNewGroupDialog()
|
||||
},
|
||||
onConfirm = {
|
||||
subscribeViewModel.dispatch(SubscribeViewAction.AddNewGroup)
|
||||
subscribeViewModel.addNewGroup()
|
||||
}
|
||||
)
|
||||
}
|
|
@ -3,7 +3,6 @@ package me.ash.reader.ui.page.home.feeds.subscribe
|
|||
import android.util.Log
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.google.accompanist.pager.ExperimentalPagerApi
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.Job
|
||||
|
@ -32,35 +31,12 @@ class SubscribeViewModel @Inject constructor(
|
|||
@DispatcherIO
|
||||
private val dispatcherIO: CoroutineDispatcher,
|
||||
) : ViewModel() {
|
||||
private val _viewState = MutableStateFlow(SubscribeViewState())
|
||||
val viewState: StateFlow<SubscribeViewState> = _viewState.asStateFlow()
|
||||
private val _subscribeUiState = MutableStateFlow(SubscribeUiState())
|
||||
val subscribeUiState: StateFlow<SubscribeUiState> = _subscribeUiState.asStateFlow()
|
||||
private var searchJob: Job? = null
|
||||
|
||||
fun dispatch(action: SubscribeViewAction) {
|
||||
when (action) {
|
||||
is SubscribeViewAction.Init -> init()
|
||||
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)
|
||||
is SubscribeViewAction.Search -> search()
|
||||
is SubscribeViewAction.ChangeAllowNotificationPreset ->
|
||||
changeAllowNotificationPreset()
|
||||
is SubscribeViewAction.ChangeParseFullContentPreset ->
|
||||
changeParseFullContentPreset()
|
||||
is SubscribeViewAction.SelectedGroup -> selectedGroup(action.groupId)
|
||||
is SubscribeViewAction.InputNewGroup -> inputNewGroup(action.content)
|
||||
is SubscribeViewAction.AddNewGroup -> addNewGroup()
|
||||
is SubscribeViewAction.Subscribe -> subscribe()
|
||||
}
|
||||
}
|
||||
|
||||
private fun init() {
|
||||
_viewState.update {
|
||||
fun init() {
|
||||
_subscribeUiState.update {
|
||||
it.copy(
|
||||
title = stringsRepository.getString(R.string.subscribe),
|
||||
groups = rssRepository.get().pullGroups(),
|
||||
|
@ -68,17 +44,17 @@ class SubscribeViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun reset() {
|
||||
fun reset() {
|
||||
searchJob?.cancel()
|
||||
searchJob = null
|
||||
_viewState.update {
|
||||
SubscribeViewState().copy(
|
||||
_subscribeUiState.update {
|
||||
SubscribeUiState().copy(
|
||||
title = stringsRepository.getString(R.string.subscribe),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun importFromInputStream(inputStream: InputStream) {
|
||||
fun importFromInputStream(inputStream: InputStream) {
|
||||
viewModelScope.launch(dispatcherIO) {
|
||||
try {
|
||||
opmlRepository.saveToDatabase(inputStream)
|
||||
|
@ -89,38 +65,38 @@ class SubscribeViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun subscribe() {
|
||||
val feed = _viewState.value.feed ?: return
|
||||
val articles = _viewState.value.articles
|
||||
fun subscribe() {
|
||||
val feed = _subscribeUiState.value.feed ?: return
|
||||
val articles = _subscribeUiState.value.articles
|
||||
viewModelScope.launch(dispatcherIO) {
|
||||
val groupId = async {
|
||||
_viewState.value.selectedGroupId
|
||||
_subscribeUiState.value.selectedGroupId
|
||||
}
|
||||
rssRepository.get().subscribe(
|
||||
feed.copy(
|
||||
groupId = groupId.await(),
|
||||
isNotification = _viewState.value.allowNotificationPreset,
|
||||
isFullContent = _viewState.value.parseFullContentPreset,
|
||||
isNotification = _subscribeUiState.value.allowNotificationPreset,
|
||||
isFullContent = _subscribeUiState.value.parseFullContentPreset,
|
||||
), articles
|
||||
)
|
||||
changeVisible(false)
|
||||
hideDrawer()
|
||||
}
|
||||
}
|
||||
|
||||
private fun selectedGroup(groupId: String) {
|
||||
_viewState.update {
|
||||
fun selectedGroup(groupId: String) {
|
||||
_subscribeUiState.update {
|
||||
it.copy(
|
||||
selectedGroupId = groupId,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun addNewGroup() {
|
||||
if (_viewState.value.newGroupContent.isNotBlank()) {
|
||||
fun addNewGroup() {
|
||||
if (_subscribeUiState.value.newGroupContent.isNotBlank()) {
|
||||
viewModelScope.launch {
|
||||
selectedGroup(rssRepository.get().addGroup(_viewState.value.newGroupContent))
|
||||
changeNewGroupDialogVisible(false)
|
||||
_viewState.update {
|
||||
selectedGroup(rssRepository.get().addGroup(_subscribeUiState.value.newGroupContent))
|
||||
hideNewGroupDialog()
|
||||
_subscribeUiState.update {
|
||||
it.copy(
|
||||
newGroupContent = "",
|
||||
)
|
||||
|
@ -129,48 +105,48 @@ class SubscribeViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun changeParseFullContentPreset() {
|
||||
_viewState.update {
|
||||
fun changeParseFullContentPreset() {
|
||||
_subscribeUiState.update {
|
||||
it.copy(
|
||||
parseFullContentPreset = !_viewState.value.parseFullContentPreset
|
||||
parseFullContentPreset = !_subscribeUiState.value.parseFullContentPreset
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun changeAllowNotificationPreset() {
|
||||
_viewState.update {
|
||||
fun changeAllowNotificationPreset() {
|
||||
_subscribeUiState.update {
|
||||
it.copy(
|
||||
allowNotificationPreset = !_viewState.value.allowNotificationPreset
|
||||
allowNotificationPreset = !_subscribeUiState.value.allowNotificationPreset
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun search() {
|
||||
fun search() {
|
||||
searchJob?.cancel()
|
||||
viewModelScope.launch(dispatcherIO) {
|
||||
try {
|
||||
_viewState.update {
|
||||
_subscribeUiState.update {
|
||||
it.copy(
|
||||
errorMessage = "",
|
||||
)
|
||||
}
|
||||
_viewState.value.linkContent.formatUrl().let { str ->
|
||||
if (str != _viewState.value.linkContent) {
|
||||
_viewState.update {
|
||||
_subscribeUiState.value.linkContent.formatUrl().let { str ->
|
||||
if (str != _subscribeUiState.value.linkContent) {
|
||||
_subscribeUiState.update {
|
||||
it.copy(
|
||||
linkContent = str
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
_viewState.update {
|
||||
_subscribeUiState.update {
|
||||
it.copy(
|
||||
title = stringsRepository.getString(R.string.searching),
|
||||
lockLinkInput = true,
|
||||
)
|
||||
}
|
||||
if (rssRepository.get().isFeedExist(_viewState.value.linkContent)) {
|
||||
_viewState.update {
|
||||
if (rssRepository.get().isFeedExist(_subscribeUiState.value.linkContent)) {
|
||||
_subscribeUiState.update {
|
||||
it.copy(
|
||||
title = stringsRepository.getString(R.string.subscribe),
|
||||
errorMessage = stringsRepository.getString(R.string.already_subscribed),
|
||||
|
@ -179,8 +155,8 @@ class SubscribeViewModel @Inject constructor(
|
|||
}
|
||||
return@launch
|
||||
}
|
||||
val feedWithArticle = rssHelper.searchFeed(_viewState.value.linkContent)
|
||||
_viewState.update {
|
||||
val feedWithArticle = rssHelper.searchFeed(_subscribeUiState.value.linkContent)
|
||||
_subscribeUiState.update {
|
||||
it.copy(
|
||||
feed = feedWithArticle.feed,
|
||||
articles = feedWithArticle.articles,
|
||||
|
@ -189,7 +165,7 @@ class SubscribeViewModel @Inject constructor(
|
|||
switchPage(false)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
_viewState.update {
|
||||
_subscribeUiState.update {
|
||||
it.copy(
|
||||
title = stringsRepository.getString(R.string.subscribe),
|
||||
errorMessage = e.message ?: stringsRepository.getString(R.string.unknown),
|
||||
|
@ -202,8 +178,8 @@ class SubscribeViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun inputLink(content: String) {
|
||||
_viewState.update {
|
||||
fun inputLink(content: String) {
|
||||
_subscribeUiState.update {
|
||||
it.copy(
|
||||
linkContent = content,
|
||||
errorMessage = "",
|
||||
|
@ -211,32 +187,48 @@ class SubscribeViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun inputNewGroup(content: String) {
|
||||
_viewState.update {
|
||||
fun inputNewGroup(content: String) {
|
||||
_subscribeUiState.update {
|
||||
it.copy(
|
||||
newGroupContent = content
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun changeVisible(visible: Boolean) {
|
||||
_viewState.update {
|
||||
fun showDrawer() {
|
||||
_subscribeUiState.update {
|
||||
it.copy(
|
||||
visible = visible
|
||||
visible = true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun changeNewGroupDialogVisible(visible: Boolean) {
|
||||
_viewState.update {
|
||||
fun hideDrawer() {
|
||||
_subscribeUiState.update {
|
||||
it.copy(
|
||||
newGroupDialogVisible = visible,
|
||||
visible = false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun switchPage(isSearchPage: Boolean) {
|
||||
_viewState.update {
|
||||
fun showNewGroupDialog() {
|
||||
_subscribeUiState.update {
|
||||
it.copy(
|
||||
newGroupDialogVisible = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun hideNewGroupDialog() {
|
||||
_subscribeUiState.update {
|
||||
it.copy(
|
||||
newGroupDialogVisible = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun switchPage(isSearchPage: Boolean) {
|
||||
_subscribeUiState.update {
|
||||
it.copy(
|
||||
isSearchPage = isSearchPage
|
||||
)
|
||||
|
@ -244,7 +236,7 @@ class SubscribeViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
data class SubscribeViewState(
|
||||
data class SubscribeUiState(
|
||||
val visible: Boolean = false,
|
||||
val title: String = "",
|
||||
val errorMessage: String = "",
|
||||
|
@ -260,42 +252,3 @@ data class SubscribeViewState(
|
|||
val groups: Flow<List<Group>> = emptyFlow(),
|
||||
val isSearchPage: Boolean = true,
|
||||
)
|
||||
|
||||
sealed class SubscribeViewAction {
|
||||
object Init : SubscribeViewAction()
|
||||
object Reset : SubscribeViewAction()
|
||||
|
||||
object Show : SubscribeViewAction()
|
||||
object Hide : SubscribeViewAction()
|
||||
|
||||
object ShowNewGroupDialog : SubscribeViewAction()
|
||||
object HideNewGroupDialog : SubscribeViewAction()
|
||||
object AddNewGroup : SubscribeViewAction()
|
||||
|
||||
data class SwitchPage(
|
||||
val isSearchPage: Boolean
|
||||
) : SubscribeViewAction()
|
||||
|
||||
data class ImportFromInputStream(
|
||||
val inputStream: InputStream
|
||||
) : SubscribeViewAction()
|
||||
|
||||
data class InputLink(
|
||||
val content: String
|
||||
) : SubscribeViewAction()
|
||||
|
||||
object Search : SubscribeViewAction()
|
||||
|
||||
object ChangeAllowNotificationPreset : SubscribeViewAction()
|
||||
object ChangeParseFullContentPreset : SubscribeViewAction()
|
||||
|
||||
data class SelectedGroup(
|
||||
val groupId: String
|
||||
) : SubscribeViewAction()
|
||||
|
||||
data class InputNewGroup(
|
||||
val content: String
|
||||
) : SubscribeViewAction()
|
||||
|
||||
object Subscribe : SubscribeViewAction()
|
||||
}
|
||||
|
|
|
@ -34,7 +34,6 @@ import me.ash.reader.ui.component.base.SwipeRefresh
|
|||
import me.ash.reader.ui.ext.collectAsStateValue
|
||||
import me.ash.reader.ui.page.common.RouteName
|
||||
import me.ash.reader.ui.page.home.FilterState
|
||||
import me.ash.reader.ui.page.home.HomeViewAction
|
||||
import me.ash.reader.ui.page.home.HomeViewModel
|
||||
|
||||
@OptIn(
|
||||
|
@ -47,8 +46,6 @@ fun FlowPage(
|
|||
flowViewModel: FlowViewModel = hiltViewModel(),
|
||||
homeViewModel: HomeViewModel,
|
||||
) {
|
||||
val homeViewView = homeViewModel.viewState.collectAsStateValue()
|
||||
val pagingItems = homeViewView.pagingData.collectAsLazyPagingItems()
|
||||
val keyboardController = LocalSoftwareKeyboardController.current
|
||||
val topBarTonalElevation = LocalFlowTopBarTonalElevation.current
|
||||
val articleListTonalElevation = LocalFlowArticleListTonalElevation.current
|
||||
|
@ -59,16 +56,18 @@ fun FlowPage(
|
|||
val filterBarPadding = LocalFlowFilterBarPadding.current
|
||||
val filterBarTonalElevation = LocalFlowFilterBarTonalElevation.current
|
||||
|
||||
val homeUiState = homeViewModel.homeUiState.collectAsStateValue()
|
||||
val flowUiState = flowViewModel.flowUiState.collectAsStateValue()
|
||||
val filterUiState = homeViewModel.filterUiState.collectAsStateValue()
|
||||
val pagingItems = homeUiState.pagingData.collectAsLazyPagingItems()
|
||||
val listState =
|
||||
if (pagingItems.itemCount > 0) flowUiState.listState else rememberLazyListState()
|
||||
|
||||
val scope = rememberCoroutineScope()
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
var markAsRead by remember { mutableStateOf(false) }
|
||||
var onSearch by remember { mutableStateOf(false) }
|
||||
|
||||
val viewState = flowViewModel.viewState.collectAsStateValue()
|
||||
val filterState = homeViewModel.filterState.collectAsStateValue()
|
||||
val homeViewState = homeViewModel.viewState.collectAsStateValue()
|
||||
val listState = if (pagingItems.itemCount > 0) viewState.listState else rememberLazyListState()
|
||||
|
||||
val owner = LocalLifecycleOwner.current
|
||||
var isSyncing by remember { mutableStateOf(false) }
|
||||
homeViewModel.syncWorkLiveData.observe(owner) {
|
||||
|
@ -82,15 +81,15 @@ fun FlowPage(
|
|||
focusRequester.requestFocus()
|
||||
} else {
|
||||
keyboardController?.hide()
|
||||
if (homeViewState.searchContent.isNotBlank()) {
|
||||
homeViewModel.dispatch(HomeViewAction.InputSearchContent(""))
|
||||
if (homeUiState.searchContent.isNotBlank()) {
|
||||
homeViewModel.inputSearchContent("")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(viewState.listState) {
|
||||
snapshotFlow { viewState.listState.firstVisibleItemIndex }.collect {
|
||||
LaunchedEffect(flowUiState.listState) {
|
||||
snapshotFlow { flowUiState.listState.firstVisibleItemIndex }.collect {
|
||||
if (it > 0) {
|
||||
keyboardController?.hide()
|
||||
}
|
||||
|
@ -122,7 +121,7 @@ fun FlowPage(
|
|||
},
|
||||
actions = {
|
||||
AnimatedVisibility(
|
||||
visible = !filterState.filter.isStarred(),
|
||||
visible = !filterUiState.filter.isStarred(),
|
||||
enter = fadeIn() + expandVertically(),
|
||||
exit = fadeOut() + shrinkVertically(),
|
||||
) {
|
||||
|
@ -136,7 +135,7 @@ fun FlowPage(
|
|||
},
|
||||
) {
|
||||
scope.launch {
|
||||
viewState.listState.scrollToItem(0)
|
||||
flowUiState.listState.scrollToItem(0)
|
||||
markAsRead = !markAsRead
|
||||
onSearch = false
|
||||
}
|
||||
|
@ -152,7 +151,7 @@ fun FlowPage(
|
|||
},
|
||||
) {
|
||||
scope.launch {
|
||||
viewState.listState.scrollToItem(0)
|
||||
flowUiState.listState.scrollToItem(0)
|
||||
onSearch = !onSearch
|
||||
}
|
||||
}
|
||||
|
@ -161,7 +160,7 @@ fun FlowPage(
|
|||
SwipeRefresh(
|
||||
onRefresh = {
|
||||
if (!isSyncing) {
|
||||
flowViewModel.dispatch(FlowViewAction.Sync)
|
||||
flowViewModel.sync()
|
||||
}
|
||||
}
|
||||
) {
|
||||
|
@ -170,7 +169,7 @@ fun FlowPage(
|
|||
state = listState,
|
||||
) {
|
||||
item {
|
||||
DisplayTextHeader(filterState, isSyncing, articleListFeedIcon.value)
|
||||
DisplayTextHeader(filterUiState, isSyncing, articleListFeedIcon.value)
|
||||
AnimatedVisibility(
|
||||
visible = markAsRead,
|
||||
enter = fadeIn() + expandVertically(),
|
||||
|
@ -186,13 +185,11 @@ fun FlowPage(
|
|||
},
|
||||
) {
|
||||
markAsRead = false
|
||||
flowViewModel.dispatch(
|
||||
FlowViewAction.MarkAsRead(
|
||||
groupId = filterState.group?.id,
|
||||
feedId = filterState.feed?.id,
|
||||
articleId = null,
|
||||
markAsReadBefore = it,
|
||||
)
|
||||
flowViewModel.markAsRead(
|
||||
groupId = filterUiState.group?.id,
|
||||
feedId = filterUiState.feed?.id,
|
||||
articleId = null,
|
||||
markAsReadBefore = it,
|
||||
)
|
||||
}
|
||||
AnimatedVisibility(
|
||||
|
@ -201,30 +198,30 @@ fun FlowPage(
|
|||
exit = fadeOut() + shrinkVertically(),
|
||||
) {
|
||||
SearchBar(
|
||||
value = homeViewState.searchContent,
|
||||
value = homeUiState.searchContent,
|
||||
placeholder = when {
|
||||
filterState.group != null -> stringResource(
|
||||
filterUiState.group != null -> stringResource(
|
||||
R.string.search_for_in,
|
||||
filterState.filter.getName(),
|
||||
filterState.group.name
|
||||
filterUiState.filter.getName(),
|
||||
filterUiState.group.name
|
||||
)
|
||||
filterState.feed != null -> stringResource(
|
||||
filterUiState.feed != null -> stringResource(
|
||||
R.string.search_for_in,
|
||||
filterState.filter.getName(),
|
||||
filterState.feed.name
|
||||
filterUiState.filter.getName(),
|
||||
filterUiState.feed.name
|
||||
)
|
||||
else -> stringResource(
|
||||
R.string.search_for,
|
||||
filterState.filter.getName()
|
||||
filterUiState.filter.getName()
|
||||
)
|
||||
},
|
||||
focusRequester = focusRequester,
|
||||
onValueChange = {
|
||||
homeViewModel.dispatch(HomeViewAction.InputSearchContent(it))
|
||||
homeViewModel.inputSearchContent(it)
|
||||
},
|
||||
onClose = {
|
||||
onSearch = false
|
||||
homeViewModel.dispatch(HomeViewAction.InputSearchContent(""))
|
||||
homeViewModel.inputSearchContent("")
|
||||
}
|
||||
)
|
||||
Spacer(modifier = Modifier.height((56 + 24 + 10).dp))
|
||||
|
@ -253,15 +250,15 @@ fun FlowPage(
|
|||
},
|
||||
bottomBar = {
|
||||
FilterBar(
|
||||
filter = filterState.filter,
|
||||
filter = filterUiState.filter,
|
||||
filterBarStyle = filterBarStyle.value,
|
||||
filterBarFilled = filterBarFilled.value,
|
||||
filterBarPadding = filterBarPadding.dp,
|
||||
filterBarTonalElevation = filterBarTonalElevation.value.dp,
|
||||
) {
|
||||
flowViewModel.dispatch(FlowViewAction.ScrollToItem(0))
|
||||
homeViewModel.dispatch(HomeViewAction.ChangeFilter(filterState.copy(filter = it)))
|
||||
homeViewModel.dispatch(HomeViewAction.FetchArticles)
|
||||
flowViewModel.scrollToItem(0)
|
||||
homeViewModel.changeFilter(filterUiState.copy(filter = it))
|
||||
homeViewModel.fetchArticles()
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
|
@ -7,7 +7,6 @@ import dagger.hilt.android.lifecycle.HiltViewModel
|
|||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import me.ash.reader.data.entity.ArticleWithFeed
|
||||
import me.ash.reader.data.repository.RssRepository
|
||||
|
@ -19,40 +18,20 @@ import javax.inject.Inject
|
|||
class FlowViewModel @Inject constructor(
|
||||
private val rssRepository: RssRepository,
|
||||
) : ViewModel() {
|
||||
private val _viewState = MutableStateFlow(ArticleViewState())
|
||||
val viewState: StateFlow<ArticleViewState> = _viewState.asStateFlow()
|
||||
private val _flowUiState = MutableStateFlow(FlowUiState())
|
||||
val flowUiState: StateFlow<FlowUiState> = _flowUiState.asStateFlow()
|
||||
|
||||
fun dispatch(action: FlowViewAction) {
|
||||
when (action) {
|
||||
is FlowViewAction.Sync -> sync()
|
||||
is FlowViewAction.ChangeIsBack -> changeIsBack(action.isBack)
|
||||
is FlowViewAction.ScrollToItem -> scrollToItem(action.index)
|
||||
is FlowViewAction.MarkAsRead -> markAsRead(
|
||||
action.groupId,
|
||||
action.feedId,
|
||||
action.articleId,
|
||||
action.markAsReadBefore,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun sync() {
|
||||
fun sync() {
|
||||
rssRepository.get().doSync()
|
||||
}
|
||||
|
||||
private fun scrollToItem(index: Int) {
|
||||
fun scrollToItem(index: Int) {
|
||||
viewModelScope.launch {
|
||||
_viewState.value.listState.scrollToItem(index)
|
||||
_flowUiState.value.listState.scrollToItem(index)
|
||||
}
|
||||
}
|
||||
|
||||
private fun changeIsBack(isBack: Boolean) {
|
||||
_viewState.update {
|
||||
it.copy(isBack = isBack)
|
||||
}
|
||||
}
|
||||
|
||||
private fun markAsRead(
|
||||
fun markAsRead(
|
||||
groupId: String?,
|
||||
feedId: String?,
|
||||
articleId: String?,
|
||||
|
@ -84,32 +63,13 @@ class FlowViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
data class ArticleViewState(
|
||||
data class FlowUiState(
|
||||
val filterImportant: Int = 0,
|
||||
val listState: LazyListState = LazyListState(),
|
||||
val isBack: Boolean = false,
|
||||
val syncWorkInfo: String = "",
|
||||
)
|
||||
|
||||
sealed class FlowViewAction {
|
||||
object Sync : FlowViewAction()
|
||||
|
||||
data class ChangeIsBack(
|
||||
val isBack: Boolean
|
||||
) : FlowViewAction()
|
||||
|
||||
data class ScrollToItem(
|
||||
val index: Int
|
||||
) : FlowViewAction()
|
||||
|
||||
data class MarkAsRead(
|
||||
val groupId: String?,
|
||||
val feedId: String?,
|
||||
val articleId: String?,
|
||||
val markAsReadBefore: MarkAsReadBefore
|
||||
) : FlowViewAction()
|
||||
}
|
||||
|
||||
enum class MarkAsReadBefore {
|
||||
SevenDays,
|
||||
ThreeDays,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package me.ash.reader.ui.page.home.read
|
||||
package me.ash.reader.ui.page.home.reading
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
|
@ -1,4 +1,4 @@
|
|||
package me.ash.reader.ui.page.home.read
|
||||
package me.ash.reader.ui.page.home.reading
|
||||
|
||||
import android.view.HapticFeedbackConstants
|
||||
import androidx.compose.foundation.background
|
|
@ -1,4 +1,4 @@
|
|||
package me.ash.reader.ui.page.home.read
|
||||
package me.ash.reader.ui.page.home.reading
|
||||
|
||||
import android.content.Intent
|
||||
import android.util.Log
|
||||
|
@ -32,26 +32,26 @@ import me.ash.reader.ui.ext.collectAsStateValue
|
|||
import me.ash.reader.ui.ext.drawVerticalScrollbar
|
||||
|
||||
@Composable
|
||||
fun ReadPage(
|
||||
fun ReadingPage(
|
||||
navController: NavHostController,
|
||||
readViewModel: ReadViewModel = hiltViewModel(),
|
||||
readingViewModel: ReadingViewModel = hiltViewModel(),
|
||||
) {
|
||||
val viewState = readViewModel.viewState.collectAsStateValue()
|
||||
val isScrollDown = viewState.listState.isScrollDown()
|
||||
val readingUiState = readingViewModel.readingUiState.collectAsStateValue()
|
||||
val isScrollDown = readingUiState.listState.isScrollDown()
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
navController.currentBackStackEntryFlow.collect {
|
||||
it.arguments?.getString("articleId")?.let {
|
||||
readViewModel.dispatch(ReadViewAction.InitData(it))
|
||||
readingViewModel.initData(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(viewState.articleWithFeed?.article?.id) {
|
||||
Log.i("RLog", "ReadPage: ${viewState.articleWithFeed}")
|
||||
viewState.articleWithFeed?.let {
|
||||
LaunchedEffect(readingUiState.articleWithFeed?.article?.id) {
|
||||
Log.i("RLog", "ReadPage: ${readingUiState.articleWithFeed}")
|
||||
readingUiState.articleWithFeed?.let {
|
||||
if (it.article.isUnread) {
|
||||
readViewModel.dispatch(ReadViewAction.MarkUnread(false))
|
||||
readingViewModel.markUnread(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -66,19 +66,19 @@ fun ReadPage(
|
|||
contentAlignment = Alignment.TopCenter
|
||||
) {
|
||||
TopBar(
|
||||
isShow = viewState.articleWithFeed == null || !isScrollDown,
|
||||
title = viewState.articleWithFeed?.article?.title,
|
||||
link = viewState.articleWithFeed?.article?.link,
|
||||
isShow = readingUiState.articleWithFeed == null || !isScrollDown,
|
||||
title = readingUiState.articleWithFeed?.article?.title,
|
||||
link = readingUiState.articleWithFeed?.article?.link,
|
||||
onClose = {
|
||||
navController.popBackStack()
|
||||
},
|
||||
)
|
||||
}
|
||||
Content(
|
||||
content = viewState.content ?: "",
|
||||
articleWithFeed = viewState.articleWithFeed,
|
||||
isLoading = viewState.isLoading,
|
||||
listState = viewState.listState,
|
||||
content = readingUiState.content ?: "",
|
||||
articleWithFeed = readingUiState.articleWithFeed,
|
||||
isLoading = readingUiState.isLoading,
|
||||
listState = readingUiState.listState,
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
|
@ -87,17 +87,17 @@ fun ReadPage(
|
|||
contentAlignment = Alignment.BottomCenter
|
||||
) {
|
||||
BottomBar(
|
||||
isShow = viewState.articleWithFeed != null && !isScrollDown,
|
||||
articleWithFeed = viewState.articleWithFeed,
|
||||
isShow = readingUiState.articleWithFeed != null && !isScrollDown,
|
||||
articleWithFeed = readingUiState.articleWithFeed,
|
||||
unreadOnClick = {
|
||||
readViewModel.dispatch(ReadViewAction.MarkUnread(it))
|
||||
readingViewModel.markUnread(it)
|
||||
},
|
||||
starredOnClick = {
|
||||
readViewModel.dispatch(ReadViewAction.MarkStarred(it))
|
||||
readingViewModel.markStarred(it)
|
||||
},
|
||||
fullContentOnClick = { afterIsFullContent ->
|
||||
if (afterIsFullContent) readViewModel.dispatch(ReadViewAction.RenderFullContent)
|
||||
else readViewModel.dispatch(ReadViewAction.RenderDescriptionContent)
|
||||
if (afterIsFullContent) readingViewModel.renderFullContent()
|
||||
else readingViewModel.renderDescriptionContent()
|
||||
},
|
||||
)
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package me.ash.reader.ui.page.home.read
|
||||
package me.ash.reader.ui.page.home.reading
|
||||
|
||||
import android.util.Log
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
|
@ -17,41 +17,29 @@ import me.ash.reader.data.repository.RssRepository
|
|||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class ReadViewModel @Inject constructor(
|
||||
class ReadingViewModel @Inject constructor(
|
||||
val rssRepository: RssRepository,
|
||||
private val rssHelper: RssHelper,
|
||||
) : ViewModel() {
|
||||
private val _viewState = MutableStateFlow(ReadViewState())
|
||||
val viewState: StateFlow<ReadViewState> = _viewState.asStateFlow()
|
||||
private val _readingUiState = MutableStateFlow(ReadingUiState())
|
||||
val readingUiState: StateFlow<ReadingUiState> = _readingUiState.asStateFlow()
|
||||
|
||||
fun dispatch(action: ReadViewAction) {
|
||||
when (action) {
|
||||
is ReadViewAction.InitData -> bindArticleWithFeed(action.articleId)
|
||||
is ReadViewAction.RenderDescriptionContent -> renderDescriptionContent()
|
||||
is ReadViewAction.RenderFullContent -> renderFullContent()
|
||||
is ReadViewAction.MarkUnread -> markUnread(action.isUnread)
|
||||
is ReadViewAction.MarkStarred -> markStarred(action.isStarred)
|
||||
is ReadViewAction.ClearArticle -> clearArticle()
|
||||
is ReadViewAction.ChangeLoading -> changeLoading(action.isLoading)
|
||||
}
|
||||
}
|
||||
|
||||
private fun bindArticleWithFeed(articleId: String) {
|
||||
changeLoading(true)
|
||||
fun initData(articleId: String) {
|
||||
showLoading()
|
||||
viewModelScope.launch {
|
||||
_viewState.update {
|
||||
_readingUiState.update {
|
||||
it.copy(articleWithFeed = rssRepository.get().findArticleById(articleId))
|
||||
}
|
||||
_viewState.value.articleWithFeed?.let {
|
||||
_readingUiState.value.articleWithFeed?.let {
|
||||
if (it.feed.isFullContent) internalRenderFullContent()
|
||||
else renderDescriptionContent()
|
||||
}
|
||||
changeLoading(false)
|
||||
hideLoading()
|
||||
}
|
||||
}
|
||||
|
||||
private fun renderDescriptionContent() {
|
||||
_viewState.update {
|
||||
fun renderDescriptionContent() {
|
||||
_readingUiState.update {
|
||||
it.copy(
|
||||
content = it.articleWithFeed?.article?.fullContent
|
||||
?: it.articleWithFeed?.article?.rawDescription ?: "",
|
||||
|
@ -59,38 +47,38 @@ class ReadViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun renderFullContent() {
|
||||
fun renderFullContent() {
|
||||
viewModelScope.launch {
|
||||
internalRenderFullContent()
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun internalRenderFullContent() {
|
||||
changeLoading(true)
|
||||
suspend fun internalRenderFullContent() {
|
||||
showLoading()
|
||||
try {
|
||||
_viewState.update {
|
||||
_readingUiState.update {
|
||||
it.copy(
|
||||
content = rssHelper.parseFullContent(
|
||||
_viewState.value.articleWithFeed?.article?.link ?: "",
|
||||
_viewState.value.articleWithFeed?.article?.title ?: ""
|
||||
_readingUiState.value.articleWithFeed?.article?.link ?: "",
|
||||
_readingUiState.value.articleWithFeed?.article?.title ?: ""
|
||||
)
|
||||
)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.i("RLog", "renderFullContent: ${e.message}")
|
||||
_viewState.update {
|
||||
_readingUiState.update {
|
||||
it.copy(
|
||||
content = e.message
|
||||
)
|
||||
}
|
||||
}
|
||||
changeLoading(false)
|
||||
hideLoading()
|
||||
}
|
||||
|
||||
private fun markUnread(isUnread: Boolean) {
|
||||
val articleWithFeed = _viewState.value.articleWithFeed ?: return
|
||||
fun markUnread(isUnread: Boolean) {
|
||||
val articleWithFeed = _readingUiState.value.articleWithFeed ?: return
|
||||
viewModelScope.launch {
|
||||
_viewState.update {
|
||||
_readingUiState.update {
|
||||
it.copy(
|
||||
articleWithFeed = articleWithFeed.copy(
|
||||
article = articleWithFeed.article.copy(
|
||||
|
@ -102,17 +90,17 @@ class ReadViewModel @Inject constructor(
|
|||
rssRepository.get().markAsRead(
|
||||
groupId = null,
|
||||
feedId = null,
|
||||
articleId = _viewState.value.articleWithFeed!!.article.id,
|
||||
articleId = _readingUiState.value.articleWithFeed!!.article.id,
|
||||
before = null,
|
||||
isUnread = isUnread,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun markStarred(isStarred: Boolean) {
|
||||
val articleWithFeed = _viewState.value.articleWithFeed ?: return
|
||||
fun markStarred(isStarred: Boolean) {
|
||||
val articleWithFeed = _readingUiState.value.articleWithFeed ?: return
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
_viewState.update {
|
||||
_readingUiState.update {
|
||||
it.copy(
|
||||
articleWithFeed = articleWithFeed.copy(
|
||||
article = articleWithFeed.article.copy(
|
||||
|
@ -129,47 +117,22 @@ class ReadViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun clearArticle() {
|
||||
_viewState.update {
|
||||
it.copy(articleWithFeed = null)
|
||||
private fun showLoading() {
|
||||
_readingUiState.update {
|
||||
it.copy(isLoading = true)
|
||||
}
|
||||
}
|
||||
|
||||
private fun changeLoading(isLoading: Boolean) {
|
||||
_viewState.update {
|
||||
it.copy(isLoading = isLoading)
|
||||
private fun hideLoading() {
|
||||
_readingUiState.update {
|
||||
it.copy(isLoading = false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class ReadViewState(
|
||||
data class ReadingUiState(
|
||||
val articleWithFeed: ArticleWithFeed? = null,
|
||||
val content: String? = null,
|
||||
val isLoading: Boolean = true,
|
||||
// val scrollState: ScrollState = ScrollState(0),
|
||||
val listState: LazyListState = LazyListState(),
|
||||
)
|
||||
|
||||
sealed class ReadViewAction {
|
||||
data class InitData(
|
||||
val articleId: String,
|
||||
) : ReadViewAction()
|
||||
|
||||
object RenderDescriptionContent : ReadViewAction()
|
||||
|
||||
object RenderFullContent : ReadViewAction()
|
||||
|
||||
data class MarkUnread(
|
||||
val isUnread: Boolean,
|
||||
) : ReadViewAction()
|
||||
|
||||
data class MarkStarred(
|
||||
val isStarred: Boolean,
|
||||
) : ReadViewAction()
|
||||
|
||||
object ClearArticle : ReadViewAction()
|
||||
|
||||
data class ChangeLoading(
|
||||
val isLoading: Boolean
|
||||
) : ReadViewAction()
|
||||
}
|
|
@ -28,7 +28,6 @@ import me.ash.reader.ui.component.base.FeedbackIconButton
|
|||
import me.ash.reader.ui.ext.getCurrentVersion
|
||||
import me.ash.reader.ui.page.common.RouteName
|
||||
import me.ash.reader.ui.page.settings.tips.UpdateDialog
|
||||
import me.ash.reader.ui.page.settings.tips.UpdateViewAction
|
||||
import me.ash.reader.ui.page.settings.tips.UpdateViewModel
|
||||
import me.ash.reader.ui.theme.palette.onLight
|
||||
|
||||
|
@ -76,7 +75,7 @@ fun SettingsPage(
|
|||
)
|
||||
},
|
||||
) {
|
||||
updateViewModel.dispatch(UpdateViewAction.Show)
|
||||
updateViewModel.showDialog()
|
||||
}
|
||||
}
|
||||
Banner(
|
||||
|
|
|
@ -103,23 +103,21 @@ fun TipsAndSupportPage(
|
|||
onTap = {
|
||||
if (System.currentTimeMillis() - clickTime > 2000) {
|
||||
clickTime = System.currentTimeMillis()
|
||||
updateViewModel.dispatch(
|
||||
UpdateViewAction.CheckUpdate(
|
||||
{
|
||||
context.showToast(context.getString(R.string.checking_updates))
|
||||
context.dataStore.put(
|
||||
DataStoreKeys.SkipVersionNumber,
|
||||
""
|
||||
updateViewModel.checkUpdate(
|
||||
{
|
||||
context.showToast(context.getString(R.string.checking_updates))
|
||||
context.dataStore.put(
|
||||
DataStoreKeys.SkipVersionNumber,
|
||||
""
|
||||
)
|
||||
},
|
||||
{
|
||||
if (!it) {
|
||||
context.showToast(
|
||||
context.getString(R.string.is_latest_version)
|
||||
)
|
||||
},
|
||||
{
|
||||
if (!it) {
|
||||
context.showToast(
|
||||
context.getString(R.string.is_latest_version)
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
} else {
|
||||
clickTime = System.currentTimeMillis()
|
||||
|
|
|
@ -42,8 +42,8 @@ fun UpdateDialog(
|
|||
updateViewModel: UpdateViewModel = hiltViewModel(),
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val viewState = updateViewModel.viewState.collectAsStateValue()
|
||||
val downloadState = viewState.downloadFlow.collectAsState(initial = Download.NotYet).value
|
||||
val updateUiState = updateViewModel.updateUiState.collectAsStateValue()
|
||||
val downloadState = updateUiState.downloadFlow.collectAsState(initial = Download.NotYet).value
|
||||
val scope = rememberCoroutineScope { Dispatchers.IO }
|
||||
val newVersionNumber = LocalNewVersionNumber.current
|
||||
val newVersionPublishDate = LocalNewVersionPublishDate.current
|
||||
|
@ -73,8 +73,8 @@ fun UpdateDialog(
|
|||
|
||||
Dialog(
|
||||
modifier = Modifier.heightIn(max = 400.dp),
|
||||
visible = viewState.updateDialogVisible,
|
||||
onDismissRequest = { updateViewModel.dispatch(UpdateViewAction.Hide) },
|
||||
visible = updateUiState.updateDialogVisible,
|
||||
onDismissRequest = { updateViewModel.hideDialog() },
|
||||
icon = {
|
||||
Icon(
|
||||
imageVector = Icons.Rounded.Update,
|
||||
|
@ -147,7 +147,7 @@ fun UpdateDialog(
|
|||
TextButton(
|
||||
onClick = {
|
||||
SkipVersionNumberPreference.put(context, scope, newVersionNumber.toString())
|
||||
updateViewModel.dispatch(UpdateViewAction.Hide)
|
||||
updateViewModel.hideDialog()
|
||||
}
|
||||
) {
|
||||
Text(text = stringResource(R.string.skip_this_version))
|
||||
|
|
|
@ -14,22 +14,10 @@ import javax.inject.Inject
|
|||
class UpdateViewModel @Inject constructor(
|
||||
private val appRepository: AppRepository,
|
||||
) : ViewModel() {
|
||||
private val _viewState = MutableStateFlow(UpdateViewState())
|
||||
val viewState: StateFlow<UpdateViewState> = _viewState.asStateFlow()
|
||||
private val _updateUiState = MutableStateFlow(UpdateUiState())
|
||||
val updateUiState: StateFlow<UpdateUiState> = _updateUiState.asStateFlow()
|
||||
|
||||
fun dispatch(action: UpdateViewAction) {
|
||||
when (action) {
|
||||
is UpdateViewAction.Show -> changeUpdateDialogVisible(true)
|
||||
is UpdateViewAction.Hide -> changeUpdateDialogVisible(false)
|
||||
is UpdateViewAction.CheckUpdate -> checkUpdate(
|
||||
action.preProcessor,
|
||||
action.postProcessor
|
||||
)
|
||||
is UpdateViewAction.DownloadUpdate -> downloadUpdate(action.url)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkUpdate(
|
||||
fun checkUpdate(
|
||||
preProcessor: suspend () -> Unit = {},
|
||||
postProcessor: suspend (Boolean) -> Unit = {}
|
||||
) {
|
||||
|
@ -38,7 +26,11 @@ class UpdateViewModel @Inject constructor(
|
|||
preProcessor()
|
||||
appRepository.checkUpdate().let {
|
||||
it?.let {
|
||||
changeUpdateDialogVisible(it)
|
||||
if (it) {
|
||||
showDialog()
|
||||
} else {
|
||||
hideDialog()
|
||||
}
|
||||
postProcessor(it)
|
||||
}
|
||||
}
|
||||
|
@ -46,22 +38,30 @@ class UpdateViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun changeUpdateDialogVisible(visible: Boolean) {
|
||||
_viewState.update {
|
||||
fun showDialog() {
|
||||
_updateUiState.update {
|
||||
it.copy(
|
||||
updateDialogVisible = visible
|
||||
updateDialogVisible = true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun downloadUpdate(url: String) {
|
||||
fun hideDialog() {
|
||||
_updateUiState.update {
|
||||
it.copy(
|
||||
updateDialogVisible = false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun downloadUpdate(url: String) {
|
||||
viewModelScope.launch {
|
||||
_viewState.update {
|
||||
_updateUiState.update {
|
||||
it.copy(
|
||||
downloadFlow = flow { emit(Download.Progress(0)) }
|
||||
)
|
||||
}
|
||||
_viewState.update {
|
||||
_updateUiState.update {
|
||||
it.copy(
|
||||
downloadFlow = appRepository.downloadFile(url)
|
||||
)
|
||||
|
@ -70,21 +70,7 @@ class UpdateViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
data class UpdateViewState(
|
||||
data class UpdateUiState(
|
||||
val updateDialogVisible: Boolean = false,
|
||||
val downloadFlow: Flow<Download> = emptyFlow(),
|
||||
)
|
||||
|
||||
sealed class UpdateViewAction {
|
||||
object Show : UpdateViewAction()
|
||||
object Hide : UpdateViewAction()
|
||||
|
||||
data class CheckUpdate(
|
||||
val preProcessor: suspend () -> Unit = {},
|
||||
val postProcessor: suspend (Boolean) -> Unit = {}
|
||||
) : UpdateViewAction()
|
||||
|
||||
data class DownloadUpdate(
|
||||
val url: String,
|
||||
) : UpdateViewAction()
|
||||
}
|
Loading…
Reference in New Issue
Block a user