diff --git a/app/src/main/java/me/ash/reader/ui/page/home/drawer/feed/FeedOptionDrawer.kt b/app/src/main/java/me/ash/reader/ui/page/home/drawer/feed/FeedOptionDrawer.kt index 7485742..0344e6a 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/drawer/feed/FeedOptionDrawer.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/drawer/feed/FeedOptionDrawer.kt @@ -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, diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/ResultViewPage.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/ResultView.kt similarity index 99% rename from app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/ResultViewPage.kt rename to app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/ResultView.kt index 990d2f1..2d03324 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/ResultViewPage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/ResultView.kt @@ -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 = emptyList(), diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/SearchViewPage.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/SearchView.kt similarity index 96% rename from app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/SearchViewPage.kt rename to app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/SearchView.kt index 99ffa70..d629329 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/SearchViewPage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/SearchView.kt @@ -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 = "", diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/SubscribeDialog.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/SubscribeDialog.kt index 00a3990..fa57b5b 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/SubscribeDialog.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/SubscribeDialog.kt @@ -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)) } } }, diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/SubscribeViewModel.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/SubscribeViewModel.kt index 28504b8..6a6389b 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/SubscribeViewModel.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/SubscribeViewModel.kt @@ -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> = 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() diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/SubscribeViewPager.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/SubscribeViewPager.kt deleted file mode 100644 index dc277ba..0000000 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/SubscribeViewPager.kt +++ /dev/null @@ -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 = 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, - ) - } - ) - ) -} \ No newline at end of file