Improve SubscribeDialog

This commit is contained in:
Ash 2022-04-03 01:52:56 +08:00
parent 31a8445df2
commit 54506e5019
6 changed files with 127 additions and 181 deletions

View File

@ -21,7 +21,7 @@ import me.ash.reader.ui.component.BottomDrawer
import me.ash.reader.ui.component.Subtitle
import me.ash.reader.ui.ext.collectAsStateValue
import me.ash.reader.ui.ext.roundClick
import me.ash.reader.ui.page.home.feeds.subscribe.ResultViewPage
import me.ash.reader.ui.page.home.feeds.subscribe.ResultView
@OptIn(ExperimentalMaterialApi::class)
@Composable
@ -60,7 +60,7 @@ fun FeedOptionDrawer(
overflow = TextOverflow.Ellipsis,
)
Spacer(modifier = modifier.height(16.dp))
ResultViewPage(
ResultView(
link = feed?.url ?: stringResource(R.string.unknown),
groups = viewState.groups,
selectedAllowNotificationPreset = viewState.feed?.isNotification ?: false,

View File

@ -29,7 +29,7 @@ import me.ash.reader.ui.component.Subtitle
import me.ash.reader.ui.ext.roundClick
@Composable
fun ResultViewPage(
fun ResultView(
modifier: Modifier = Modifier,
link: String = "",
groups: List<Group> = emptyList(),

View File

@ -30,16 +30,12 @@ import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.unit.dp
import com.google.accompanist.pager.ExperimentalPagerApi
import com.google.accompanist.pager.PagerState
import kotlinx.coroutines.delay
import me.ash.reader.R
@OptIn(ExperimentalPagerApi::class)
@Composable
fun SearchViewPage(
fun SearchView(
modifier: Modifier = Modifier,
pagerState: PagerState,
readOnly: Boolean = false,
inputLink: String = "",
errorMessage: String = "",

View File

@ -2,7 +2,7 @@ package me.ash.reader.ui.page.home.feeds.subscribe
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.height
import androidx.compose.animation.*
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.RssFeed
@ -10,22 +10,25 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.DialogProperties
import androidx.hilt.navigation.compose.hiltViewModel
import com.google.accompanist.pager.ExperimentalPagerApi
import me.ash.reader.R
import me.ash.reader.ui.component.Dialog
import me.ash.reader.ui.ext.*
@OptIn(ExperimentalPagerApi::class, androidx.compose.ui.ExperimentalComposeUiApi::class)
@OptIn(
androidx.compose.ui.ExperimentalComposeUiApi::class,
ExperimentalAnimationApi::class
)
@Composable
fun SubscribeDialog(
modifier: Modifier = Modifier,
@ -33,10 +36,8 @@ fun SubscribeDialog(
) {
val context = LocalContext.current
val focusManager = LocalFocusManager.current
val scope = rememberCoroutineScope()
val viewState = subscribeViewModel.viewState.collectAsStateValue()
val groupsState = viewState.groups.collectAsState(initial = emptyList())
var dialogHeight by remember { mutableStateOf(300.dp) }
val launcher = rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) {
it?.let { uri ->
context.contentResolver.openInputStream(uri)?.let { inputStream ->
@ -56,22 +57,12 @@ fun SubscribeDialog(
subscribeViewModel.dispatch(SubscribeViewAction.Init)
} else {
subscribeViewModel.dispatch(SubscribeViewAction.Reset)
viewState.pagerState.scrollToPage(0)
}
}
LaunchedEffect(viewState.pagerState.currentPage) {
focusManager.clearFocus()
when (viewState.pagerState.currentPage) {
0 -> dialogHeight = 300.dp
1 -> dialogHeight = Dp.Unspecified
subscribeViewModel.dispatch(SubscribeViewAction.SwitchPage(true))
}
}
Dialog(
modifier = Modifier
.padding(horizontal = 44.dp)
.height(dialogHeight),
modifier = Modifier.padding(horizontal = 44.dp),
visible = viewState.visible,
properties = DialogProperties(usePlatformDefaultWidth = false),
onDismissRequest = {
@ -86,97 +77,113 @@ fun SubscribeDialog(
},
title = {
Text(
when (viewState.pagerState.currentPage) {
0 -> viewState.title
else -> viewState.feed?.name ?: stringResource(R.string.unknown)
if (viewState.isSearchPage) {
viewState.title
} else {
viewState.feed?.name ?: stringResource(R.string.unknown)
}
)
},
text = {
SubscribeViewPager(
viewState = viewState,
onLinkValueChange = {
subscribeViewModel.dispatch(SubscribeViewAction.InputLink(it))
},
onSearchKeyboardAction = {
subscribeViewModel.dispatch(SubscribeViewAction.Search(scope))
},
groups = groupsState.value,
onNewGroupValueChange = {
subscribeViewModel.dispatch(SubscribeViewAction.InputNewGroup(it))
},
changeNewGroupSelected = {
subscribeViewModel.dispatch(SubscribeViewAction.SelectedNewGroup(it))
},
allowNotificationPresetOnClick = {
subscribeViewModel.dispatch(SubscribeViewAction.ChangeAllowNotificationPreset)
},
parseFullContentPresetOnClick = {
subscribeViewModel.dispatch(SubscribeViewAction.ChangeParseFullContentPreset)
},
onGroupClick = {
subscribeViewModel.dispatch(SubscribeViewAction.SelectedGroup(it))
},
onResultKeyboardAction = {
subscribeViewModel.dispatch(SubscribeViewAction.Subscribe)
},
)
AnimatedContent(
targetState = viewState.isSearchPage,
transitionSpec = {
slideInHorizontally { width -> width } + fadeIn() with
slideOutHorizontally { width -> -width } + fadeOut()
}
) { targetExpanded ->
if (targetExpanded) {
SearchView(
readOnly = viewState.lockLinkInput,
inputLink = viewState.linkContent,
errorMessage = viewState.errorMessage,
onLinkValueChange = {
subscribeViewModel.dispatch(SubscribeViewAction.InputLink(it))
},
onKeyboardAction = {
subscribeViewModel.dispatch(SubscribeViewAction.Search)
},
)
} else {
ResultView(
link = viewState.linkContent,
groups = groupsState.value,
selectedAllowNotificationPreset = viewState.allowNotificationPreset,
selectedParseFullContentPreset = viewState.parseFullContentPreset,
selectedGroupId = viewState.selectedGroupId,
newGroupContent = viewState.newGroupContent,
onNewGroupValueChange = {
subscribeViewModel.dispatch(SubscribeViewAction.InputNewGroup(it))
},
newGroupSelected = viewState.newGroupSelected,
changeNewGroupSelected = {
subscribeViewModel.dispatch(SubscribeViewAction.SelectedNewGroup(it))
},
allowNotificationPresetOnClick = {
subscribeViewModel.dispatch(SubscribeViewAction.ChangeAllowNotificationPreset)
},
parseFullContentPresetOnClick = {
subscribeViewModel.dispatch(SubscribeViewAction.ChangeParseFullContentPreset)
},
onGroupClick = {
subscribeViewModel.dispatch(SubscribeViewAction.SelectedGroup(it))
},
onKeyboardAction = {
subscribeViewModel.dispatch(SubscribeViewAction.Subscribe)
},
)
}
}
},
confirmButton = {
when (viewState.pagerState.currentPage) {
0 -> {
TextButton(
enabled = viewState.linkContent.isNotEmpty()
&& viewState.title != stringResource(R.string.searching),
onClick = {
focusManager.clearFocus()
subscribeViewModel.dispatch(SubscribeViewAction.Search(scope))
}
) {
Text(
text = stringResource(R.string.search),
color = if (viewState.linkContent.isNotEmpty()) {
Color.Unspecified
} else {
MaterialTheme.colorScheme.outline.copy(alpha = 0.7f)
}
)
if (viewState.isSearchPage) {
TextButton(
enabled = viewState.linkContent.isNotEmpty()
&& viewState.title != stringResource(R.string.searching),
onClick = {
focusManager.clearFocus()
subscribeViewModel.dispatch(SubscribeViewAction.Search)
}
) {
Text(
text = stringResource(R.string.search),
color = if (viewState.linkContent.isNotEmpty()) {
Color.Unspecified
} else {
MaterialTheme.colorScheme.outline.copy(alpha = 0.7f)
}
)
}
1 -> {
TextButton(
onClick = {
focusManager.clearFocus()
subscribeViewModel.dispatch(SubscribeViewAction.Subscribe)
}
) {
Text(stringResource(R.string.subscribe))
} else {
TextButton(
onClick = {
focusManager.clearFocus()
subscribeViewModel.dispatch(SubscribeViewAction.Subscribe)
}
) {
Text(stringResource(R.string.subscribe))
}
}
},
dismissButton = {
when (viewState.pagerState.currentPage) {
0 -> {
TextButton(
onClick = {
focusManager.clearFocus()
launcher.launch("*/*")
subscribeViewModel.dispatch(SubscribeViewAction.Hide)
}
) {
Text(text = stringResource(R.string.import_from_opml))
if (viewState.isSearchPage) {
TextButton(
onClick = {
focusManager.clearFocus()
launcher.launch("*/*")
subscribeViewModel.dispatch(SubscribeViewAction.Hide)
}
) {
Text(text = stringResource(R.string.import_from_opml))
}
1 -> {
TextButton(
onClick = {
focusManager.clearFocus()
subscribeViewModel.dispatch(SubscribeViewAction.Hide)
}
) {
Text(text = stringResource(R.string.cancel))
} else {
TextButton(
onClick = {
focusManager.clearFocus()
subscribeViewModel.dispatch(SubscribeViewAction.Hide)
}
) {
Text(text = stringResource(R.string.cancel))
}
}
},

View File

@ -4,10 +4,12 @@ import android.util.Log
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.google.accompanist.pager.ExperimentalPagerApi
import com.google.accompanist.pager.PagerState
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import me.ash.reader.R
import me.ash.reader.data.entity.Article
import me.ash.reader.data.entity.Feed
@ -16,7 +18,6 @@ import me.ash.reader.data.repository.OpmlRepository
import me.ash.reader.data.repository.RssHelper
import me.ash.reader.data.repository.RssRepository
import me.ash.reader.data.repository.StringsRepository
import me.ash.reader.ui.ext.animateScrollToPage
import me.ash.reader.ui.ext.formatUrl
import java.io.InputStream
import javax.inject.Inject
@ -39,9 +40,10 @@ class SubscribeViewModel @Inject constructor(
is SubscribeViewAction.Reset -> reset()
is SubscribeViewAction.Show -> changeVisible(true)
is SubscribeViewAction.Hide -> changeVisible(false)
is SubscribeViewAction.SwitchPage -> switchPage(action.isSearchPage)
is SubscribeViewAction.ImportFromInputStream -> importFromInputStream(action.inputStream)
is SubscribeViewAction.InputLink -> inputLink(action.content)
is SubscribeViewAction.Search -> search(action.scope)
is SubscribeViewAction.Search -> search()
is SubscribeViewAction.ChangeAllowNotificationPreset ->
changeAllowNotificationPreset()
is SubscribeViewAction.ChangeParseFullContentPreset ->
@ -140,7 +142,7 @@ class SubscribeViewModel @Inject constructor(
}
}
private fun search(scope: CoroutineScope) {
private fun search() {
searchJob?.cancel()
viewModelScope.launch(Dispatchers.IO) {
try {
@ -181,7 +183,7 @@ class SubscribeViewModel @Inject constructor(
articles = feedWithArticle.articles,
)
}
_viewState.value.pagerState.animateScrollToPage(scope, 1)
switchPage(false)
} catch (e: Exception) {
e.printStackTrace()
_viewState.update {
@ -221,6 +223,14 @@ class SubscribeViewModel @Inject constructor(
)
}
}
private fun switchPage(isSearchPage: Boolean) {
_viewState.update {
it.copy(
isSearchPage = isSearchPage
)
}
}
}
@OptIn(ExperimentalPagerApi::class)
@ -238,7 +248,7 @@ data class SubscribeViewState(
val newGroupContent: String = "",
val newGroupSelected: Boolean = false,
val groups: Flow<List<Group>> = emptyFlow(),
val pagerState: PagerState = PagerState(),
val isSearchPage: Boolean = true,
)
sealed class SubscribeViewAction {
@ -248,6 +258,10 @@ sealed class SubscribeViewAction {
object Show : SubscribeViewAction()
object Hide : SubscribeViewAction()
data class SwitchPage(
val isSearchPage: Boolean
) : SubscribeViewAction()
data class ImportFromInputStream(
val inputStream: InputStream
) : SubscribeViewAction()
@ -256,9 +270,7 @@ sealed class SubscribeViewAction {
val content: String
) : SubscribeViewAction()
data class Search(
val scope: CoroutineScope,
) : SubscribeViewAction()
object Search: SubscribeViewAction()
object ChangeAllowNotificationPreset : SubscribeViewAction()
object ChangeParseFullContentPreset : SubscribeViewAction()

View File

@ -1,69 +0,0 @@
package me.ash.reader.ui.page.home.feeds.subscribe
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalFocusManager
import com.google.accompanist.pager.ExperimentalPagerApi
import me.ash.reader.data.entity.Group
import me.ash.reader.ui.component.ViewPager
@OptIn(ExperimentalPagerApi::class)
@Composable
fun SubscribeViewPager(
viewState: SubscribeViewState,
modifier: Modifier = Modifier,
onLinkValueChange: (String) -> Unit = {},
onSearchKeyboardAction: () -> Unit = {},
groups: List<Group> = emptyList(),
onNewGroupValueChange: (String) -> Unit = {},
changeNewGroupSelected: (Boolean) -> Unit = {},
allowNotificationPresetOnClick: () -> Unit = {},
parseFullContentPresetOnClick: () -> Unit = {},
onGroupClick: (groupId: String) -> Unit = {},
onResultKeyboardAction: () -> Unit = {},
) {
val focusManager = LocalFocusManager.current
ViewPager(
modifier = modifier.pointerInput(Unit) {
detectTapGestures(
onTap = {
focusManager.clearFocus()
}
)
},
state = viewState.pagerState,
userScrollEnabled = false,
composableList = listOf(
{
SearchViewPage(
pagerState = viewState.pagerState,
readOnly = viewState.lockLinkInput,
inputLink = viewState.linkContent,
errorMessage = viewState.errorMessage,
onLinkValueChange = onLinkValueChange,
onKeyboardAction = onSearchKeyboardAction,
)
},
{
ResultViewPage(
link = viewState.linkContent,
groups = groups,
selectedAllowNotificationPreset = viewState.allowNotificationPreset,
selectedParseFullContentPreset = viewState.parseFullContentPreset,
selectedGroupId = viewState.selectedGroupId,
newGroupContent = viewState.newGroupContent,
onNewGroupValueChange = onNewGroupValueChange,
newGroupSelected = viewState.newGroupSelected,
changeNewGroupSelected = changeNewGroupSelected,
allowNotificationPresetOnClick = allowNotificationPresetOnClick,
parseFullContentPresetOnClick = parseFullContentPresetOnClick,
onGroupClick = onGroupClick,
onKeyboardAction = onResultKeyboardAction,
)
}
)
)
}