From 4b7eb980fde0f4ca4875bbc20e89356bc5f31059 Mon Sep 17 00:00:00 2001 From: Ash Date: Wed, 25 May 2022 08:21:12 +0800 Subject: [PATCH 01/18] Add French and Czech translations --- .../data/preference/LanguagesPreference.kt | 12 ++++- app/src/main/res/values-cs-rCZ/strings.xml | 13 +++-- .../strings.xml | 53 +++++++++---------- app/src/main/res/values/strings.xml | 2 + 4 files changed, 43 insertions(+), 37 deletions(-) rename app/src/main/res/{values-fr-FR => values-fr-rFR}/strings.xml (82%) diff --git a/app/src/main/java/me/ash/reader/data/preference/LanguagesPreference.kt b/app/src/main/java/me/ash/reader/data/preference/LanguagesPreference.kt index 122fa49..db11427 100644 --- a/app/src/main/java/me/ash/reader/data/preference/LanguagesPreference.kt +++ b/app/src/main/java/me/ash/reader/data/preference/LanguagesPreference.kt @@ -17,6 +17,8 @@ sealed class LanguagesPreference(val value: Int) : Preference() { object English : LanguagesPreference(1) object ChineseSimplified : LanguagesPreference(2) object German : LanguagesPreference(3) + object French : LanguagesPreference(4) + object Czech : LanguagesPreference(5) override fun put(context: Context, scope: CoroutineScope) { scope.launch { @@ -34,6 +36,8 @@ sealed class LanguagesPreference(val value: Int) : Preference() { English -> context.getString(R.string.english) ChineseSimplified -> context.getString(R.string.chinese_simplified) German -> context.getString(R.string.german) + French -> context.getString(R.string.french) + Czech -> context.getString(R.string.czech) } fun getLocale(): Locale = @@ -42,6 +46,8 @@ sealed class LanguagesPreference(val value: Int) : Preference() { English -> Locale("en", "US") ChineseSimplified -> Locale("zh", "CN") German -> Locale("de", "DE") + French -> Locale("fr", "FR") + Czech -> Locale("cs", "CZ") } fun setLocale(context: Context) { @@ -68,7 +74,7 @@ sealed class LanguagesPreference(val value: Int) : Preference() { companion object { val default = UseDeviceLanguages - val values = listOf(UseDeviceLanguages, English, ChineseSimplified, German) + val values = listOf(UseDeviceLanguages, English, ChineseSimplified, German, French, Czech) fun fromPreferences(preferences: Preferences): LanguagesPreference = when (preferences[DataStoreKeys.Languages.key]) { @@ -76,6 +82,8 @@ sealed class LanguagesPreference(val value: Int) : Preference() { 1 -> English 2 -> ChineseSimplified 3 -> German + 4 -> French + 5 -> Czech else -> default } @@ -85,6 +93,8 @@ sealed class LanguagesPreference(val value: Int) : Preference() { 1 -> English 2 -> ChineseSimplified 3 -> German + 4 -> French + 5 -> Czech else -> default } } diff --git a/app/src/main/res/values-cs-rCZ/strings.xml b/app/src/main/res/values-cs-rCZ/strings.xml index 3491fc4..6da936a 100644 --- a/app/src/main/res/values-cs-rCZ/strings.xml +++ b/app/src/main/res/values-cs-rCZ/strings.xml @@ -1,19 +1,24 @@ - Read You Vše %1$d archivovaná položka %1$d archivovaných položek + %1$d archivovaných položek + %1$d archivovaných položek Nepřečtené %1$d nepřečtená položka %1$d nepřečtených položek + %1$d nepřečtených položek + %1$d nepřečtených položek Oblíbené %1$d oblíbená položka %1$d oblíbených položek + %1$d oblíbených položek + %1$d oblíbených položek Zdroje Synchronizace… @@ -59,7 +64,6 @@ Vše přesunuto do skupiny \"%1$s\" Přejmenovat Změnit URL - http://example.com/feed.rss Přejmenováno na \"%1$s\" Vytvořit novou skupinu Název @@ -103,9 +107,6 @@ Angličtina, zj. čínština, němčina Pomozte nám s překladem Použít jazyk zařízení - English - 简体中文 - Deutsch Tipy a podpora O aplikaci, open source Vítejte @@ -135,8 +136,6 @@ Čtecí stránka Sponzor Licence open source - https://github.com/Ashinch/ReadYou - https://t.me/ReadYouApp https://api.github.com/repos/Ashinch/ReadYou/releases/latest Seznam změn Aktualizovat diff --git a/app/src/main/res/values-fr-FR/strings.xml b/app/src/main/res/values-fr-rFR/strings.xml similarity index 82% rename from app/src/main/res/values-fr-FR/strings.xml rename to app/src/main/res/values-fr-rFR/strings.xml index a687538..e6493f9 100644 --- a/app/src/main/res/values-fr-FR/strings.xml +++ b/app/src/main/res/values-fr-rFR/strings.xml @@ -1,19 +1,21 @@ - Read You Tout %1$d élément archivé %1$d éléments archivés + %1$d éléments archivés Non lus %1$d élément non lu %1$d éléments non lus + %1$d éléments non lus Favoris %1$d élément favori %1$d éléments favoris + %1$d éléments favoris Flux Synchronisation… @@ -32,7 +34,7 @@ Actualiser Rechercher Recherche… - S'abonner + S\'abonner Déjà abonné Vider Coller @@ -41,25 +43,24 @@ Préréglages Sélectionné Autoriser les notifications - Vous êtes sur le point d'autoriser tous les flux dans le groupe \"%1$s\" à vous envoyer des notifications. + Vous êtes sur le point d\'autoriser tous les flux dans le groupe \"%1$s\" à vous envoyer des notifications. Toutes les notifications du groupe \"%1$s\" sont autorisées Toutes les notifications du groupe \"%1$s\" sont bloquées Analyser tout le contenu - Vous êtes sur le point d'autoriser l'analyse de tout le contenu de tous les articles du groupe \"%1$s\". + Vous êtes sur le point d\'autoriser l\'analyse de tout le contenu de tous les articles du groupe \"%1$s\". Le contenu de tous les articles du groupe \"%1$s\" est maintenant analysé - Le contenu de tous les articles du groupe \"%1$s\" n'est plus analysé + Le contenu de tous les articles du groupe \"%1$s\" n\'est plus analysé Vider les articles Tous les articles archivés du flux \"%1$s\" ont été effacés Tous les articles archivés du groupe \"%1$s\" ont été effacés - Vous êtes sur le point d'effacer tous les articles archivés du flux \"%1$s\". - Vous êtes sur le point d'effacer tous les articles archivés du groupe \"%1$s\". + Vous êtes sur le point d\'effacer tous les articles archivés du flux \"%1$s\". + Vous êtes sur le point d\'effacer tous les articles archivés du groupe \"%1$s\". Ajouter au groupe Déplacer vers le groupe Voulez-vous vraiment déplacer tous les flux du groupe \"%1$s\" vers le groupe \"%2$s\" ? Déplacé vers le groupe \"%1$s\" Renommer - Modifier l'URL - http://example.com/feed.rss + Modifier l\'URL Renommé en \"%1$s\" Créer un nouveau groupe Nom @@ -72,7 +73,7 @@ Supprimer le groupe Supprimer le groupe \"%1$s\" et tous ses articles archivés ? Les options suivantes seront appliqués à tous les flux du groupe. - Aujourd'hui + Aujourd\'hui Hier %1$s à %2$s Rechercher dans %1$s du flux \"%2$s\" @@ -82,9 +83,9 @@ Marquer comme non lu Ajouter aux favoris Retirer des favoris - Marquer comme lu les articles jusqu'à 1 jour - Marquer comme lu les articles jusqu'à 3 jours - Marquer comme lu les articles jusqu'à 7 jours + Marquer comme lu les articles jusqu\'à 1 jour + Marquer comme lu les articles jusqu\'à 3 jours + Marquer comme lu les articles jusqu\'à 7 jours 1j 3j 7j @@ -103,18 +104,14 @@ Français, English, 简体中文, Deutsch Aidez-nous à traduire Utiliser la langue du système - English - 简体中文 - Deutsch - Français Aide et assistance À propos, Open Source Bienvenue - Avant de continuer, vous devez accepter les conditions d'utilisation et la politique de confidentialité de Read You. - Lire les <i><u>conditions d'utilisation et la politique de confidentialité</u></i> + Avant de continuer, vous devez accepter les conditions d\'utilisation et la politique de confidentialité de Read You. + Lire les <i><u>conditions d\'utilisation et la politique de confidentialité</u></i> https://github.com/Ashinch/ReadYou/blob/main/TERMS_OF_SERVICE_AND_PRIVACY_POLICY.md - J'accepte - Couleurs du fond d'écran + J\'accepte + Couleurs du fond d\'écran Aucune palette Android 8.1+ uniquement Couleurs de base @@ -136,8 +133,6 @@ Page de lecture Sponsor Licences Open Source - https://github.com/Ashinch/ReadYou - https://t.me/ReadYouApp https://api.github.com/repos/Ashinch/ReadYou/releases/latest Nouveautés Mettre à jour @@ -161,21 +156,21 @@ Images des articles Noms des flux Favicons des flux - En-tête fixe de l'heure de publication des articles (Expérimental) - Liste d'articles + En-tête fixe de l\'heure de publication des articles (Expérimental) + Liste d\'articles Liste des groupes Toujours développer Haut Position du bouton \"Marquer comme lu\" Barre supérieure - Remplir l'icône sélectionné + Remplir l\'icône sélectionné Barre de filtre Icônes Icônes et étiquettes Icônes et étiquettes (sélectionné uniquement) - L'intensité de l'en-tête est valable uniquement lors du défilement. - L'intensité de la liste d'articles est uniquement disponible pour le thème clair. - L'intensité de la liste des groupes est uniquement disponible pour le thème clair. + L\'intensité de l\'en-tête est valable uniquement lors du défilement. + L\'intensité de la liste d\'articles est uniquement disponible pour le thème clair. + L\'intensité de la liste des groupes est uniquement disponible pour le thème clair. Partager Appuyer pour lancer la lecture diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0c8a45c..8cab7dd 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -106,6 +106,8 @@ English 简体中文 Deutsch + français + Čeština Tips & support About, open source Welcome From 31fd54254f93633d660b80a6e268ae0a34da08d1 Mon Sep 17 00:00:00 2001 From: Ashinch Date: Wed, 25 May 2022 08:26:13 +0800 Subject: [PATCH 02/18] Fixed crash if loading very large (50MB+) images (#83) From: Feeder --- app/src/main/java/me/ash/reader/ui/component/AsyncImage.kt | 2 ++ .../main/java/me/ash/reader/ui/page/home/flow/ArticleItem.kt | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/app/src/main/java/me/ash/reader/ui/component/AsyncImage.kt b/app/src/main/java/me/ash/reader/ui/component/AsyncImage.kt index 8689c2f..8ac3cb9 100644 --- a/app/src/main/java/me/ash/reader/ui/component/AsyncImage.kt +++ b/app/src/main/java/me/ash/reader/ui/component/AsyncImage.kt @@ -19,6 +19,8 @@ import coil.size.Scale import coil.size.Size import me.ash.reader.R +val Size_1000 = Size(1000, 1000) + @Composable fun AsyncImage( modifier: Modifier = Modifier, diff --git a/app/src/main/java/me/ash/reader/ui/page/home/flow/ArticleItem.kt b/app/src/main/java/me/ash/reader/ui/page/home/flow/ArticleItem.kt index 8b8a61e..045f6cd 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/flow/ArticleItem.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/flow/ArticleItem.kt @@ -18,11 +18,13 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp +import coil.size.Precision import coil.size.Scale import me.ash.reader.R import me.ash.reader.data.entity.ArticleWithFeed import me.ash.reader.data.preference.* import me.ash.reader.ui.component.AsyncImage +import me.ash.reader.ui.component.Size_1000 import me.ash.reader.ui.ext.formatAsString import me.ash.reader.ui.page.home.FeedIcon @@ -144,6 +146,8 @@ fun ArticleItem( .clip(RoundedCornerShape(20.dp)), data = articleWithFeed.article.img, scale = Scale.FILL, + precision = Precision.INEXACT, + size = Size_1000, contentScale = ContentScale.Crop, ) } From a1021c3c3f8c9015adb0a70b36bdc6471fe2d76a Mon Sep 17 00:00:00 2001 From: Ash Date: Sun, 15 May 2022 23:42:53 +0800 Subject: [PATCH 03/18] OkHttpClient DI --- app/src/main/java/me/ash/reader/App.kt | 3 ++ .../reader/data/module/ImageLoaderModule.kt | 15 +++---- .../reader/data/module/OkHttpClientModule.kt | 42 +++++++++++++++++++ .../ash/reader/data/repository/RssHelper.kt | 3 +- 4 files changed, 52 insertions(+), 11 deletions(-) create mode 100644 app/src/main/java/me/ash/reader/data/module/OkHttpClientModule.kt diff --git a/app/src/main/java/me/ash/reader/App.kt b/app/src/main/java/me/ash/reader/App.kt index 0e463be..2b194b8 100644 --- a/app/src/main/java/me/ash/reader/App.kt +++ b/app/src/main/java/me/ash/reader/App.kt @@ -79,6 +79,9 @@ class App : Application(), Configuration.Provider { @DispatcherDefault lateinit var dispatcherDefault: CoroutineDispatcher + @Inject + lateinit var okHttpClient: OkHttpClient + @Inject lateinit var imageLoader: ImageLoader diff --git a/app/src/main/java/me/ash/reader/data/module/ImageLoaderModule.kt b/app/src/main/java/me/ash/reader/data/module/ImageLoaderModule.kt index bd79879..795c2f1 100644 --- a/app/src/main/java/me/ash/reader/data/module/ImageLoaderModule.kt +++ b/app/src/main/java/me/ash/reader/data/module/ImageLoaderModule.kt @@ -15,7 +15,7 @@ import dagger.hilt.InstallIn import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.components.SingletonComponent import kotlinx.coroutines.Dispatchers -import me.ash.reader.cachingHttpClient +import okhttp3.OkHttpClient import javax.inject.Singleton @Module @@ -25,16 +25,11 @@ object ImageLoaderModule { @Provides @Singleton fun provideImageLoader( - @ApplicationContext context: Context + @ApplicationContext context: Context, + okHttpClient: OkHttpClient, ): ImageLoader { return ImageLoader.Builder(context) - .okHttpClient( - okHttpClient = cachingHttpClient( - cacheDirectory = context.cacheDir.resolve("http") - ).newBuilder() - //.addNetworkInterceptor(UserAgentInterceptor) - .build() - ) + .okHttpClient(okHttpClient) .dispatcher(Dispatchers.Default) // This slightly improves scrolling performance .components{ add(SvgDecoder.Factory()) @@ -59,4 +54,4 @@ object ImageLoaderModule { ) .build() } -} \ No newline at end of file +} diff --git a/app/src/main/java/me/ash/reader/data/module/OkHttpClientModule.kt b/app/src/main/java/me/ash/reader/data/module/OkHttpClientModule.kt new file mode 100644 index 0000000..dcd9224 --- /dev/null +++ b/app/src/main/java/me/ash/reader/data/module/OkHttpClientModule.kt @@ -0,0 +1,42 @@ +package me.ash.reader.data.module + +import android.content.Context +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent +import me.ash.reader.BuildConfig +import me.ash.reader.cachingHttpClient +import okhttp3.Interceptor +import okhttp3.OkHttpClient +import okhttp3.Response +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +object OkHttpClientModule { + + @Provides + @Singleton + fun provideOkHttpClient( + @ApplicationContext context: Context + ): OkHttpClient = cachingHttpClient( + cacheDirectory = context.cacheDir.resolve("http") + ).newBuilder() + .addNetworkInterceptor(UserAgentInterceptor) + .build() +} + +object UserAgentInterceptor : Interceptor { + override fun intercept(chain: Interceptor.Chain): Response { + return chain.proceed( + chain.request() + .newBuilder() + .header("User-Agent", USER_AGENT_STRING) + .build() + ) + } +} + +const val USER_AGENT_STRING = "ReadYou / ${BuildConfig.VERSION_NAME}(${BuildConfig.VERSION_CODE})" \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/data/repository/RssHelper.kt b/app/src/main/java/me/ash/reader/data/repository/RssHelper.kt index 55e1b11..18df76c 100644 --- a/app/src/main/java/me/ash/reader/data/repository/RssHelper.kt +++ b/app/src/main/java/me/ash/reader/data/repository/RssHelper.kt @@ -31,6 +31,7 @@ class RssHelper @Inject constructor( private val context: Context, @DispatcherIO private val dispatcherIO: CoroutineDispatcher, + private val okHttpClient: OkHttpClient, ) { @Throws(Exception::class) suspend fun searchFeed(feedLink: String): FeedWithArticle { @@ -58,7 +59,7 @@ class RssHelper @Inject constructor( @Throws(Exception::class) suspend fun parseFullContent(link: String, title: String): String { return withContext(dispatcherIO) { - val response = OkHttpClient() + val response = okHttpClient .newCall(Request.Builder().url(link).build()) .execute() val content = response.body!!.string() From 962e0ee8dc2635e523babe4abd62526bafde9ced Mon Sep 17 00:00:00 2001 From: Ash Date: Mon, 16 May 2022 10:57:40 +0800 Subject: [PATCH 04/18] Unified CoroutineDispatcher --- .../ui/page/home/feeds/FeedsViewModel.kt | 16 ++++++---- .../feeds/option/feed/FeedOptionViewModel.kt | 24 +++++++++------ .../option/group/GroupOptionViewModel.kt | 30 +++++++++++-------- .../feeds/subscribe/SubscribeViewModel.kt | 11 ++++--- 4 files changed, 51 insertions(+), 30 deletions(-) diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedsViewModel.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedsViewModel.kt index 43c9f63..6030473 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedsViewModel.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedsViewModel.kt @@ -5,12 +5,14 @@ import androidx.compose.foundation.lazy.LazyListState import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import me.ash.reader.R import me.ash.reader.data.entity.Account import me.ash.reader.data.entity.GroupWithFeed +import me.ash.reader.data.module.DispatcherDefault +import me.ash.reader.data.module.DispatcherIO import me.ash.reader.data.repository.AccountRepository import me.ash.reader.data.repository.OpmlRepository import me.ash.reader.data.repository.RssRepository @@ -24,6 +26,10 @@ class FeedsViewModel @Inject constructor( private val rssRepository: RssRepository, private val opmlRepository: OpmlRepository, private val stringsRepository: StringsRepository, + @DispatcherDefault + private val dispatcherDefault: CoroutineDispatcher, + @DispatcherIO + private val dispatcherIO: CoroutineDispatcher, ) : ViewModel() { private val _viewState = MutableStateFlow(FeedsViewState()) val viewState: StateFlow = _viewState.asStateFlow() @@ -38,7 +44,7 @@ class FeedsViewModel @Inject constructor( } private fun fetchAccount() { - viewModelScope.launch(Dispatchers.IO) { + viewModelScope.launch(dispatcherIO) { _viewState.update { it.copy( account = accountRepository.getCurrentAccount() @@ -48,7 +54,7 @@ class FeedsViewModel @Inject constructor( } private fun exportAsOpml(callback: (String) -> Unit = {}) { - viewModelScope.launch(Dispatchers.Default) { + viewModelScope.launch(dispatcherDefault) { try { callback(opmlRepository.saveToString()) } catch (e: Exception) { @@ -58,7 +64,7 @@ class FeedsViewModel @Inject constructor( } private fun fetchData(filterState: FilterState) { - viewModelScope.launch(Dispatchers.IO) { + viewModelScope.launch(dispatcherIO) { pullFeeds( isStarred = filterState.filter.isStarred(), isUnread = filterState.filter.isUnread(), @@ -118,7 +124,7 @@ class FeedsViewModel @Inject constructor( } }.catch { Log.e("RLog", "catch in articleRepository.pullFeeds(): ${it.message}") - }.flowOn(Dispatchers.Default).collect() + }.flowOn(dispatcherDefault).collect() } private fun scrollToItem(index: Int) { diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/feed/FeedOptionViewModel.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/feed/FeedOptionViewModel.kt index d31c510..bbe0aef 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/feed/FeedOptionViewModel.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/feed/FeedOptionViewModel.kt @@ -7,8 +7,8 @@ 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.CoroutineScope -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow @@ -17,6 +17,8 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import me.ash.reader.data.entity.Feed import me.ash.reader.data.entity.Group +import me.ash.reader.data.module.DispatcherIO +import me.ash.reader.data.module.DispatcherMain import me.ash.reader.data.repository.RssRepository import javax.inject.Inject @@ -27,12 +29,16 @@ import javax.inject.Inject @HiltViewModel class FeedOptionViewModel @Inject constructor( private val rssRepository: RssRepository, + @DispatcherMain + private val dispatcherMain: CoroutineDispatcher, + @DispatcherIO + private val dispatcherIO: CoroutineDispatcher, ) : ViewModel() { private val _viewState = MutableStateFlow(FeedOptionViewState()) val viewState: StateFlow = _viewState.asStateFlow() init { - viewModelScope.launch(Dispatchers.IO) { + viewModelScope.launch(dispatcherIO) { rssRepository.get().pullGroups().collect { groups -> _viewState.update { it.copy( @@ -121,7 +127,7 @@ class FeedOptionViewModel @Inject constructor( } private fun selectedGroup(groupId: String) { - viewModelScope.launch(Dispatchers.IO) { + viewModelScope.launch(dispatcherIO) { _viewState.value.feed?.let { rssRepository.get().updateFeed( it.copy( @@ -134,7 +140,7 @@ class FeedOptionViewModel @Inject constructor( } private fun changeParseFullContentPreset() { - viewModelScope.launch(Dispatchers.IO) { + viewModelScope.launch(dispatcherIO) { _viewState.value.feed?.let { rssRepository.get().updateFeed( it.copy( @@ -147,7 +153,7 @@ class FeedOptionViewModel @Inject constructor( } private fun changeAllowNotificationPreset() { - viewModelScope.launch(Dispatchers.IO) { + viewModelScope.launch(dispatcherIO) { _viewState.value.feed?.let { rssRepository.get().updateFeed( it.copy( @@ -161,9 +167,9 @@ class FeedOptionViewModel @Inject constructor( private fun delete(callback: () -> Unit = {}) { _viewState.value.feed?.let { - viewModelScope.launch(Dispatchers.IO) { + viewModelScope.launch(dispatcherIO) { rssRepository.get().deleteFeed(it) - withContext(Dispatchers.Main) { + withContext(dispatcherMain) { callback() } } @@ -204,9 +210,9 @@ class FeedOptionViewModel @Inject constructor( private fun clear(callback: () -> Unit = {}) { _viewState.value.feed?.let { - viewModelScope.launch(Dispatchers.IO) { + viewModelScope.launch(dispatcherIO) { rssRepository.get().deleteArticles(feed = it) - withContext(Dispatchers.Main) { + withContext(dispatcherMain) { callback() } } diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/group/GroupOptionViewModel.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/group/GroupOptionViewModel.kt index b696c8d..59415b4 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/group/GroupOptionViewModel.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/group/GroupOptionViewModel.kt @@ -7,8 +7,8 @@ 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.CoroutineScope -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow @@ -16,6 +16,8 @@ import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import me.ash.reader.data.entity.Group +import me.ash.reader.data.module.DispatcherIO +import me.ash.reader.data.module.DispatcherMain import me.ash.reader.data.repository.RssRepository import javax.inject.Inject @@ -26,12 +28,16 @@ import javax.inject.Inject @HiltViewModel class GroupOptionViewModel @Inject constructor( private val rssRepository: RssRepository, + @DispatcherMain + private val dispatcherMain: CoroutineDispatcher, + @DispatcherIO + private val dispatcherIO: CoroutineDispatcher, ) : ViewModel() { private val _viewState = MutableStateFlow(GroupOptionViewState()) val viewState: StateFlow = _viewState.asStateFlow() init { - viewModelScope.launch(Dispatchers.IO) { + viewModelScope.launch(dispatcherIO) { rssRepository.get().pullGroups().collect { groups -> _viewState.update { it.copy( @@ -106,9 +112,9 @@ class GroupOptionViewModel @Inject constructor( private fun allAllowNotification(isNotification: Boolean, callback: () -> Unit = {}) { _viewState.value.group?.let { - viewModelScope.launch(Dispatchers.IO) { + viewModelScope.launch(dispatcherIO) { rssRepository.get().groupAllowNotification(it, isNotification) - withContext(Dispatchers.Main) { + withContext(dispatcherMain) { callback() } } @@ -125,9 +131,9 @@ class GroupOptionViewModel @Inject constructor( private fun allParseFullContent(isFullContent: Boolean, callback: () -> Unit = {}) { _viewState.value.group?.let { - viewModelScope.launch(Dispatchers.IO) { + viewModelScope.launch(dispatcherIO) { rssRepository.get().groupParseFullContent(it, isFullContent) - withContext(Dispatchers.Main) { + withContext(dispatcherMain) { callback() } } @@ -144,9 +150,9 @@ class GroupOptionViewModel @Inject constructor( private fun delete(callback: () -> Unit = {}) { _viewState.value.group?.let { - viewModelScope.launch(Dispatchers.IO) { + viewModelScope.launch(dispatcherIO) { rssRepository.get().deleteGroup(it) - withContext(Dispatchers.Main) { + withContext(dispatcherMain) { callback() } } @@ -179,9 +185,9 @@ class GroupOptionViewModel @Inject constructor( private fun clear(callback: () -> Unit = {}) { _viewState.value.group?.let { - viewModelScope.launch(Dispatchers.IO) { + viewModelScope.launch(dispatcherIO) { rssRepository.get().deleteArticles(group = it) - withContext(Dispatchers.Main) { + withContext(dispatcherMain) { callback() } } @@ -191,9 +197,9 @@ class GroupOptionViewModel @Inject constructor( private fun allMoveToGroup(callback: () -> Unit) { _viewState.value.group?.let { group -> _viewState.value.targetGroup?.let { targetGroup -> - viewModelScope.launch(Dispatchers.IO) { + viewModelScope.launch(dispatcherIO) { rssRepository.get().groupMoveToTargetGroup(group, targetGroup) - withContext(Dispatchers.Main) { + withContext(dispatcherMain) { callback() } } 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 2b4f807..9bdc1f1 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 @@ -5,7 +5,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.google.accompanist.pager.ExperimentalPagerApi import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Job import kotlinx.coroutines.async import kotlinx.coroutines.flow.* @@ -14,6 +14,7 @@ import me.ash.reader.R import me.ash.reader.data.entity.Article import me.ash.reader.data.entity.Feed import me.ash.reader.data.entity.Group +import me.ash.reader.data.module.DispatcherIO import me.ash.reader.data.repository.OpmlRepository import me.ash.reader.data.repository.RssHelper import me.ash.reader.data.repository.RssRepository @@ -29,6 +30,8 @@ class SubscribeViewModel @Inject constructor( private val rssRepository: RssRepository, private val rssHelper: RssHelper, private val stringsRepository: StringsRepository, + @DispatcherIO + private val dispatcherIO: CoroutineDispatcher, ) : ViewModel() { private val _viewState = MutableStateFlow(SubscribeViewState()) val viewState: StateFlow = _viewState.asStateFlow() @@ -77,7 +80,7 @@ class SubscribeViewModel @Inject constructor( } private fun importFromInputStream(inputStream: InputStream) { - viewModelScope.launch(Dispatchers.IO) { + viewModelScope.launch(dispatcherIO) { try { opmlRepository.saveToDatabase(inputStream) rssRepository.get().doSync() @@ -90,7 +93,7 @@ class SubscribeViewModel @Inject constructor( private fun subscribe() { val feed = _viewState.value.feed ?: return val articles = _viewState.value.articles - viewModelScope.launch(Dispatchers.IO) { + viewModelScope.launch(dispatcherIO) { val groupId = async { _viewState.value.selectedGroupId } @@ -145,7 +148,7 @@ class SubscribeViewModel @Inject constructor( private fun search() { searchJob?.cancel() - viewModelScope.launch(Dispatchers.IO) { + viewModelScope.launch(dispatcherIO) { try { _viewState.update { it.copy( From b010fe940b975c30a46e656ff997ea81c27d433f Mon Sep 17 00:00:00 2001 From: Ash Date: Mon, 16 May 2022 11:04:51 +0800 Subject: [PATCH 05/18] Remove invalid @OptIn --- app/src/main/java/me/ash/reader/ui/component/RadioDialog.kt | 3 +-- .../main/java/me/ash/reader/ui/component/TextFieldDialog.kt | 1 - .../me/ash/reader/ui/component/reader/HtmlToComposable.kt | 4 ---- .../ui/page/home/feeds/option/feed/ClearFeedDialog.kt | 1 - .../ui/page/home/feeds/option/feed/DeleteFeedDialog.kt | 1 - .../ui/page/home/feeds/option/feed/FeedOptionViewModel.kt | 1 - .../home/feeds/option/group/AllAllowNotificationDialog.kt | 1 - .../ui/page/home/feeds/option/group/AllMoveToGroupDialog.kt | 1 - .../home/feeds/option/group/AllParseFullContentDialog.kt | 1 - .../ui/page/home/feeds/option/group/ClearGroupDialog.kt | 1 - .../ui/page/home/feeds/option/group/DeleteGroupDialog.kt | 1 - .../ui/page/home/feeds/option/group/GroupOptionViewModel.kt | 6 +----- .../ui/page/home/feeds/subscribe/SubscribeViewModel.kt | 2 -- .../me/ash/reader/ui/page/settings/tips/UpdateDialog.kt | 2 -- 14 files changed, 2 insertions(+), 24 deletions(-) diff --git a/app/src/main/java/me/ash/reader/ui/component/RadioDialog.kt b/app/src/main/java/me/ash/reader/ui/component/RadioDialog.kt index ff1608c..3b447a4 100644 --- a/app/src/main/java/me/ash/reader/ui/component/RadioDialog.kt +++ b/app/src/main/java/me/ash/reader/ui/component/RadioDialog.kt @@ -19,9 +19,8 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.text.style.BaselineShift import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp -import com.google.accompanist.pager.ExperimentalPagerApi -@OptIn(ExperimentalPagerApi::class, ExperimentalMaterial3Api::class) +@OptIn(ExperimentalMaterial3Api::class) @Composable fun RadioDialog( modifier: Modifier = Modifier, diff --git a/app/src/main/java/me/ash/reader/ui/component/TextFieldDialog.kt b/app/src/main/java/me/ash/reader/ui/component/TextFieldDialog.kt index 4609764..5f7adb3 100644 --- a/app/src/main/java/me/ash/reader/ui/component/TextFieldDialog.kt +++ b/app/src/main/java/me/ash/reader/ui/component/TextFieldDialog.kt @@ -16,7 +16,6 @@ import androidx.compose.ui.window.DialogProperties import com.google.accompanist.pager.ExperimentalPagerApi import me.ash.reader.R -@OptIn(ExperimentalPagerApi::class) @Composable fun TextFieldDialog( modifier: Modifier = Modifier, diff --git a/app/src/main/java/me/ash/reader/ui/component/reader/HtmlToComposable.kt b/app/src/main/java/me/ash/reader/ui/component/reader/HtmlToComposable.kt index da8c1f1..9e66174 100644 --- a/app/src/main/java/me/ash/reader/ui/component/reader/HtmlToComposable.kt +++ b/app/src/main/java/me/ash/reader/ui/component/reader/HtmlToComposable.kt @@ -33,7 +33,6 @@ import androidx.compose.foundation.text.selection.DisableSelection import androidx.compose.material.Text import androidx.compose.material3.Surface import androidx.compose.runtime.Composable -import androidx.compose.runtime.ExperimentalComposeApi import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.RectangleShape @@ -48,7 +47,6 @@ import androidx.compose.ui.text.style.BaselineShift import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import coil.annotation.ExperimentalCoilApi import coil.size.Precision import coil.size.Size import coil.size.pxOrElse @@ -178,7 +176,6 @@ private fun LazyListScope.formatCodeBlock( composer.terminateCurrentText() } -@OptIn(ExperimentalComposeApi::class, ExperimentalCoilApi::class) private fun TextComposer.appendTextChildren( nodes: List, preFormatted: Boolean = false, @@ -646,7 +643,6 @@ private fun TextComposer.appendTextChildren( } } -@OptIn(ExperimentalStdlibApi::class) private fun String.asFontFamily(): FontFamily? = when (this.lowercase()) { "monospace" -> FontFamily.Monospace "serif" -> FontFamily.Serif diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/feed/ClearFeedDialog.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/feed/ClearFeedDialog.kt index cad233b..ef28388 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/feed/ClearFeedDialog.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/feed/ClearFeedDialog.kt @@ -17,7 +17,6 @@ import me.ash.reader.ui.component.Dialog import me.ash.reader.ui.ext.collectAsStateValue import me.ash.reader.ui.ext.showToast -@OptIn(ExperimentalPagerApi::class) @Composable fun ClearFeedDialog( modifier: Modifier = Modifier, diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/feed/DeleteFeedDialog.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/feed/DeleteFeedDialog.kt index 22c8a8e..f0a5c5d 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/feed/DeleteFeedDialog.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/feed/DeleteFeedDialog.kt @@ -17,7 +17,6 @@ import me.ash.reader.ui.component.Dialog import me.ash.reader.ui.ext.collectAsStateValue import me.ash.reader.ui.ext.showToast -@OptIn(ExperimentalPagerApi::class) @Composable fun DeleteFeedDialog( modifier: Modifier = Modifier, diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/feed/FeedOptionViewModel.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/feed/FeedOptionViewModel.kt index bbe0aef..0488eee 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/feed/FeedOptionViewModel.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/feed/FeedOptionViewModel.kt @@ -23,7 +23,6 @@ import me.ash.reader.data.repository.RssRepository import javax.inject.Inject @OptIn( - ExperimentalPagerApi::class, ExperimentalMaterialApi::class ) @HiltViewModel diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/group/AllAllowNotificationDialog.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/group/AllAllowNotificationDialog.kt index 32b599d..d3a7634 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/group/AllAllowNotificationDialog.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/group/AllAllowNotificationDialog.kt @@ -17,7 +17,6 @@ import me.ash.reader.ui.component.Dialog import me.ash.reader.ui.ext.collectAsStateValue import me.ash.reader.ui.ext.showToast -@OptIn(ExperimentalPagerApi::class) @Composable fun AllAllowNotificationDialog( modifier: Modifier = Modifier, diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/group/AllMoveToGroupDialog.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/group/AllMoveToGroupDialog.kt index e5ddb62..2aa13ce 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/group/AllMoveToGroupDialog.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/group/AllMoveToGroupDialog.kt @@ -17,7 +17,6 @@ import me.ash.reader.ui.component.Dialog import me.ash.reader.ui.ext.collectAsStateValue import me.ash.reader.ui.ext.showToast -@OptIn(ExperimentalPagerApi::class) @Composable fun AllMoveToGroupDialog( modifier: Modifier = Modifier, diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/group/AllParseFullContentDialog.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/group/AllParseFullContentDialog.kt index c5db1da..43d844c 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/group/AllParseFullContentDialog.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/group/AllParseFullContentDialog.kt @@ -17,7 +17,6 @@ import me.ash.reader.ui.component.Dialog import me.ash.reader.ui.ext.collectAsStateValue import me.ash.reader.ui.ext.showToast -@OptIn(ExperimentalPagerApi::class) @Composable fun AllParseFullContentDialog( modifier: Modifier = Modifier, diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/group/ClearGroupDialog.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/group/ClearGroupDialog.kt index b02403a..00f26bb 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/group/ClearGroupDialog.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/group/ClearGroupDialog.kt @@ -17,7 +17,6 @@ import me.ash.reader.ui.component.Dialog import me.ash.reader.ui.ext.collectAsStateValue import me.ash.reader.ui.ext.showToast -@OptIn(ExperimentalPagerApi::class) @Composable fun ClearGroupDialog( modifier: Modifier = Modifier, diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/group/DeleteGroupDialog.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/group/DeleteGroupDialog.kt index 8c39dc4..d9e96fb 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/group/DeleteGroupDialog.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/group/DeleteGroupDialog.kt @@ -17,7 +17,6 @@ import me.ash.reader.ui.component.Dialog import me.ash.reader.ui.ext.collectAsStateValue import me.ash.reader.ui.ext.showToast -@OptIn(ExperimentalPagerApi::class) @Composable fun DeleteGroupDialog( modifier: Modifier = Modifier, diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/group/GroupOptionViewModel.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/group/GroupOptionViewModel.kt index 59415b4..f86d43c 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/group/GroupOptionViewModel.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/group/GroupOptionViewModel.kt @@ -5,7 +5,6 @@ import androidx.compose.material.ModalBottomSheetState import androidx.compose.material.ModalBottomSheetValue 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.CoroutineScope @@ -21,10 +20,7 @@ import me.ash.reader.data.module.DispatcherMain import me.ash.reader.data.repository.RssRepository import javax.inject.Inject -@OptIn( - ExperimentalPagerApi::class, - ExperimentalMaterialApi::class -) +@OptIn(ExperimentalMaterialApi::class) @HiltViewModel class GroupOptionViewModel @Inject constructor( private val rssRepository: RssRepository, 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 9bdc1f1..243ed9c 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 @@ -23,7 +23,6 @@ import me.ash.reader.ui.ext.formatUrl import java.io.InputStream import javax.inject.Inject -@OptIn(ExperimentalPagerApi::class) @HiltViewModel class SubscribeViewModel @Inject constructor( private val opmlRepository: OpmlRepository, @@ -245,7 +244,6 @@ class SubscribeViewModel @Inject constructor( } } -@OptIn(ExperimentalPagerApi::class) data class SubscribeViewState( val visible: Boolean = false, val title: String = "", diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/tips/UpdateDialog.kt b/app/src/main/java/me/ash/reader/ui/page/settings/tips/UpdateDialog.kt index 081058b..d1e1cf1 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/tips/UpdateDialog.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/tips/UpdateDialog.kt @@ -30,7 +30,6 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel -import com.google.accompanist.pager.ExperimentalPagerApi import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch @@ -40,7 +39,6 @@ import me.ash.reader.ui.component.Dialog import me.ash.reader.ui.ext.* @SuppressLint("FlowOperatorInvokedInComposition") -@OptIn(ExperimentalPagerApi::class) @Composable fun UpdateDialog( updateViewModel: UpdateViewModel = hiltViewModel(), From 2f1263c82c7b5a55b4a73618553acf958343fdf8 Mon Sep 17 00:00:00 2001 From: Ash Date: Mon, 16 May 2022 11:11:24 +0800 Subject: [PATCH 06/18] Change to variable fields --- .../java/me/ash/reader/data/entity/Article.kt | 16 ++++++++-------- .../me/ash/reader/data/entity/ArticleWithFeed.kt | 4 ++-- .../main/java/me/ash/reader/data/entity/Feed.kt | 8 ++++---- .../me/ash/reader/data/entity/FeedWithArticle.kt | 4 ++-- .../me/ash/reader/data/entity/FeedWithGroup.kt | 4 ++-- .../main/java/me/ash/reader/data/entity/Group.kt | 6 +++--- .../me/ash/reader/data/entity/GroupWithFeed.kt | 4 ++-- 7 files changed, 23 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/me/ash/reader/data/entity/Article.kt b/app/src/main/java/me/ash/reader/data/entity/Article.kt index c358d2b..c18859e 100644 --- a/app/src/main/java/me/ash/reader/data/entity/Article.kt +++ b/app/src/main/java/me/ash/reader/data/entity/Article.kt @@ -18,13 +18,13 @@ import java.util.* ) data class Article( @PrimaryKey - val id: String, + var id: String, @ColumnInfo - val date: Date, + var date: Date, @ColumnInfo - val title: String, + var title: String, @ColumnInfo - val author: String? = null, + var author: String? = null, @ColumnInfo var rawDescription: String, @ColumnInfo @@ -32,13 +32,13 @@ data class Article( @ColumnInfo var fullContent: String? = null, @ColumnInfo - val img: String? = null, + var img: String? = null, @ColumnInfo - val link: String, + var link: String, @ColumnInfo(index = true) - val feedId: String, + var feedId: String, @ColumnInfo(index = true) - val accountId: Int, + var accountId: Int, @ColumnInfo(defaultValue = "true") var isUnread: Boolean = true, @ColumnInfo(defaultValue = "false") diff --git a/app/src/main/java/me/ash/reader/data/entity/ArticleWithFeed.kt b/app/src/main/java/me/ash/reader/data/entity/ArticleWithFeed.kt index 4910ceb..c3ccc6f 100644 --- a/app/src/main/java/me/ash/reader/data/entity/ArticleWithFeed.kt +++ b/app/src/main/java/me/ash/reader/data/entity/ArticleWithFeed.kt @@ -5,7 +5,7 @@ import androidx.room.Relation data class ArticleWithFeed( @Embedded - val article: Article, + var article: Article, @Relation(parentColumn = "feedId", entityColumn = "id") - val feed: Feed, + var feed: Feed, ) diff --git a/app/src/main/java/me/ash/reader/data/entity/Feed.kt b/app/src/main/java/me/ash/reader/data/entity/Feed.kt index 6c8f0c1..8dafacf 100644 --- a/app/src/main/java/me/ash/reader/data/entity/Feed.kt +++ b/app/src/main/java/me/ash/reader/data/entity/Feed.kt @@ -14,17 +14,17 @@ import androidx.room.* ) data class Feed( @PrimaryKey - val id: String, + var id: String, @ColumnInfo - val name: String, + var name: String, @ColumnInfo var icon: String? = null, @ColumnInfo - val url: String, + var url: String, @ColumnInfo(index = true) var groupId: String, @ColumnInfo(index = true) - val accountId: Int, + var accountId: Int, @ColumnInfo(defaultValue = "false") var isNotification: Boolean = false, @ColumnInfo(defaultValue = "false") diff --git a/app/src/main/java/me/ash/reader/data/entity/FeedWithArticle.kt b/app/src/main/java/me/ash/reader/data/entity/FeedWithArticle.kt index 48ee0ab..f7e77ac 100644 --- a/app/src/main/java/me/ash/reader/data/entity/FeedWithArticle.kt +++ b/app/src/main/java/me/ash/reader/data/entity/FeedWithArticle.kt @@ -5,7 +5,7 @@ import androidx.room.Relation data class FeedWithArticle( @Embedded - val feed: Feed, + var feed: Feed, @Relation(parentColumn = "id", entityColumn = "feedId") - val articles: List
+ var articles: List
) diff --git a/app/src/main/java/me/ash/reader/data/entity/FeedWithGroup.kt b/app/src/main/java/me/ash/reader/data/entity/FeedWithGroup.kt index 01d4087..bf254b7 100644 --- a/app/src/main/java/me/ash/reader/data/entity/FeedWithGroup.kt +++ b/app/src/main/java/me/ash/reader/data/entity/FeedWithGroup.kt @@ -5,7 +5,7 @@ import androidx.room.Relation data class FeedWithGroup( @Embedded - val feed: Feed, + var feed: Feed, @Relation(parentColumn = "groupId", entityColumn = "id") - val group: Group + var group: Group ) diff --git a/app/src/main/java/me/ash/reader/data/entity/Group.kt b/app/src/main/java/me/ash/reader/data/entity/Group.kt index 5aa7962..4929d4b 100644 --- a/app/src/main/java/me/ash/reader/data/entity/Group.kt +++ b/app/src/main/java/me/ash/reader/data/entity/Group.kt @@ -8,11 +8,11 @@ import androidx.room.PrimaryKey @Entity(tableName = "group") data class Group( @PrimaryKey - val id: String, + var id: String, @ColumnInfo - val name: String, + var name: String, @ColumnInfo(index = true) - val accountId: Int, + var accountId: Int, ) { @Ignore var important: Int? = 0 diff --git a/app/src/main/java/me/ash/reader/data/entity/GroupWithFeed.kt b/app/src/main/java/me/ash/reader/data/entity/GroupWithFeed.kt index 28f2c47..f15efdb 100644 --- a/app/src/main/java/me/ash/reader/data/entity/GroupWithFeed.kt +++ b/app/src/main/java/me/ash/reader/data/entity/GroupWithFeed.kt @@ -5,7 +5,7 @@ import androidx.room.Relation data class GroupWithFeed( @Embedded - val group: Group, + var group: Group, @Relation(parentColumn = "id", entityColumn = "groupId") - val feeds: MutableList + var feeds: MutableList ) From ee55f671bcf4a52270e8a81f055ed559403bda43 Mon Sep 17 00:00:00 2001 From: Ash Date: Mon, 16 May 2022 11:16:42 +0800 Subject: [PATCH 07/18] DB record null value --- app/src/main/java/me/ash/reader/data/dao/AccountDao.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/me/ash/reader/data/dao/AccountDao.kt b/app/src/main/java/me/ash/reader/data/dao/AccountDao.kt index 37bb4b5..67fe06c 100644 --- a/app/src/main/java/me/ash/reader/data/dao/AccountDao.kt +++ b/app/src/main/java/me/ash/reader/data/dao/AccountDao.kt @@ -18,7 +18,7 @@ interface AccountDao { WHERE id = :id """ ) - suspend fun queryById(id: Int): Account + suspend fun queryById(id: Int): Account? @Insert suspend fun insert(account: Account): Long From b33e0a7ac59bd0eef6a40a8bd19cbd5ffb38a6fc Mon Sep 17 00:00:00 2001 From: Ash Date: Tue, 17 May 2022 20:39:07 +0800 Subject: [PATCH 08/18] Refactor data layer --- app/src/main/AndroidManifest.xml | 3 - app/src/main/java/me/ash/reader/App.kt | 36 +--- .../java/me/ash/reader/data/dao/ArticleDao.kt | 2 +- .../ash/reader/data/entity/LatestRelease.kt | 23 --- .../reader/data/{entity => model}/Filter.kt | 13 +- .../data/{entity => model}/ImportantCount.kt | 2 +- .../me/ash/reader/data/model/LatestRelease.kt | 2 + .../reader/data/{entity => model}/Version.kt | 2 +- .../reader/data/module/OkHttpClientModule.kt | 81 ++++++++- .../data/repository/AbstractRssRepository.kt | 45 +---- .../reader/data/repository/AppRepository.kt | 16 +- .../data/repository/FeverRssRepository.kt | 163 ------------------ .../data/repository/LocalRssRepository.kt | 93 +--------- .../data/repository/NotificationHelper.kt | 103 +++++++++++ .../reader/data/repository/OpmlRepository.kt | 2 +- .../ash/reader/data/repository/RssHelper.kt | 23 --- .../ash/reader/data/repository/SyncWorker.kt | 41 +++++ .../data/source/AppNetworkDataSource.kt | 37 +++- .../java/me/ash/reader/ui/ext/ContextExt.kt | 4 +- .../main/java/me/ash/reader/ui/ext/DateExt.kt | 24 +++ .../java/me/ash/reader/ui/ext/FilterExt.kt | 13 -- .../java/me/ash/reader/ui/ext/FlavorExt.kt | 11 ++ .../me/ash/reader/ui/page/common/HomeEntry.kt | 2 +- .../me/ash/reader/ui/page/home/FilterBar.kt | 10 +- .../ash/reader/ui/page/home/HomeViewModel.kt | 2 +- .../reader/ui/page/home/feeds/FeedsPage.kt | 3 +- .../ash/reader/ui/page/home/flow/FlowPage.kt | 2 +- .../reader/ui/page/settings/SettingsPage.kt | 2 +- .../settings/color/feeds/FeedsPageStyle.kt | 2 +- .../page/settings/color/flow/FlowPageStyle.kt | 2 +- .../ui/page/settings/tips/UpdateViewModel.kt | 4 +- 31 files changed, 335 insertions(+), 433 deletions(-) delete mode 100644 app/src/main/java/me/ash/reader/data/entity/LatestRelease.kt rename app/src/main/java/me/ash/reader/data/{entity => model}/Filter.kt (74%) rename app/src/main/java/me/ash/reader/data/{entity => model}/ImportantCount.kt (75%) create mode 100644 app/src/main/java/me/ash/reader/data/model/LatestRelease.kt rename app/src/main/java/me/ash/reader/data/{entity => model}/Version.kt (96%) delete mode 100644 app/src/main/java/me/ash/reader/data/repository/FeverRssRepository.kt create mode 100644 app/src/main/java/me/ash/reader/data/repository/NotificationHelper.kt create mode 100644 app/src/main/java/me/ash/reader/data/repository/SyncWorker.kt delete mode 100644 app/src/main/java/me/ash/reader/ui/ext/FilterExt.kt create mode 100644 app/src/main/java/me/ash/reader/ui/ext/FlavorExt.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index d275ec0..5830478 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -5,9 +5,6 @@ - - - ? = null, - val body: String? = null, -) - -data class AssetsItem( - val name: String? = null, - val content_type: String? = null, - val size: Int? = null, - val download_count: Int? = null, - val created_at: String? = null, - val updated_at: String? = null, - val browser_download_url: String? = null, -) \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/data/entity/Filter.kt b/app/src/main/java/me/ash/reader/data/model/Filter.kt similarity index 74% rename from app/src/main/java/me/ash/reader/data/entity/Filter.kt rename to app/src/main/java/me/ash/reader/data/model/Filter.kt index 738ebca..4d1ada5 100644 --- a/app/src/main/java/me/ash/reader/data/entity/Filter.kt +++ b/app/src/main/java/me/ash/reader/data/model/Filter.kt @@ -1,4 +1,4 @@ -package me.ash.reader.data.entity +package me.ash.reader.data.model import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.FiberManualRecord @@ -6,7 +6,10 @@ import androidx.compose.material.icons.rounded.FiberManualRecord import androidx.compose.material.icons.rounded.Star import androidx.compose.material.icons.rounded.StarOutline import androidx.compose.material.icons.rounded.Subject +import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.stringResource +import me.ash.reader.R class Filter( val index: Int, @@ -33,5 +36,13 @@ class Filter( iconOutline = Icons.Rounded.Subject, iconFilled = Icons.Rounded.Subject, ) + val values = listOf(Starred, Unread, All) } +} + +@Composable +fun Filter.getName(): String = when (this) { + Filter.Unread -> stringResource(R.string.unread) + Filter.Starred -> stringResource(R.string.starred) + else -> stringResource(R.string.all) } \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/data/entity/ImportantCount.kt b/app/src/main/java/me/ash/reader/data/model/ImportantCount.kt similarity index 75% rename from app/src/main/java/me/ash/reader/data/entity/ImportantCount.kt rename to app/src/main/java/me/ash/reader/data/model/ImportantCount.kt index c876bb6..5e6ffc0 100644 --- a/app/src/main/java/me/ash/reader/data/entity/ImportantCount.kt +++ b/app/src/main/java/me/ash/reader/data/model/ImportantCount.kt @@ -1,4 +1,4 @@ -package me.ash.reader.data.entity +package me.ash.reader.data.model data class ImportantCount( val important: Int, diff --git a/app/src/main/java/me/ash/reader/data/model/LatestRelease.kt b/app/src/main/java/me/ash/reader/data/model/LatestRelease.kt new file mode 100644 index 0000000..ff18b3c --- /dev/null +++ b/app/src/main/java/me/ash/reader/data/model/LatestRelease.kt @@ -0,0 +1,2 @@ +package me.ash.reader.data.model + diff --git a/app/src/main/java/me/ash/reader/data/entity/Version.kt b/app/src/main/java/me/ash/reader/data/model/Version.kt similarity index 96% rename from app/src/main/java/me/ash/reader/data/entity/Version.kt rename to app/src/main/java/me/ash/reader/data/model/Version.kt index 1d8243f..50ef322 100644 --- a/app/src/main/java/me/ash/reader/data/entity/Version.kt +++ b/app/src/main/java/me/ash/reader/data/model/Version.kt @@ -1,4 +1,4 @@ -package me.ash.reader.data.entity +package me.ash.reader.data.model class Version(identifiers: List) { private var major: Int = 0 diff --git a/app/src/main/java/me/ash/reader/data/module/OkHttpClientModule.kt b/app/src/main/java/me/ash/reader/data/module/OkHttpClientModule.kt index dcd9224..279248b 100644 --- a/app/src/main/java/me/ash/reader/data/module/OkHttpClientModule.kt +++ b/app/src/main/java/me/ash/reader/data/module/OkHttpClientModule.kt @@ -1,3 +1,23 @@ +/* + * Feeder: Android RSS reader app + * https://gitlab.com/spacecowboy/Feeder + * + * Copyright (C) 2022 Jonas Kalderstam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + package me.ash.reader.data.module import android.content.Context @@ -7,11 +27,20 @@ import dagger.hilt.InstallIn import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.components.SingletonComponent import me.ash.reader.BuildConfig -import me.ash.reader.cachingHttpClient +import okhttp3.Cache import okhttp3.Interceptor import okhttp3.OkHttpClient import okhttp3.Response +import java.io.File +import java.security.KeyManagementException +import java.security.NoSuchAlgorithmException +import java.security.cert.X509Certificate +import java.util.concurrent.TimeUnit import javax.inject.Singleton +import javax.net.ssl.HostnameVerifier +import javax.net.ssl.SSLContext +import javax.net.ssl.TrustManager +import javax.net.ssl.X509TrustManager @Module @InstallIn(SingletonComponent::class) @@ -28,6 +57,56 @@ object OkHttpClientModule { .build() } +fun cachingHttpClient( + cacheDirectory: File? = null, + cacheSize: Long = 10L * 1024L * 1024L, + trustAllCerts: Boolean = true, + connectTimeoutSecs: Long = 30L, + readTimeoutSecs: Long = 30L +): OkHttpClient { + val builder: OkHttpClient.Builder = OkHttpClient.Builder() + + if (cacheDirectory != null) { + builder.cache(Cache(cacheDirectory, cacheSize)) + } + + builder + .connectTimeout(connectTimeoutSecs, TimeUnit.SECONDS) + .readTimeout(readTimeoutSecs, TimeUnit.SECONDS) + .followRedirects(true) + + if (trustAllCerts) { + builder.trustAllCerts() + } + + return builder.build() +} + +fun OkHttpClient.Builder.trustAllCerts() { + try { + val trustManager = object : X509TrustManager { + override fun checkClientTrusted(chain: Array?, authType: String?) { + } + + override fun checkServerTrusted(chain: Array?, authType: String?) { + } + + override fun getAcceptedIssuers(): Array = emptyArray() + } + + val sslContext = SSLContext.getInstance("TLS") + sslContext.init(null, arrayOf(trustManager), null) + val sslSocketFactory = sslContext.socketFactory + + sslSocketFactory(sslSocketFactory, trustManager) + .hostnameVerifier(HostnameVerifier { _, _ -> true }) + } catch (e: NoSuchAlgorithmException) { + // ignore + } catch (e: KeyManagementException) { + // ignore + } +} + object UserAgentInterceptor : Interceptor { override fun intercept(chain: Interceptor.Chain): Response { return chain.proceed( diff --git a/app/src/main/java/me/ash/reader/data/repository/AbstractRssRepository.kt b/app/src/main/java/me/ash/reader/data/repository/AbstractRssRepository.kt index 71eb82d..75f42f8 100644 --- a/app/src/main/java/me/ash/reader/data/repository/AbstractRssRepository.kt +++ b/app/src/main/java/me/ash/reader/data/repository/AbstractRssRepository.kt @@ -2,11 +2,11 @@ package me.ash.reader.data.repository import android.content.Context import android.util.Log -import androidx.hilt.work.HiltWorker import androidx.paging.PagingSource -import androidx.work.* -import dagger.assisted.Assisted -import dagger.assisted.AssistedInject +import androidx.work.CoroutineWorker +import androidx.work.ExistingPeriodicWorkPolicy +import androidx.work.ListenableWorker +import androidx.work.WorkManager import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOn @@ -15,9 +15,9 @@ import me.ash.reader.data.dao.ArticleDao import me.ash.reader.data.dao.FeedDao import me.ash.reader.data.dao.GroupDao import me.ash.reader.data.entity.* +import me.ash.reader.data.model.ImportantCount import me.ash.reader.ui.ext.currentAccountId import java.util.* -import java.util.concurrent.TimeUnit abstract class AbstractRssRepository constructor( private val context: Context, @@ -130,10 +130,6 @@ abstract class AbstractRssRepository constructor( return feedDao.queryByLink(context.currentAccountId, url).isNotEmpty() } - fun peekWork(): String { - return workManager.getWorkInfosByTag("sync").get().size.toString() - } - suspend fun updateGroup(group: Group) { groupDao.update(group) } @@ -207,34 +203,3 @@ abstract class AbstractRssRepository constructor( } } } - -@HiltWorker -class SyncWorker @AssistedInject constructor( - @Assisted context: Context, - @Assisted workerParams: WorkerParameters, - private val rssRepository: RssRepository, -) : CoroutineWorker(context, workerParams) { - - override suspend fun doWork(): Result { - Log.i("RLog", "doWork: ") - return rssRepository.get().sync(this) - } - - companion object { - const val WORK_NAME = "article.sync" - - val UUID: UUID - - val repeatingRequest = PeriodicWorkRequestBuilder( - 15, TimeUnit.MINUTES - ).setConstraints( - Constraints.Builder() - .build() - ).addTag(WORK_NAME).build().also { - UUID = it.id - } - - fun setIsSyncing(boolean: Boolean) = workDataOf("isSyncing" to boolean) - fun Data.getIsSyncing(): Boolean = getBoolean("isSyncing", false) - } -} \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/data/repository/AppRepository.kt b/app/src/main/java/me/ash/reader/data/repository/AppRepository.kt index 5d67c56..bfa0216 100644 --- a/app/src/main/java/me/ash/reader/data/repository/AppRepository.kt +++ b/app/src/main/java/me/ash/reader/data/repository/AppRepository.kt @@ -4,13 +4,11 @@ import android.content.Context import android.util.Log import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.withContext import me.ash.reader.R -import me.ash.reader.data.entity.toVersion -import me.ash.reader.data.module.ApplicationScope +import me.ash.reader.data.model.toVersion import me.ash.reader.data.module.DispatcherIO import me.ash.reader.data.module.DispatcherMain import me.ash.reader.data.source.AppNetworkDataSource @@ -23,8 +21,6 @@ class AppRepository @Inject constructor( @ApplicationContext private val context: Context, private val appNetworkDataSource: AppNetworkDataSource, - @ApplicationScope - private val applicationScope: CoroutineScope, @DispatcherIO private val dispatcherIO: CoroutineDispatcher, @DispatcherMain @@ -55,14 +51,8 @@ class AppRepository @Inject constructor( val currentVersion = context.getCurrentVersion() val latestLog = latest.body ?: "" val latestPublishDate = latest.published_at ?: latest.created_at ?: "" - val latestSize = latest.assets - ?.first() - ?.size - ?: 0 - val latestDownloadUrl = latest.assets - ?.first() - ?.browser_download_url - ?: "" + val latestSize = latest.assets?.first()?.size ?: 0 + val latestDownloadUrl = latest.assets?.first()?.browser_download_url ?: "" Log.i("RLog", "current version $currentVersion") if (latestVersion.whetherNeedUpdate(currentVersion, skipVersion)) { diff --git a/app/src/main/java/me/ash/reader/data/repository/FeverRssRepository.kt b/app/src/main/java/me/ash/reader/data/repository/FeverRssRepository.kt deleted file mode 100644 index a7b8600..0000000 --- a/app/src/main/java/me/ash/reader/data/repository/FeverRssRepository.kt +++ /dev/null @@ -1,163 +0,0 @@ -//package me.ash.reader.data.repository -// -//import android.content.Context -//import android.util.Log -//import androidx.work.WorkManager -//import dagger.hilt.android.qualifiers.ApplicationContext -//import kotlinx.coroutines.CoroutineDispatcher -//import kotlinx.coroutines.CoroutineScope -//import kotlinx.coroutines.launch -//import kotlinx.coroutines.sync.withLock -//import me.ash.reader.data.dao.AccountDao -//import me.ash.reader.data.dao.ArticleDao -//import me.ash.reader.data.dao.FeedDao -//import me.ash.reader.data.dao.GroupDao -//import me.ash.reader.data.entity.Article -//import me.ash.reader.data.entity.Feed -//import me.ash.reader.data.entity.Group -//import me.ash.reader.data.module.ApplicationScope -//import me.ash.reader.data.module.DispatcherDefault -//import me.ash.reader.data.module.DispatcherIO -//import me.ash.reader.data.source.FeverApiDataSource -//import me.ash.reader.data.source.RssNetworkDataSource -//import me.ash.reader.ui.ext.currentAccountId -//import me.ash.reader.ui.ext.spacerDollar -//import net.dankito.readability4j.extended.Readability4JExtended -//import java.util.* -//import javax.inject.Inject -//import kotlin.collections.set -// -//class FeverRssRepository @Inject constructor( -// @ApplicationContext -// private val context: Context, -// private val articleDao: ArticleDao, -// private val feedDao: FeedDao, -// private val groupDao: GroupDao, -// private val rssHelper: RssHelper, -// private val feverApiDataSource: FeverApiDataSource, -// private val accountDao: AccountDao, -// rssNetworkDataSource: RssNetworkDataSource, -// @ApplicationScope -// private val applicationScope: CoroutineScope, -// @DispatcherDefault -// private val dispatcherDefault: CoroutineDispatcher, -// @DispatcherIO -// private val dispatcherIO: CoroutineDispatcher, -// workManager: WorkManager, -//) : AbstractRssRepository( -// context, accountDao, articleDao, groupDao, -// feedDao, rssNetworkDataSource, workManager, -// dispatcherIO -//) { -// override suspend fun updateArticleInfo(article: Article) { -// articleDao.update(article) -// } -// -// override suspend fun subscribe(feed: Feed, articles: List
) { -// feedDao.insert(feed) -// articleDao.insertList(articles.map { -// it.copy(feedId = feed.id) -// }) -// } -// -// override suspend fun addGroup(name: String): String { -// return UUID.randomUUID().toString().also { -// groupDao.insert( -// Group( -// id = it, -// name = name, -// accountId = context.currentAccountId -// ) -// ) -// } -// } -// -// override suspend fun sync() { -// applicationScope.launch(dispatcherDefault) { -// mutex.withLock { -// val accountId = context.currentAccountId -// -// updateSyncState { -// it.copy( -// feedCount = 1, -// syncedCount = 1, -// currentFeedName = "Fever" -// ) -// } -// -// if (feedDao.queryAll(accountId).isNullOrEmpty()) { -// // Temporary add feeds -// val feverFeeds = feverApiDataSource.feeds().execute().body()!!.feeds -// val feverGroupsBody = feverApiDataSource.groups().execute().body()!! -// Log.i("RLog", "Fever groups: $feverGroupsBody") -// feverGroupsBody.groups.forEach { -// groupDao.insert( -// Group( -// id = accountId.spacerDollar(it.id), -// name = it.title, -// accountId = accountId, -// ) -// ) -// } -// val feverFeedsGroupsMap = mutableMapOf() -// feverGroupsBody.feeds_groups.forEach { item -> -// item.feed_ids -// .split(",") -// .map { it.toInt() } -// .forEach { id -> -// feverFeedsGroupsMap[id] = item.group_id -// } -// } -// val feeds = feverFeeds.map { -// Feed( -// id = accountId.spacerDollar(it.id), -// name = it.title, -// url = it.url, -// groupId = feverFeedsGroupsMap[it.id].toString(), -// accountId = accountId -// ) -// } -// feedDao.insertList(feeds) -// } -// -// // Add articles -// val articles = mutableListOf
() -// feverApiDataSource.itemsBySince(since = 1647444325925621L) -// .execute().body()!!.items -// .forEach { -// articles.add( -// Article( -// id = accountId.spacerDollar(it.id), -// date = Date(it.created_on_time * 1000), -// title = it.title, -// author = it.author, -// rawDescription = it.html, -// shortDescription = ( -// Readability4JExtended("", it.html) -// .parse().textContent ?: "" -// ).take(100).trim(), -// link = it.url, -// accountId = accountId, -// feedId = it.feed_id.toString(), -// isUnread = it.is_read == 0, -// isStarred = it.is_saved == 1, -// ) -// ) -// } -// articleDao.insertList(articles) -// -// // Complete sync -// accountDao.update(accountDao.queryById(accountId)!!.apply { -// updateAt = Date() -// }) -// updateSyncState { -// it.copy( -// feedCount = 0, -// syncedCount = 0, -// currentFeedName = "" -// ) -// } -// } -// } -// } -//} \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/data/repository/LocalRssRepository.kt b/app/src/main/java/me/ash/reader/data/repository/LocalRssRepository.kt index d101a19..b09d0ad 100644 --- a/app/src/main/java/me/ash/reader/data/repository/LocalRssRepository.kt +++ b/app/src/main/java/me/ash/reader/data/repository/LocalRssRepository.kt @@ -1,12 +1,7 @@ package me.ash.reader.data.repository -import android.app.* import android.content.Context -import android.content.Intent -import android.graphics.BitmapFactory import android.util.Log -import androidx.core.app.NotificationCompat -import androidx.core.app.NotificationManagerCompat import androidx.work.CoroutineWorker import androidx.work.ListenableWorker import androidx.work.WorkManager @@ -15,8 +10,6 @@ import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.withContext -import me.ash.reader.MainActivity -import me.ash.reader.R import me.ash.reader.data.dao.AccountDao import me.ash.reader.data.dao.ArticleDao import me.ash.reader.data.dao.FeedDao @@ -30,8 +23,6 @@ import me.ash.reader.data.module.DispatcherIO import me.ash.reader.data.repository.SyncWorker.Companion.setIsSyncing import me.ash.reader.ui.ext.currentAccountId import me.ash.reader.ui.ext.spacerDollar -import me.ash.reader.ui.page.common.ExtraName -import me.ash.reader.ui.page.common.NotificationGroupName import java.util.* import javax.inject.Inject @@ -41,6 +32,7 @@ class LocalRssRepository @Inject constructor( private val articleDao: ArticleDao, private val feedDao: FeedDao, private val rssHelper: RssHelper, + private val notificationHelper: NotificationHelper, private val accountDao: AccountDao, private val groupDao: GroupDao, @DispatcherDefault @@ -52,16 +44,6 @@ class LocalRssRepository @Inject constructor( context, accountDao, articleDao, groupDao, feedDao, workManager, dispatcherIO ) { - private val notificationManager: NotificationManagerCompat = - NotificationManagerCompat.from(context).apply { - createNotificationChannel( - NotificationChannel( - NotificationGroupName.ARTICLE_UPDATE, - NotificationGroupName.ARTICLE_UPDATE, - NotificationManager.IMPORTANCE_DEFAULT - ) - ) - } override suspend fun updateArticleInfo(article: Article) { articleDao.update(article) @@ -98,7 +80,7 @@ class LocalRssRepository @Inject constructor( .awaitAll() .forEach { if (it.isNotify) { - notify( + notificationHelper.notify( FeedWithArticle( it.feedWithArticle.feed, articleDao.insertIfNotExist(it.feedWithArticle.articles) @@ -183,75 +165,4 @@ class LocalRssRepository @Inject constructor( isNotify = articles.isNotEmpty() && feed.isNotification ) } - - private fun notify( - feedWithArticle: FeedWithArticle, - ) { - notificationManager.createNotificationChannelGroup( - NotificationChannelGroup( - feedWithArticle.feed.id, - feedWithArticle.feed.name - ) - ) - feedWithArticle.articles.forEach { article -> - val builder = NotificationCompat.Builder(context, NotificationGroupName.ARTICLE_UPDATE) - .setSmallIcon(R.drawable.ic_notification) - .setLargeIcon( - (BitmapFactory.decodeResource( - context.resources, - R.drawable.ic_notification - )) - ) - .setContentTitle(article.title) - .setContentIntent( - PendingIntent.getActivity( - context, - Random().nextInt() + article.id.hashCode(), - Intent(context, MainActivity::class.java).apply { - flags = Intent.FLAG_ACTIVITY_NEW_TASK or - Intent.FLAG_ACTIVITY_CLEAR_TASK - putExtra( - ExtraName.ARTICLE_ID, - article.id - ) - }, - PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT - ) - ) - .setGroup(feedWithArticle.feed.id) - .setStyle( - NotificationCompat.BigTextStyle() - .bigText(article.shortDescription) - .setSummaryText(feedWithArticle.feed.name) - ) - - notificationManager.notify( - Random().nextInt() + article.id.hashCode(), - builder.build().apply { - flags = Notification.FLAG_AUTO_CANCEL - } - ) - } - - if (feedWithArticle.articles.size > 1) { - notificationManager.notify( - Random().nextInt() + feedWithArticle.feed.id.hashCode(), - NotificationCompat.Builder(context, NotificationGroupName.ARTICLE_UPDATE) - .setSmallIcon(R.drawable.ic_notification) - .setLargeIcon( - (BitmapFactory.decodeResource( - context.resources, - R.drawable.ic_notification - )) - ) - .setStyle( - NotificationCompat.InboxStyle() - .setSummaryText(feedWithArticle.feed.name) - ) - .setGroup(feedWithArticle.feed.id) - .setGroupSummary(true) - .build() - ) - } - } } \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/data/repository/NotificationHelper.kt b/app/src/main/java/me/ash/reader/data/repository/NotificationHelper.kt new file mode 100644 index 0000000..ee421c8 --- /dev/null +++ b/app/src/main/java/me/ash/reader/data/repository/NotificationHelper.kt @@ -0,0 +1,103 @@ +package me.ash.reader.data.repository + +import android.app.* +import android.content.Context +import android.content.Intent +import android.graphics.BitmapFactory +import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationManagerCompat +import dagger.hilt.android.qualifiers.ApplicationContext +import me.ash.reader.MainActivity +import me.ash.reader.R +import me.ash.reader.data.entity.FeedWithArticle +import me.ash.reader.ui.page.common.ExtraName +import me.ash.reader.ui.page.common.NotificationGroupName +import java.util.* +import javax.inject.Inject + +class NotificationHelper @Inject constructor( + @ApplicationContext + private val context: Context, +) { + private val notificationManager: NotificationManagerCompat = + NotificationManagerCompat.from(context).apply { + createNotificationChannel( + NotificationChannel( + NotificationGroupName.ARTICLE_UPDATE, + NotificationGroupName.ARTICLE_UPDATE, + NotificationManager.IMPORTANCE_DEFAULT + ) + ) + } + + fun notify( + feedWithArticle: FeedWithArticle, + ) { + notificationManager.createNotificationChannelGroup( + NotificationChannelGroup( + feedWithArticle.feed.id, + feedWithArticle.feed.name + ) + ) + feedWithArticle.articles.forEach { article -> + val builder = NotificationCompat.Builder(context, NotificationGroupName.ARTICLE_UPDATE) + .setSmallIcon(R.drawable.ic_notification) + .setLargeIcon( + (BitmapFactory.decodeResource( + context.resources, + R.drawable.ic_notification + )) + ) + .setContentTitle(article.title) + .setContentIntent( + PendingIntent.getActivity( + context, + Random().nextInt() + article.id.hashCode(), + Intent(context, MainActivity::class.java).apply { + flags = Intent.FLAG_ACTIVITY_NEW_TASK or + Intent.FLAG_ACTIVITY_CLEAR_TASK + putExtra( + ExtraName.ARTICLE_ID, + article.id + ) + }, + PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT + ) + ) + .setGroup(feedWithArticle.feed.id) + .setStyle( + NotificationCompat.BigTextStyle() + .bigText(article.shortDescription) + .setSummaryText(feedWithArticle.feed.name) + ) + + notificationManager.notify( + Random().nextInt() + article.id.hashCode(), + builder.build().apply { + flags = Notification.FLAG_AUTO_CANCEL + } + ) + } + + if (feedWithArticle.articles.size > 1) { + notificationManager.notify( + Random().nextInt() + feedWithArticle.feed.id.hashCode(), + NotificationCompat.Builder(context, NotificationGroupName.ARTICLE_UPDATE) + .setSmallIcon(R.drawable.ic_notification) + .setLargeIcon( + (BitmapFactory.decodeResource( + context.resources, + R.drawable.ic_notification + )) + ) + .setStyle( + NotificationCompat.InboxStyle() + .setSummaryText(feedWithArticle.feed.name) + ) + .setGroup(feedWithArticle.feed.id) + .setGroupSummary(true) + .build() + ) + } + } +} diff --git a/app/src/main/java/me/ash/reader/data/repository/OpmlRepository.kt b/app/src/main/java/me/ash/reader/data/repository/OpmlRepository.kt index 304bed4..7af9c0e 100644 --- a/app/src/main/java/me/ash/reader/data/repository/OpmlRepository.kt +++ b/app/src/main/java/me/ash/reader/data/repository/OpmlRepository.kt @@ -43,7 +43,7 @@ class OpmlRepository @Inject constructor( repeatList.add(it) } } - feedDao.insertList((groupWithFeed.feeds subtract repeatList).toList()) + feedDao.insertList((groupWithFeed.feeds subtract repeatList.toSet()).toList()) } } diff --git a/app/src/main/java/me/ash/reader/data/repository/RssHelper.kt b/app/src/main/java/me/ash/reader/data/repository/RssHelper.kt index 18df76c..584fa48 100644 --- a/app/src/main/java/me/ash/reader/data/repository/RssHelper.kt +++ b/app/src/main/java/me/ash/reader/data/repository/RssHelper.kt @@ -183,27 +183,4 @@ class RssHelper @Inject constructor( } ) } - - private fun parseDate( - inputDate: String, patterns: Array = arrayOf( - "yyyy-MM-dd'T'HH:mm:ss'Z'", - "yyyy-MM-dd", - "yyyy-MM-dd HH:mm:ss", - "yyyyMMdd", - "yyyy/MM/dd", - "yyyy年MM月dd日", - "yyyy MM dd", - ) - ): Date? { - val df = SimpleDateFormat() - for (pattern in patterns) { - df.applyPattern(pattern) - df.isLenient = false - val date = df.parse(inputDate, ParsePosition(0)) - if (date != null) { - return date - } - } - return null - } } \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/data/repository/SyncWorker.kt b/app/src/main/java/me/ash/reader/data/repository/SyncWorker.kt new file mode 100644 index 0000000..50cdaf2 --- /dev/null +++ b/app/src/main/java/me/ash/reader/data/repository/SyncWorker.kt @@ -0,0 +1,41 @@ +package me.ash.reader.data.repository + +import android.content.Context +import android.util.Log +import androidx.hilt.work.HiltWorker +import androidx.work.* +import dagger.assisted.Assisted +import dagger.assisted.AssistedInject +import java.util.* +import java.util.concurrent.TimeUnit + +@HiltWorker +class SyncWorker @AssistedInject constructor( + @Assisted context: Context, + @Assisted workerParams: WorkerParameters, + private val rssRepository: RssRepository, +) : CoroutineWorker(context, workerParams) { + + override suspend fun doWork(): Result { + Log.i("RLog", "doWork: ") + return rssRepository.get().sync(this) + } + + companion object { + const val WORK_NAME = "article.sync" + + val UUID: UUID + + val repeatingRequest = PeriodicWorkRequestBuilder( + 15, TimeUnit.MINUTES + ).setConstraints( + Constraints.Builder() + .build() + ).addTag(WORK_NAME).build().also { + UUID = it.id + } + + fun setIsSyncing(boolean: Boolean) = workDataOf("isSyncing" to boolean) + fun Data.getIsSyncing(): Boolean = getBoolean("isSyncing", false) + } +} \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/data/source/AppNetworkDataSource.kt b/app/src/main/java/me/ash/reader/data/source/AppNetworkDataSource.kt index a37d951..df0709e 100644 --- a/app/src/main/java/me/ash/reader/data/source/AppNetworkDataSource.kt +++ b/app/src/main/java/me/ash/reader/data/source/AppNetworkDataSource.kt @@ -5,7 +5,6 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOn -import me.ash.reader.data.entity.LatestRelease import okhttp3.ResponseBody import retrofit2.Response import retrofit2.Retrofit @@ -15,12 +14,6 @@ import retrofit2.http.Streaming import retrofit2.http.Url import java.io.File -sealed class Download { - object NotYet : Download() - data class Progress(val percent: Int) : Download() - data class Finished(val file: File) : Download() -} - interface AppNetworkDataSource { @GET suspend fun getReleaseLatest(@Url url: String): Response @@ -92,4 +85,32 @@ fun ResponseBody.downloadToFileWithProgress(saveFile: File): Flow = saveFile.delete() } } - }.flowOn(Dispatchers.IO).distinctUntilChanged() \ No newline at end of file + }.flowOn(Dispatchers.IO).distinctUntilChanged() + +data class LatestRelease( + val html_url: String? = null, + val tag_name: String? = null, + val name: String? = null, + val draft: Boolean? = null, + val prerelease: Boolean? = null, + val created_at: String? = null, + val published_at: String? = null, + val assets: List? = null, + val body: String? = null, +) + +data class AssetsItem( + val name: String? = null, + val content_type: String? = null, + val size: Int? = null, + val download_count: Int? = null, + val created_at: String? = null, + val updated_at: String? = null, + val browser_download_url: String? = null, +) + +sealed class Download { + object NotYet : Download() + data class Progress(val percent: Int) : Download() + data class Finished(val file: File) : Download() +} \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/ui/ext/ContextExt.kt b/app/src/main/java/me/ash/reader/ui/ext/ContextExt.kt index 203e575..2db93f0 100644 --- a/app/src/main/java/me/ash/reader/ui/ext/ContextExt.kt +++ b/app/src/main/java/me/ash/reader/ui/ext/ContextExt.kt @@ -7,8 +7,8 @@ import android.content.Intent import android.util.Log import android.widget.Toast import androidx.core.content.FileProvider -import me.ash.reader.data.entity.Version -import me.ash.reader.data.entity.toVersion +import me.ash.reader.data.model.Version +import me.ash.reader.data.model.toVersion import java.io.File fun Context.findActivity(): Activity? = when (this) { diff --git a/app/src/main/java/me/ash/reader/ui/ext/DateExt.kt b/app/src/main/java/me/ash/reader/ui/ext/DateExt.kt index 887b285..e609595 100644 --- a/app/src/main/java/me/ash/reader/ui/ext/DateExt.kt +++ b/app/src/main/java/me/ash/reader/ui/ext/DateExt.kt @@ -4,6 +4,7 @@ import android.content.Context import androidx.core.os.ConfigurationCompat import me.ash.reader.R import java.text.DateFormat +import java.text.ParsePosition import java.text.SimpleDateFormat import java.util.* @@ -40,4 +41,27 @@ fun Date.formatAsString( } } } +} + +private fun String.parseToDate( + patterns: Array = arrayOf( + "yyyy-MM-dd'T'HH:mm:ss'Z'", + "yyyy-MM-dd", + "yyyy-MM-dd HH:mm:ss", + "yyyyMMdd", + "yyyy/MM/dd", + "yyyy年MM月dd日", + "yyyy MM dd", + ) +): Date? { + val df = SimpleDateFormat() + for (pattern in patterns) { + df.applyPattern(pattern) + df.isLenient = false + val date = df.parse(this, ParsePosition(0)) + if (date != null) { + return date + } + } + return null } \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/ui/ext/FilterExt.kt b/app/src/main/java/me/ash/reader/ui/ext/FilterExt.kt deleted file mode 100644 index 8a2327a..0000000 --- a/app/src/main/java/me/ash/reader/ui/ext/FilterExt.kt +++ /dev/null @@ -1,13 +0,0 @@ -package me.ash.reader.ui.ext - -import androidx.compose.runtime.Composable -import androidx.compose.ui.res.stringResource -import me.ash.reader.R -import me.ash.reader.data.entity.Filter - -@Composable -fun Filter.getName(): String = when (this) { - Filter.Unread -> stringResource(R.string.unread) - Filter.Starred -> stringResource(R.string.starred) - else -> stringResource(R.string.all) -} diff --git a/app/src/main/java/me/ash/reader/ui/ext/FlavorExt.kt b/app/src/main/java/me/ash/reader/ui/ext/FlavorExt.kt new file mode 100644 index 0000000..2a3e8b1 --- /dev/null +++ b/app/src/main/java/me/ash/reader/ui/ext/FlavorExt.kt @@ -0,0 +1,11 @@ +@file:Suppress("SpellCheckingInspection") + +package me.ash.reader.ui.ext + +import me.ash.reader.BuildConfig + +const val GITHUB = "github" +const val FDROID = "fdroid" + +const val isFdroid = BuildConfig.FLAVOR == FDROID +const val notFdroid = !isFdroid \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/ui/page/common/HomeEntry.kt b/app/src/main/java/me/ash/reader/ui/page/common/HomeEntry.kt index 87b5015..3855951 100644 --- a/app/src/main/java/me/ash/reader/ui/page/common/HomeEntry.kt +++ b/app/src/main/java/me/ash/reader/ui/page/common/HomeEntry.kt @@ -12,7 +12,7 @@ import androidx.hilt.navigation.compose.hiltViewModel import com.google.accompanist.navigation.animation.AnimatedNavHost import com.google.accompanist.navigation.animation.rememberAnimatedNavController import com.google.accompanist.systemuicontroller.rememberSystemUiController -import me.ash.reader.data.entity.Filter +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 diff --git a/app/src/main/java/me/ash/reader/ui/page/home/FilterBar.kt b/app/src/main/java/me/ash/reader/ui/page/home/FilterBar.kt index a9307e7..cb7dad0 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/FilterBar.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/FilterBar.kt @@ -12,10 +12,10 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalView import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.Dp -import me.ash.reader.data.entity.Filter +import me.ash.reader.data.model.Filter +import me.ash.reader.data.model.getName import me.ash.reader.data.preference.FlowFilterBarStylePreference import me.ash.reader.data.preference.LocalThemeIndex -import me.ash.reader.ui.ext.getName import me.ash.reader.ui.ext.surfaceColorAtElevation import me.ash.reader.ui.theme.palette.onDark @@ -39,11 +39,7 @@ fun FilterBar( tonalElevation = filterBarTonalElevation, ) { Spacer(modifier = Modifier.width(filterBarPadding)) - listOf( - Filter.Starred, - Filter.Unread, - Filter.All, - ).forEach { item -> + Filter.values.forEach { item -> NavigationBarItem( // modifier = Modifier.height(60.dp), alwaysShowLabel = when (filterBarStyle) { diff --git a/app/src/main/java/me/ash/reader/ui/page/home/HomeViewModel.kt b/app/src/main/java/me/ash/reader/ui/page/home/HomeViewModel.kt index 9149e23..3f7f258 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/HomeViewModel.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/HomeViewModel.kt @@ -7,7 +7,7 @@ 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.entity.Filter +import me.ash.reader.data.model.Filter import me.ash.reader.data.entity.Group import me.ash.reader.data.module.ApplicationScope import me.ash.reader.data.repository.RssRepository diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedsPage.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedsPage.kt index 4b22327..d522d5c 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedsPage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedsPage.kt @@ -28,7 +28,8 @@ import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavHostController import kotlinx.coroutines.flow.map import me.ash.reader.R -import me.ash.reader.data.entity.toVersion +import me.ash.reader.data.model.getName +import me.ash.reader.data.model.toVersion import me.ash.reader.data.preference.* import me.ash.reader.data.repository.SyncWorker.Companion.getIsSyncing import me.ash.reader.ui.component.Banner diff --git a/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowPage.kt b/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowPage.kt index d6a1abd..81dd07f 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowPage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowPage.kt @@ -25,13 +25,13 @@ import androidx.paging.compose.collectAsLazyPagingItems import kotlinx.coroutines.delay import kotlinx.coroutines.launch import me.ash.reader.R +import me.ash.reader.data.model.getName import me.ash.reader.data.preference.* import me.ash.reader.data.repository.SyncWorker.Companion.getIsSyncing import me.ash.reader.ui.component.DisplayText import me.ash.reader.ui.component.FeedbackIconButton import me.ash.reader.ui.component.SwipeRefresh import me.ash.reader.ui.ext.collectAsStateValue -import me.ash.reader.ui.ext.getName import me.ash.reader.ui.ext.surfaceColorAtElevation import me.ash.reader.ui.page.common.RouteName import me.ash.reader.ui.page.home.FilterBar diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/SettingsPage.kt b/app/src/main/java/me/ash/reader/ui/page/settings/SettingsPage.kt index 5a88950..620bc52 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/SettingsPage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/SettingsPage.kt @@ -19,7 +19,7 @@ import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavHostController import kotlinx.coroutines.flow.map import me.ash.reader.R -import me.ash.reader.data.entity.toVersion +import me.ash.reader.data.model.toVersion import me.ash.reader.ui.component.Banner import me.ash.reader.ui.component.DisplayText import me.ash.reader.ui.component.FeedbackIconButton diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/color/feeds/FeedsPageStyle.kt b/app/src/main/java/me/ash/reader/ui/page/settings/color/feeds/FeedsPageStyle.kt index a7fce97..2f7a95c 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/color/feeds/FeedsPageStyle.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/color/feeds/FeedsPageStyle.kt @@ -22,7 +22,7 @@ import androidx.compose.ui.unit.dp import androidx.navigation.NavHostController import me.ash.reader.R import me.ash.reader.data.entity.Feed -import me.ash.reader.data.entity.Filter +import me.ash.reader.data.model.Filter import me.ash.reader.data.entity.Group import me.ash.reader.data.preference.* import me.ash.reader.ui.component.* diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/color/flow/FlowPageStyle.kt b/app/src/main/java/me/ash/reader/ui/page/settings/color/flow/FlowPageStyle.kt index a07e097..9471ece 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/color/flow/FlowPageStyle.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/color/flow/FlowPageStyle.kt @@ -25,7 +25,7 @@ import me.ash.reader.R import me.ash.reader.data.entity.Article import me.ash.reader.data.entity.ArticleWithFeed import me.ash.reader.data.entity.Feed -import me.ash.reader.data.entity.Filter +import me.ash.reader.data.model.Filter import me.ash.reader.data.preference.* import me.ash.reader.ui.component.* import me.ash.reader.ui.ext.surfaceColorAtElevation diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/tips/UpdateViewModel.kt b/app/src/main/java/me/ash/reader/ui/page/settings/tips/UpdateViewModel.kt index 03739ca..1a4e54a 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/tips/UpdateViewModel.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/tips/UpdateViewModel.kt @@ -5,9 +5,9 @@ import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch -import me.ash.reader.BuildConfig import me.ash.reader.data.repository.AppRepository import me.ash.reader.data.source.Download +import me.ash.reader.ui.ext.notFdroid import javax.inject.Inject @HiltViewModel @@ -33,7 +33,7 @@ class UpdateViewModel @Inject constructor( preProcessor: suspend () -> Unit = {}, postProcessor: suspend (Boolean) -> Unit = {} ) { - if (BuildConfig.FLAVOR != "fdroid") { + if (notFdroid) { viewModelScope.launch { preProcessor() appRepository.checkUpdate().let { From 140cb3ed0a65adf3d2bff3c9787b8eb2b6710e3b Mon Sep 17 00:00:00 2001 From: Ash Date: Wed, 18 May 2022 02:58:30 +0800 Subject: [PATCH 09/18] Move the package --- .../data/preference/DarkThemePreference.kt | 2 ++ .../ash/reader/data/preference/Preference.kt | 35 +++++++++++++++++++ .../me/ash/reader/data/preference/Settings.kt | 35 ------------------- .../ui/{page/home => component}/FeedIcon.kt | 2 +- .../ui/{page/home => component}/FilterBar.kt | 2 +- .../ui/component/{ => base}/AnimatedPopup.kt | 2 +- .../ui/component/{ => base}/AnimatedText.kt | 2 +- .../ui/component/{ => base}/AsyncImage.kt | 2 +- .../reader/ui/component/{ => base}/Banner.kt | 2 +- .../ui/component/{ => base}/BlockButton.kt | 2 +- .../component/{ => base}/BlockRadioButton.kt | 2 +- .../ui/component/{ => base}/BottomDrawer.kt | 2 +- .../{ => base}/CanBeDisabledIconButton.kt | 2 +- .../{ => base}/ClipboardTextField.kt | 2 +- .../component/{ => base}/CurlyCornerShape.kt | 2 +- .../reader/ui/component/{ => base}/Dialog.kt | 2 +- .../ui/component/{ => base}/DisplayText.kt | 2 +- .../component/{ => base}/DynamicSVGImage.kt | 2 +- .../{ => base}/FeedbackIconButton.kt | 2 +- .../reader/ui/component/{ => base}/Menu.kt | 2 +- .../ui/component/{ => base}/RadioDialog.kt | 2 +- .../ui/component/{ => base}/SelectionChip.kt | 2 +- .../ui/component/{ => base}/SubTitle.kt | 2 +- .../ui/component/{ => base}/SwipeRefresh.kt | 2 +- .../reader/ui/component/{ => base}/Switch.kt | 2 +- .../ui/component/{ => base}/TextField.kt | 2 +- .../component/{ => base}/TextFieldDialog.kt | 3 +- .../reader/ui/component/{ => base}/Tips.kt | 2 +- .../ui/component/{ => base}/ViewPager.kt | 2 +- .../reader/ui/component/{ => base}/WebView.kt | 2 +- .../ui/component/reader/HtmlToComposable.kt | 2 +- .../ash/reader/ui/page/home/feeds/FeedItem.kt | 6 ++-- .../reader/ui/page/home/feeds/FeedsPage.kt | 16 ++++----- .../reader/ui/page/home/feeds/GroupItem.kt | 4 +-- .../feed/ClearFeedDialog.kt | 5 ++- .../feed/DeleteFeedDialog.kt | 5 ++- .../feed/FeedOptionDrawer.kt | 8 ++--- .../feed/FeedOptionViewModel.kt | 3 +- .../group/AllAllowNotificationDialog.kt | 5 ++- .../group/AllMoveToGroupDialog.kt | 5 ++- .../group/AllParseFullContentDialog.kt | 5 ++- .../group/ClearGroupDialog.kt | 5 ++- .../group/DeleteGroupDialog.kt | 5 ++- .../group/GroupOptionDrawer.kt | 10 +++--- .../group/GroupOptionViewModel.kt | 2 +- .../page/home/feeds/subscribe/ResultView.kt | 4 +-- .../home/feeds/subscribe/SubscribeDialog.kt | 6 ++-- .../reader/ui/page/home/flow/ArticleItem.kt | 6 ++-- .../ash/reader/ui/page/home/flow/FlowPage.kt | 8 ++--- .../reader/ui/page/home/flow/MarkAsReadBar.kt | 2 +- .../ash/reader/ui/page/home/read/ReadBar.kt | 2 +- .../ash/reader/ui/page/home/read/ReadPage.kt | 2 +- .../reader/ui/page/settings/SettingsPage.kt | 6 ++-- .../ui/page/settings/color/ColorAndStyle.kt | 6 ++-- .../ui/page/settings/color/DarkTheme.kt | 8 ++--- .../settings/color/feeds/FeedsPageStyle.kt | 4 +-- .../page/settings/color/flow/FlowPageStyle.kt | 18 +++++----- .../page/settings/interaction/Interaction.kt | 3 +- .../ui/page/settings/languages/Languages.kt | 6 ++-- .../ui/page/settings/tips/TipsAndSupport.kt | 4 +-- .../ui/page/settings/tips/UpdateDialog.kt | 2 +- .../ash/reader/ui/page/startup/StartupPage.kt | 6 ++-- 62 files changed, 147 insertions(+), 159 deletions(-) rename app/src/main/java/me/ash/reader/ui/{page/home => component}/FeedIcon.kt (97%) rename app/src/main/java/me/ash/reader/ui/{page/home => component}/FilterBar.kt (99%) rename app/src/main/java/me/ash/reader/ui/component/{ => base}/AnimatedPopup.kt (97%) rename app/src/main/java/me/ash/reader/ui/component/{ => base}/AnimatedText.kt (98%) rename app/src/main/java/me/ash/reader/ui/component/{ => base}/AsyncImage.kt (98%) rename app/src/main/java/me/ash/reader/ui/component/{ => base}/Banner.kt (98%) rename app/src/main/java/me/ash/reader/ui/component/{ => base}/BlockButton.kt (97%) rename app/src/main/java/me/ash/reader/ui/component/{ => base}/BlockRadioButton.kt (97%) rename app/src/main/java/me/ash/reader/ui/component/{ => base}/BottomDrawer.kt (98%) rename app/src/main/java/me/ash/reader/ui/component/{ => base}/CanBeDisabledIconButton.kt (96%) rename app/src/main/java/me/ash/reader/ui/component/{ => base}/ClipboardTextField.kt (98%) rename app/src/main/java/me/ash/reader/ui/component/{ => base}/CurlyCornerShape.kt (98%) rename app/src/main/java/me/ash/reader/ui/component/{ => base}/Dialog.kt (95%) rename app/src/main/java/me/ash/reader/ui/component/{ => base}/DisplayText.kt (97%) rename app/src/main/java/me/ash/reader/ui/component/{ => base}/DynamicSVGImage.kt (97%) rename app/src/main/java/me/ash/reader/ui/component/{ => base}/FeedbackIconButton.kt (98%) rename app/src/main/java/me/ash/reader/ui/component/{ => base}/Menu.kt (97%) rename app/src/main/java/me/ash/reader/ui/component/{ => base}/RadioDialog.kt (98%) rename app/src/main/java/me/ash/reader/ui/component/{ => base}/SelectionChip.kt (98%) rename app/src/main/java/me/ash/reader/ui/component/{ => base}/SubTitle.kt (94%) rename app/src/main/java/me/ash/reader/ui/component/{ => base}/SwipeRefresh.kt (96%) rename app/src/main/java/me/ash/reader/ui/component/{ => base}/Switch.kt (98%) rename app/src/main/java/me/ash/reader/ui/component/{ => base}/TextField.kt (98%) rename app/src/main/java/me/ash/reader/ui/component/{ => base}/TextFieldDialog.kt (96%) rename app/src/main/java/me/ash/reader/ui/component/{ => base}/Tips.kt (96%) rename app/src/main/java/me/ash/reader/ui/component/{ => base}/ViewPager.kt (96%) rename app/src/main/java/me/ash/reader/ui/component/{ => base}/WebView.kt (99%) rename app/src/main/java/me/ash/reader/ui/page/home/feeds/{option => drawer}/feed/ClearFeedDialog.kt (94%) rename app/src/main/java/me/ash/reader/ui/page/home/feeds/{option => drawer}/feed/DeleteFeedDialog.kt (94%) rename app/src/main/java/me/ash/reader/ui/page/home/feeds/{option => drawer}/feed/FeedOptionDrawer.kt (97%) rename app/src/main/java/me/ash/reader/ui/page/home/feeds/{option => drawer}/feed/FeedOptionViewModel.kt (99%) rename app/src/main/java/me/ash/reader/ui/page/home/feeds/{option => drawer}/group/AllAllowNotificationDialog.kt (94%) rename app/src/main/java/me/ash/reader/ui/page/home/feeds/{option => drawer}/group/AllMoveToGroupDialog.kt (94%) rename app/src/main/java/me/ash/reader/ui/page/home/feeds/{option => drawer}/group/AllParseFullContentDialog.kt (94%) rename app/src/main/java/me/ash/reader/ui/page/home/feeds/{option => drawer}/group/ClearGroupDialog.kt (94%) rename app/src/main/java/me/ash/reader/ui/page/home/feeds/{option => drawer}/group/DeleteGroupDialog.kt (94%) rename app/src/main/java/me/ash/reader/ui/page/home/feeds/{option => drawer}/group/GroupOptionDrawer.kt (97%) rename app/src/main/java/me/ash/reader/ui/page/home/feeds/{option => drawer}/group/GroupOptionViewModel.kt (99%) diff --git a/app/src/main/java/me/ash/reader/data/preference/DarkThemePreference.kt b/app/src/main/java/me/ash/reader/data/preference/DarkThemePreference.kt index a5329df..4047372 100644 --- a/app/src/main/java/me/ash/reader/data/preference/DarkThemePreference.kt +++ b/app/src/main/java/me/ash/reader/data/preference/DarkThemePreference.kt @@ -3,6 +3,7 @@ package me.ash.reader.data.preference import android.content.Context import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.runtime.Composable +import androidx.compose.runtime.ReadOnlyComposable import androidx.datastore.preferences.core.Preferences import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch @@ -33,6 +34,7 @@ sealed class DarkThemePreference(val value: Int) : Preference() { } @Composable + @ReadOnlyComposable fun isDarkTheme(): Boolean = when (this) { UseDeviceTheme -> isSystemInDarkTheme() ON -> true diff --git a/app/src/main/java/me/ash/reader/data/preference/Preference.kt b/app/src/main/java/me/ash/reader/data/preference/Preference.kt index cd451da..0ea515e 100644 --- a/app/src/main/java/me/ash/reader/data/preference/Preference.kt +++ b/app/src/main/java/me/ash/reader/data/preference/Preference.kt @@ -1,8 +1,43 @@ package me.ash.reader.data.preference import android.content.Context +import androidx.datastore.preferences.core.Preferences import kotlinx.coroutines.CoroutineScope sealed class Preference { abstract fun put(context: Context, scope: CoroutineScope) +} + +fun Preferences.toSettings(): Settings { + return Settings( + themeIndex = ThemeIndexPreference.fromPreferences(this), + customPrimaryColor = CustomPrimaryColorPreference.fromPreferences(this), + darkTheme = DarkThemePreference.fromPreferences(this), + amoledDarkTheme = AmoledDarkThemePreference.fromPreferences(this), + + feedsFilterBarStyle = FeedsFilterBarStylePreference.fromPreferences(this), + feedsFilterBarFilled = FeedsFilterBarFilledPreference.fromPreferences(this), + feedsFilterBarPadding = FeedsFilterBarPaddingPreference.fromPreferences(this), + feedsFilterBarTonalElevation = FeedsFilterBarTonalElevationPreference.fromPreferences(this), + feedsTopBarTonalElevation = FeedsTopBarTonalElevationPreference.fromPreferences(this), + feedsGroupListExpand = FeedsGroupListExpandPreference.fromPreferences(this), + feedsGroupListTonalElevation = FeedsGroupListTonalElevationPreference.fromPreferences(this), + + flowFilterBarStyle = FlowFilterBarStylePreference.fromPreferences(this), + flowFilterBarFilled = FlowFilterBarFilledPreference.fromPreferences(this), + flowFilterBarPadding = FlowFilterBarPaddingPreference.fromPreferences(this), + flowFilterBarTonalElevation = FlowFilterBarTonalElevationPreference.fromPreferences(this), + flowTopBarTonalElevation = FlowTopBarTonalElevationPreference.fromPreferences(this), + flowArticleListFeedIcon = FlowArticleListFeedIconPreference.fromPreferences(this), + flowArticleListFeedName = FlowArticleListFeedNamePreference.fromPreferences(this), + flowArticleListImage = FlowArticleListImagePreference.fromPreferences(this), + flowArticleListDesc = FlowArticleListDescPreference.fromPreferences(this), + flowArticleListTime = FlowArticleListTimePreference.fromPreferences(this), + flowArticleListDateStickyHeader = FlowArticleListDateStickyHeaderPreference.fromPreferences( + this + ), + flowArticleListTonalElevation = FlowArticleListTonalElevationPreference.fromPreferences(this), + + languages = LanguagesPreference.fromPreferences(this), + ) } \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/data/preference/Settings.kt b/app/src/main/java/me/ash/reader/data/preference/Settings.kt index 70996c7..632662b 100644 --- a/app/src/main/java/me/ash/reader/data/preference/Settings.kt +++ b/app/src/main/java/me/ash/reader/data/preference/Settings.kt @@ -6,7 +6,6 @@ import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.compositionLocalOf import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalContext -import androidx.datastore.preferences.core.Preferences import kotlinx.coroutines.flow.map import me.ash.reader.ui.ext.collectAsStateValue import me.ash.reader.ui.ext.dataStore @@ -41,40 +40,6 @@ data class Settings( val languages: LanguagesPreference = LanguagesPreference.default, ) -fun Preferences.toSettings(): Settings { - return Settings( - themeIndex = ThemeIndexPreference.fromPreferences(this), - customPrimaryColor = CustomPrimaryColorPreference.fromPreferences(this), - darkTheme = DarkThemePreference.fromPreferences(this), - amoledDarkTheme = AmoledDarkThemePreference.fromPreferences(this), - - feedsFilterBarStyle = FeedsFilterBarStylePreference.fromPreferences(this), - feedsFilterBarFilled = FeedsFilterBarFilledPreference.fromPreferences(this), - feedsFilterBarPadding = FeedsFilterBarPaddingPreference.fromPreferences(this), - feedsFilterBarTonalElevation = FeedsFilterBarTonalElevationPreference.fromPreferences(this), - feedsTopBarTonalElevation = FeedsTopBarTonalElevationPreference.fromPreferences(this), - feedsGroupListExpand = FeedsGroupListExpandPreference.fromPreferences(this), - feedsGroupListTonalElevation = FeedsGroupListTonalElevationPreference.fromPreferences(this), - - flowFilterBarStyle = FlowFilterBarStylePreference.fromPreferences(this), - flowFilterBarFilled = FlowFilterBarFilledPreference.fromPreferences(this), - flowFilterBarPadding = FlowFilterBarPaddingPreference.fromPreferences(this), - flowFilterBarTonalElevation = FlowFilterBarTonalElevationPreference.fromPreferences(this), - flowTopBarTonalElevation = FlowTopBarTonalElevationPreference.fromPreferences(this), - flowArticleListFeedIcon = FlowArticleListFeedIconPreference.fromPreferences(this), - flowArticleListFeedName = FlowArticleListFeedNamePreference.fromPreferences(this), - flowArticleListImage = FlowArticleListImagePreference.fromPreferences(this), - flowArticleListDesc = FlowArticleListDescPreference.fromPreferences(this), - flowArticleListTime = FlowArticleListTimePreference.fromPreferences(this), - flowArticleListDateStickyHeader = FlowArticleListDateStickyHeaderPreference.fromPreferences( - this - ), - flowArticleListTonalElevation = FlowArticleListTonalElevationPreference.fromPreferences(this), - - languages = LanguagesPreference.fromPreferences(this), - ) -} - @Composable fun SettingsProvider( content: @Composable () -> Unit, diff --git a/app/src/main/java/me/ash/reader/ui/page/home/FeedIcon.kt b/app/src/main/java/me/ash/reader/ui/component/FeedIcon.kt similarity index 97% rename from app/src/main/java/me/ash/reader/ui/page/home/FeedIcon.kt rename to app/src/main/java/me/ash/reader/ui/component/FeedIcon.kt index e52288c..d8b0030 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/FeedIcon.kt +++ b/app/src/main/java/me/ash/reader/ui/component/FeedIcon.kt @@ -1,4 +1,4 @@ -package me.ash.reader.ui.page.home +package me.ash.reader.ui.component import androidx.compose.foundation.background import androidx.compose.foundation.layout.Row diff --git a/app/src/main/java/me/ash/reader/ui/page/home/FilterBar.kt b/app/src/main/java/me/ash/reader/ui/component/FilterBar.kt similarity index 99% rename from app/src/main/java/me/ash/reader/ui/page/home/FilterBar.kt rename to app/src/main/java/me/ash/reader/ui/component/FilterBar.kt index cb7dad0..ba30443 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/FilterBar.kt +++ b/app/src/main/java/me/ash/reader/ui/component/FilterBar.kt @@ -1,4 +1,4 @@ -package me.ash.reader.ui.page.home +package me.ash.reader.ui.component import android.os.Build import android.view.SoundEffectConstants diff --git a/app/src/main/java/me/ash/reader/ui/component/AnimatedPopup.kt b/app/src/main/java/me/ash/reader/ui/component/base/AnimatedPopup.kt similarity index 97% rename from app/src/main/java/me/ash/reader/ui/component/AnimatedPopup.kt rename to app/src/main/java/me/ash/reader/ui/component/base/AnimatedPopup.kt index f294bf6..d70044e 100644 --- a/app/src/main/java/me/ash/reader/ui/component/AnimatedPopup.kt +++ b/app/src/main/java/me/ash/reader/ui/component/base/AnimatedPopup.kt @@ -1,4 +1,4 @@ -package me.ash.reader.ui.component +package me.ash.reader.ui.component.base import androidx.compose.animation.* import androidx.compose.foundation.layout.WindowInsets diff --git a/app/src/main/java/me/ash/reader/ui/component/AnimatedText.kt b/app/src/main/java/me/ash/reader/ui/component/base/AnimatedText.kt similarity index 98% rename from app/src/main/java/me/ash/reader/ui/component/AnimatedText.kt rename to app/src/main/java/me/ash/reader/ui/component/base/AnimatedText.kt index 614543c..7c7e76c 100644 --- a/app/src/main/java/me/ash/reader/ui/component/AnimatedText.kt +++ b/app/src/main/java/me/ash/reader/ui/component/base/AnimatedText.kt @@ -1,4 +1,4 @@ -package me.ash.reader.ui.component +package me.ash.reader.ui.component.base import androidx.compose.animation.* import androidx.compose.animation.core.FastOutSlowInEasing diff --git a/app/src/main/java/me/ash/reader/ui/component/AsyncImage.kt b/app/src/main/java/me/ash/reader/ui/component/base/AsyncImage.kt similarity index 98% rename from app/src/main/java/me/ash/reader/ui/component/AsyncImage.kt rename to app/src/main/java/me/ash/reader/ui/component/base/AsyncImage.kt index 8ac3cb9..039e2ce 100644 --- a/app/src/main/java/me/ash/reader/ui/component/AsyncImage.kt +++ b/app/src/main/java/me/ash/reader/ui/component/base/AsyncImage.kt @@ -1,4 +1,4 @@ -package me.ash.reader.ui.component +package me.ash.reader.ui.component.base import androidx.annotation.DrawableRes import androidx.compose.material3.MaterialTheme diff --git a/app/src/main/java/me/ash/reader/ui/component/Banner.kt b/app/src/main/java/me/ash/reader/ui/component/base/Banner.kt similarity index 98% rename from app/src/main/java/me/ash/reader/ui/component/Banner.kt rename to app/src/main/java/me/ash/reader/ui/component/base/Banner.kt index 3e235bf..e9d2807 100644 --- a/app/src/main/java/me/ash/reader/ui/component/Banner.kt +++ b/app/src/main/java/me/ash/reader/ui/component/base/Banner.kt @@ -6,7 +6,7 @@ * @modifier Ashinch */ -package me.ash.reader.ui.component +package me.ash.reader.ui.component.base import android.view.SoundEffectConstants import androidx.compose.animation.Crossfade diff --git a/app/src/main/java/me/ash/reader/ui/component/BlockButton.kt b/app/src/main/java/me/ash/reader/ui/component/base/BlockButton.kt similarity index 97% rename from app/src/main/java/me/ash/reader/ui/component/BlockButton.kt rename to app/src/main/java/me/ash/reader/ui/component/base/BlockButton.kt index 433b7e7..a73a361 100644 --- a/app/src/main/java/me/ash/reader/ui/component/BlockButton.kt +++ b/app/src/main/java/me/ash/reader/ui/component/base/BlockButton.kt @@ -1,4 +1,4 @@ -package me.ash.reader.ui.component +package me.ash.reader.ui.component.base import androidx.compose.foundation.background import androidx.compose.foundation.clickable diff --git a/app/src/main/java/me/ash/reader/ui/component/BlockRadioButton.kt b/app/src/main/java/me/ash/reader/ui/component/base/BlockRadioButton.kt similarity index 97% rename from app/src/main/java/me/ash/reader/ui/component/BlockRadioButton.kt rename to app/src/main/java/me/ash/reader/ui/component/base/BlockRadioButton.kt index d1633bb..adc29f1 100644 --- a/app/src/main/java/me/ash/reader/ui/component/BlockRadioButton.kt +++ b/app/src/main/java/me/ash/reader/ui/component/base/BlockRadioButton.kt @@ -1,4 +1,4 @@ -package me.ash.reader.ui.component +package me.ash.reader.ui.component.base import androidx.compose.foundation.layout.* import androidx.compose.runtime.Composable diff --git a/app/src/main/java/me/ash/reader/ui/component/BottomDrawer.kt b/app/src/main/java/me/ash/reader/ui/component/base/BottomDrawer.kt similarity index 98% rename from app/src/main/java/me/ash/reader/ui/component/BottomDrawer.kt rename to app/src/main/java/me/ash/reader/ui/component/base/BottomDrawer.kt index ff09f00..095f61c 100644 --- a/app/src/main/java/me/ash/reader/ui/component/BottomDrawer.kt +++ b/app/src/main/java/me/ash/reader/ui/component/base/BottomDrawer.kt @@ -1,4 +1,4 @@ -package me.ash.reader.ui.component +package me.ash.reader.ui.component.base import androidx.compose.foundation.background import androidx.compose.foundation.layout.* diff --git a/app/src/main/java/me/ash/reader/ui/component/CanBeDisabledIconButton.kt b/app/src/main/java/me/ash/reader/ui/component/base/CanBeDisabledIconButton.kt similarity index 96% rename from app/src/main/java/me/ash/reader/ui/component/CanBeDisabledIconButton.kt rename to app/src/main/java/me/ash/reader/ui/component/base/CanBeDisabledIconButton.kt index 9d5852d..10388f9 100644 --- a/app/src/main/java/me/ash/reader/ui/component/CanBeDisabledIconButton.kt +++ b/app/src/main/java/me/ash/reader/ui/component/base/CanBeDisabledIconButton.kt @@ -1,4 +1,4 @@ -package me.ash.reader.ui.component +package me.ash.reader.ui.component.base import androidx.compose.foundation.layout.size import androidx.compose.material3.Icon diff --git a/app/src/main/java/me/ash/reader/ui/component/ClipboardTextField.kt b/app/src/main/java/me/ash/reader/ui/component/base/ClipboardTextField.kt similarity index 98% rename from app/src/main/java/me/ash/reader/ui/component/ClipboardTextField.kt rename to app/src/main/java/me/ash/reader/ui/component/base/ClipboardTextField.kt index 90dcd00..e48d901 100644 --- a/app/src/main/java/me/ash/reader/ui/component/ClipboardTextField.kt +++ b/app/src/main/java/me/ash/reader/ui/component/base/ClipboardTextField.kt @@ -1,4 +1,4 @@ -package me.ash.reader.ui.component +package me.ash.reader.ui.component.base import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.layout.Column diff --git a/app/src/main/java/me/ash/reader/ui/component/CurlyCornerShape.kt b/app/src/main/java/me/ash/reader/ui/component/base/CurlyCornerShape.kt similarity index 98% rename from app/src/main/java/me/ash/reader/ui/component/CurlyCornerShape.kt rename to app/src/main/java/me/ash/reader/ui/component/base/CurlyCornerShape.kt index 39821e3..0cf4df3 100644 --- a/app/src/main/java/me/ash/reader/ui/component/CurlyCornerShape.kt +++ b/app/src/main/java/me/ash/reader/ui/component/base/CurlyCornerShape.kt @@ -1,4 +1,4 @@ -package me.ash.reader.ui.component +package me.ash.reader.ui.component.base import androidx.compose.foundation.shape.CornerBasedShape import androidx.compose.foundation.shape.CornerSize diff --git a/app/src/main/java/me/ash/reader/ui/component/Dialog.kt b/app/src/main/java/me/ash/reader/ui/component/base/Dialog.kt similarity index 95% rename from app/src/main/java/me/ash/reader/ui/component/Dialog.kt rename to app/src/main/java/me/ash/reader/ui/component/base/Dialog.kt index 5d90164..ff7f6c0 100644 --- a/app/src/main/java/me/ash/reader/ui/component/Dialog.kt +++ b/app/src/main/java/me/ash/reader/ui/component/base/Dialog.kt @@ -1,4 +1,4 @@ -package me.ash.reader.ui.component +package me.ash.reader.ui.component.base import androidx.compose.material3.AlertDialog import androidx.compose.runtime.Composable diff --git a/app/src/main/java/me/ash/reader/ui/component/DisplayText.kt b/app/src/main/java/me/ash/reader/ui/component/base/DisplayText.kt similarity index 97% rename from app/src/main/java/me/ash/reader/ui/component/DisplayText.kt rename to app/src/main/java/me/ash/reader/ui/component/base/DisplayText.kt index a2fffee..965b22d 100644 --- a/app/src/main/java/me/ash/reader/ui/component/DisplayText.kt +++ b/app/src/main/java/me/ash/reader/ui/component/base/DisplayText.kt @@ -1,4 +1,4 @@ -package me.ash.reader.ui.component +package me.ash.reader.ui.component.base import androidx.compose.animation.* import androidx.compose.foundation.layout.Column diff --git a/app/src/main/java/me/ash/reader/ui/component/DynamicSVGImage.kt b/app/src/main/java/me/ash/reader/ui/component/base/DynamicSVGImage.kt similarity index 97% rename from app/src/main/java/me/ash/reader/ui/component/DynamicSVGImage.kt rename to app/src/main/java/me/ash/reader/ui/component/base/DynamicSVGImage.kt index 5b93670..da97da5 100644 --- a/app/src/main/java/me/ash/reader/ui/component/DynamicSVGImage.kt +++ b/app/src/main/java/me/ash/reader/ui/component/base/DynamicSVGImage.kt @@ -1,4 +1,4 @@ -package me.ash.reader.ui.component +package me.ash.reader.ui.component.base import android.graphics.drawable.PictureDrawable import androidx.compose.animation.Crossfade diff --git a/app/src/main/java/me/ash/reader/ui/component/FeedbackIconButton.kt b/app/src/main/java/me/ash/reader/ui/component/base/FeedbackIconButton.kt similarity index 98% rename from app/src/main/java/me/ash/reader/ui/component/FeedbackIconButton.kt rename to app/src/main/java/me/ash/reader/ui/component/base/FeedbackIconButton.kt index 9c2433d..20c8318 100644 --- a/app/src/main/java/me/ash/reader/ui/component/FeedbackIconButton.kt +++ b/app/src/main/java/me/ash/reader/ui/component/base/FeedbackIconButton.kt @@ -1,4 +1,4 @@ -package me.ash.reader.ui.component +package me.ash.reader.ui.component.base import android.view.HapticFeedbackConstants import android.view.SoundEffectConstants diff --git a/app/src/main/java/me/ash/reader/ui/component/Menu.kt b/app/src/main/java/me/ash/reader/ui/component/base/Menu.kt similarity index 97% rename from app/src/main/java/me/ash/reader/ui/component/Menu.kt rename to app/src/main/java/me/ash/reader/ui/component/base/Menu.kt index 984b225..1b41382 100644 --- a/app/src/main/java/me/ash/reader/ui/component/Menu.kt +++ b/app/src/main/java/me/ash/reader/ui/component/base/Menu.kt @@ -1,4 +1,4 @@ -package me.ash.reader.ui.component +package me.ash.reader.ui.component.base import androidx.compose.foundation.layout.Box import androidx.compose.material3.DropdownMenu diff --git a/app/src/main/java/me/ash/reader/ui/component/RadioDialog.kt b/app/src/main/java/me/ash/reader/ui/component/base/RadioDialog.kt similarity index 98% rename from app/src/main/java/me/ash/reader/ui/component/RadioDialog.kt rename to app/src/main/java/me/ash/reader/ui/component/base/RadioDialog.kt index 3b447a4..d7b7136 100644 --- a/app/src/main/java/me/ash/reader/ui/component/RadioDialog.kt +++ b/app/src/main/java/me/ash/reader/ui/component/base/RadioDialog.kt @@ -1,4 +1,4 @@ -package me.ash.reader.ui.component +package me.ash.reader.ui.component.base import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Row diff --git a/app/src/main/java/me/ash/reader/ui/component/SelectionChip.kt b/app/src/main/java/me/ash/reader/ui/component/base/SelectionChip.kt similarity index 98% rename from app/src/main/java/me/ash/reader/ui/component/SelectionChip.kt rename to app/src/main/java/me/ash/reader/ui/component/base/SelectionChip.kt index 5da94a1..dc9769b 100644 --- a/app/src/main/java/me/ash/reader/ui/component/SelectionChip.kt +++ b/app/src/main/java/me/ash/reader/ui/component/base/SelectionChip.kt @@ -1,4 +1,4 @@ -package me.ash.reader.ui.component +package me.ash.reader.ui.component.base import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.interaction.MutableInteractionSource diff --git a/app/src/main/java/me/ash/reader/ui/component/SubTitle.kt b/app/src/main/java/me/ash/reader/ui/component/base/SubTitle.kt similarity index 94% rename from app/src/main/java/me/ash/reader/ui/component/SubTitle.kt rename to app/src/main/java/me/ash/reader/ui/component/base/SubTitle.kt index ec50ae2..f45a5cb 100644 --- a/app/src/main/java/me/ash/reader/ui/component/SubTitle.kt +++ b/app/src/main/java/me/ash/reader/ui/component/base/SubTitle.kt @@ -1,4 +1,4 @@ -package me.ash.reader.ui.component +package me.ash.reader.ui.component.base import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding diff --git a/app/src/main/java/me/ash/reader/ui/component/SwipeRefresh.kt b/app/src/main/java/me/ash/reader/ui/component/base/SwipeRefresh.kt similarity index 96% rename from app/src/main/java/me/ash/reader/ui/component/SwipeRefresh.kt rename to app/src/main/java/me/ash/reader/ui/component/base/SwipeRefresh.kt index 0664f3a..5e2d311 100644 --- a/app/src/main/java/me/ash/reader/ui/component/SwipeRefresh.kt +++ b/app/src/main/java/me/ash/reader/ui/component/base/SwipeRefresh.kt @@ -1,4 +1,4 @@ -package me.ash.reader.ui.component +package me.ash.reader.ui.component.base import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable diff --git a/app/src/main/java/me/ash/reader/ui/component/Switch.kt b/app/src/main/java/me/ash/reader/ui/component/base/Switch.kt similarity index 98% rename from app/src/main/java/me/ash/reader/ui/component/Switch.kt rename to app/src/main/java/me/ash/reader/ui/component/base/Switch.kt index 53b545c..ea2640a 100644 --- a/app/src/main/java/me/ash/reader/ui/component/Switch.kt +++ b/app/src/main/java/me/ash/reader/ui/component/base/Switch.kt @@ -6,7 +6,7 @@ * @modifier Ashinch */ -package me.ash.reader.ui.component +package me.ash.reader.ui.component.base import androidx.compose.animation.animateColorAsState import androidx.compose.animation.core.animateDpAsState diff --git a/app/src/main/java/me/ash/reader/ui/component/TextField.kt b/app/src/main/java/me/ash/reader/ui/component/base/TextField.kt similarity index 98% rename from app/src/main/java/me/ash/reader/ui/component/TextField.kt rename to app/src/main/java/me/ash/reader/ui/component/base/TextField.kt index 63f11d2..a697795 100644 --- a/app/src/main/java/me/ash/reader/ui/component/TextField.kt +++ b/app/src/main/java/me/ash/reader/ui/component/base/TextField.kt @@ -1,4 +1,4 @@ -package me.ash.reader.ui.component +package me.ash.reader.ui.component.base import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions diff --git a/app/src/main/java/me/ash/reader/ui/component/TextFieldDialog.kt b/app/src/main/java/me/ash/reader/ui/component/base/TextFieldDialog.kt similarity index 96% rename from app/src/main/java/me/ash/reader/ui/component/TextFieldDialog.kt rename to app/src/main/java/me/ash/reader/ui/component/base/TextFieldDialog.kt index 5f7adb3..b92d2e7 100644 --- a/app/src/main/java/me/ash/reader/ui/component/TextFieldDialog.kt +++ b/app/src/main/java/me/ash/reader/ui/component/base/TextFieldDialog.kt @@ -1,4 +1,4 @@ -package me.ash.reader.ui.component +package me.ash.reader.ui.component.base import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme @@ -13,7 +13,6 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.window.DialogProperties -import com.google.accompanist.pager.ExperimentalPagerApi import me.ash.reader.R @Composable diff --git a/app/src/main/java/me/ash/reader/ui/component/Tips.kt b/app/src/main/java/me/ash/reader/ui/component/base/Tips.kt similarity index 96% rename from app/src/main/java/me/ash/reader/ui/component/Tips.kt rename to app/src/main/java/me/ash/reader/ui/component/base/Tips.kt index bab7833..4f9d519 100644 --- a/app/src/main/java/me/ash/reader/ui/component/Tips.kt +++ b/app/src/main/java/me/ash/reader/ui/component/base/Tips.kt @@ -1,4 +1,4 @@ -package me.ash.reader.ui.component +package me.ash.reader.ui.component.base import androidx.compose.foundation.layout.* import androidx.compose.material.icons.Icons diff --git a/app/src/main/java/me/ash/reader/ui/component/ViewPager.kt b/app/src/main/java/me/ash/reader/ui/component/base/ViewPager.kt similarity index 96% rename from app/src/main/java/me/ash/reader/ui/component/ViewPager.kt rename to app/src/main/java/me/ash/reader/ui/component/base/ViewPager.kt index 4836c07..599c027 100644 --- a/app/src/main/java/me/ash/reader/ui/component/ViewPager.kt +++ b/app/src/main/java/me/ash/reader/ui/component/base/ViewPager.kt @@ -1,4 +1,4 @@ -package me.ash.reader.ui.component +package me.ash.reader.ui.component.base import androidx.compose.animation.animateContentSize import androidx.compose.foundation.layout.fillMaxWidth diff --git a/app/src/main/java/me/ash/reader/ui/component/WebView.kt b/app/src/main/java/me/ash/reader/ui/component/base/WebView.kt similarity index 99% rename from app/src/main/java/me/ash/reader/ui/component/WebView.kt rename to app/src/main/java/me/ash/reader/ui/component/base/WebView.kt index ea2634a..ae3cf06 100644 --- a/app/src/main/java/me/ash/reader/ui/component/WebView.kt +++ b/app/src/main/java/me/ash/reader/ui/component/base/WebView.kt @@ -1,4 +1,4 @@ -package me.ash.reader.ui.component +package me.ash.reader.ui.component.base import android.content.Intent import android.net.Uri diff --git a/app/src/main/java/me/ash/reader/ui/component/reader/HtmlToComposable.kt b/app/src/main/java/me/ash/reader/ui/component/reader/HtmlToComposable.kt index 9e66174..4288193 100644 --- a/app/src/main/java/me/ash/reader/ui/component/reader/HtmlToComposable.kt +++ b/app/src/main/java/me/ash/reader/ui/component/reader/HtmlToComposable.kt @@ -51,7 +51,7 @@ import coil.size.Precision import coil.size.Size import coil.size.pxOrElse import me.ash.reader.R -import me.ash.reader.ui.component.AsyncImage +import me.ash.reader.ui.component.base.AsyncImage import org.jsoup.Jsoup import org.jsoup.helper.StringUtil import org.jsoup.nodes.Element diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedItem.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedItem.kt index 43e1814..f80a65d 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedItem.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedItem.kt @@ -20,9 +20,9 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import me.ash.reader.data.entity.Feed -import me.ash.reader.ui.page.home.FeedIcon -import me.ash.reader.ui.page.home.feeds.option.feed.FeedOptionViewAction -import me.ash.reader.ui.page.home.feeds.option.feed.FeedOptionViewModel +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 @OptIn( diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedsPage.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedsPage.kt index d522d5c..d13a200 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedsPage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedsPage.kt @@ -1,6 +1,5 @@ package me.ash.reader.ui.page.home.feeds -import android.annotation.SuppressLint import androidx.activity.compose.BackHandler import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts @@ -32,24 +31,23 @@ import me.ash.reader.data.model.getName import me.ash.reader.data.model.toVersion import me.ash.reader.data.preference.* import me.ash.reader.data.repository.SyncWorker.Companion.getIsSyncing -import me.ash.reader.ui.component.Banner -import me.ash.reader.ui.component.DisplayText -import me.ash.reader.ui.component.FeedbackIconButton -import me.ash.reader.ui.component.Subtitle +import me.ash.reader.ui.component.base.Banner +import me.ash.reader.ui.component.base.DisplayText +import me.ash.reader.ui.component.base.FeedbackIconButton +import me.ash.reader.ui.component.base.Subtitle import me.ash.reader.ui.ext.* import me.ash.reader.ui.page.common.RouteName -import me.ash.reader.ui.page.home.FilterBar +import me.ash.reader.ui.component.FilterBar 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.option.feed.FeedOptionDrawer -import me.ash.reader.ui.page.home.feeds.option.group.GroupOptionDrawer +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 import me.ash.reader.ui.theme.palette.onDark -@SuppressLint("FlowOperatorInvokedInComposition") @OptIn( ExperimentalMaterial3Api::class, com.google.accompanist.pager.ExperimentalPagerApi::class, androidx.compose.foundation.ExperimentalFoundationApi::class diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/GroupItem.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/GroupItem.kt index 9d6559f..3afdd20 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/GroupItem.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/GroupItem.kt @@ -28,8 +28,8 @@ 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.option.group.GroupOptionViewAction -import me.ash.reader.ui.page.home.feeds.option.group.GroupOptionViewModel +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) @Composable diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/feed/ClearFeedDialog.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/feed/ClearFeedDialog.kt similarity index 94% rename from app/src/main/java/me/ash/reader/ui/page/home/feeds/option/feed/ClearFeedDialog.kt rename to app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/feed/ClearFeedDialog.kt index ef28388..0ead3a7 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/feed/ClearFeedDialog.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/feed/ClearFeedDialog.kt @@ -1,4 +1,4 @@ -package me.ash.reader.ui.page.home.feeds.option.feed +package me.ash.reader.ui.page.home.feeds.drawer.feed import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.DeleteForever @@ -11,9 +11,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource 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.component.base.Dialog import me.ash.reader.ui.ext.collectAsStateValue import me.ash.reader.ui.ext.showToast diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/feed/DeleteFeedDialog.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/feed/DeleteFeedDialog.kt similarity index 94% rename from app/src/main/java/me/ash/reader/ui/page/home/feeds/option/feed/DeleteFeedDialog.kt rename to app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/feed/DeleteFeedDialog.kt index f0a5c5d..c977abe 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/feed/DeleteFeedDialog.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/feed/DeleteFeedDialog.kt @@ -1,4 +1,4 @@ -package me.ash.reader.ui.page.home.feeds.option.feed +package me.ash.reader.ui.page.home.feeds.drawer.feed import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.DeleteForever @@ -11,9 +11,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource 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.component.base.Dialog import me.ash.reader.ui.ext.collectAsStateValue import me.ash.reader.ui.ext.showToast diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/feed/FeedOptionDrawer.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/feed/FeedOptionDrawer.kt similarity index 97% rename from app/src/main/java/me/ash/reader/ui/page/home/feeds/option/feed/FeedOptionDrawer.kt rename to app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/feed/FeedOptionDrawer.kt index 843b495..d3d03b0 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/feed/FeedOptionDrawer.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/feed/FeedOptionDrawer.kt @@ -1,4 +1,4 @@ -package me.ash.reader.ui.page.home.feeds.option.feed +package me.ash.reader.ui.page.home.feeds.drawer.feed import androidx.activity.compose.BackHandler import androidx.compose.foundation.layout.* @@ -19,12 +19,12 @@ 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.BottomDrawer -import me.ash.reader.ui.component.TextFieldDialog +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.page.home.FeedIcon +import me.ash.reader.ui.component.FeedIcon import me.ash.reader.ui.page.home.feeds.subscribe.ResultView @OptIn(ExperimentalMaterialApi::class) diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/feed/FeedOptionViewModel.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/feed/FeedOptionViewModel.kt similarity index 99% rename from app/src/main/java/me/ash/reader/ui/page/home/feeds/option/feed/FeedOptionViewModel.kt rename to app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/feed/FeedOptionViewModel.kt index 0488eee..00db51a 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/feed/FeedOptionViewModel.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/feed/FeedOptionViewModel.kt @@ -1,11 +1,10 @@ -package me.ash.reader.ui.page.home.feeds.option.feed +package me.ash.reader.ui.page.home.feeds.drawer.feed import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.ModalBottomSheetState import androidx.compose.material.ModalBottomSheetValue 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.CoroutineScope diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/group/AllAllowNotificationDialog.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/AllAllowNotificationDialog.kt similarity index 94% rename from app/src/main/java/me/ash/reader/ui/page/home/feeds/option/group/AllAllowNotificationDialog.kt rename to app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/AllAllowNotificationDialog.kt index d3a7634..6ca6e7f 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/group/AllAllowNotificationDialog.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/AllAllowNotificationDialog.kt @@ -1,4 +1,4 @@ -package me.ash.reader.ui.page.home.feeds.option.group +package me.ash.reader.ui.page.home.feeds.drawer.group import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.Notifications @@ -11,9 +11,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource 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.component.base.Dialog import me.ash.reader.ui.ext.collectAsStateValue import me.ash.reader.ui.ext.showToast diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/group/AllMoveToGroupDialog.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/AllMoveToGroupDialog.kt similarity index 94% rename from app/src/main/java/me/ash/reader/ui/page/home/feeds/option/group/AllMoveToGroupDialog.kt rename to app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/AllMoveToGroupDialog.kt index 2aa13ce..7e39a85 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/group/AllMoveToGroupDialog.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/AllMoveToGroupDialog.kt @@ -1,4 +1,4 @@ -package me.ash.reader.ui.page.home.feeds.option.group +package me.ash.reader.ui.page.home.feeds.drawer.group import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.DriveFileMove @@ -11,9 +11,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource 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.component.base.Dialog import me.ash.reader.ui.ext.collectAsStateValue import me.ash.reader.ui.ext.showToast diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/group/AllParseFullContentDialog.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/AllParseFullContentDialog.kt similarity index 94% rename from app/src/main/java/me/ash/reader/ui/page/home/feeds/option/group/AllParseFullContentDialog.kt rename to app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/AllParseFullContentDialog.kt index 43d844c..bc8a92f 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/group/AllParseFullContentDialog.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/AllParseFullContentDialog.kt @@ -1,4 +1,4 @@ -package me.ash.reader.ui.page.home.feeds.option.group +package me.ash.reader.ui.page.home.feeds.drawer.group import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.Article @@ -11,9 +11,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource 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.component.base.Dialog import me.ash.reader.ui.ext.collectAsStateValue import me.ash.reader.ui.ext.showToast diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/group/ClearGroupDialog.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/ClearGroupDialog.kt similarity index 94% rename from app/src/main/java/me/ash/reader/ui/page/home/feeds/option/group/ClearGroupDialog.kt rename to app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/ClearGroupDialog.kt index 00f26bb..ce02471 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/group/ClearGroupDialog.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/ClearGroupDialog.kt @@ -1,4 +1,4 @@ -package me.ash.reader.ui.page.home.feeds.option.group +package me.ash.reader.ui.page.home.feeds.drawer.group import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.DeleteForever @@ -11,9 +11,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource 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.component.base.Dialog import me.ash.reader.ui.ext.collectAsStateValue import me.ash.reader.ui.ext.showToast diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/group/DeleteGroupDialog.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/DeleteGroupDialog.kt similarity index 94% rename from app/src/main/java/me/ash/reader/ui/page/home/feeds/option/group/DeleteGroupDialog.kt rename to app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/DeleteGroupDialog.kt index d9e96fb..ffa3a48 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/group/DeleteGroupDialog.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/DeleteGroupDialog.kt @@ -1,4 +1,4 @@ -package me.ash.reader.ui.page.home.feeds.option.group +package me.ash.reader.ui.page.home.feeds.drawer.group import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.DeleteForever @@ -11,9 +11,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource 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.component.base.Dialog import me.ash.reader.ui.ext.collectAsStateValue import me.ash.reader.ui.ext.showToast diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/group/GroupOptionDrawer.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/GroupOptionDrawer.kt similarity index 97% rename from app/src/main/java/me/ash/reader/ui/page/home/feeds/option/group/GroupOptionDrawer.kt rename to app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/GroupOptionDrawer.kt index 480aa1f..ca30d36 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/group/GroupOptionDrawer.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/GroupOptionDrawer.kt @@ -1,4 +1,4 @@ -package me.ash.reader.ui.page.home.feeds.option.group +package me.ash.reader.ui.page.home.feeds.drawer.group import android.content.Context import androidx.activity.compose.BackHandler @@ -33,10 +33,10 @@ import com.google.accompanist.flowlayout.MainAxisAlignment import kotlinx.coroutines.launch import me.ash.reader.R import me.ash.reader.data.entity.Group -import me.ash.reader.ui.component.BottomDrawer -import me.ash.reader.ui.component.SelectionChip -import me.ash.reader.ui.component.Subtitle -import me.ash.reader.ui.component.TextFieldDialog +import me.ash.reader.ui.component.base.BottomDrawer +import me.ash.reader.ui.component.base.SelectionChip +import me.ash.reader.ui.component.base.Subtitle +import me.ash.reader.ui.component.base.TextFieldDialog import me.ash.reader.ui.ext.* @OptIn(ExperimentalMaterialApi::class) diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/group/GroupOptionViewModel.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/GroupOptionViewModel.kt similarity index 99% rename from app/src/main/java/me/ash/reader/ui/page/home/feeds/option/group/GroupOptionViewModel.kt rename to app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/GroupOptionViewModel.kt index f86d43c..e3226c8 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/option/group/GroupOptionViewModel.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/GroupOptionViewModel.kt @@ -1,4 +1,4 @@ -package me.ash.reader.ui.page.home.feeds.option.group +package me.ash.reader.ui.page.home.feeds.drawer.group import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.ModalBottomSheetState diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/ResultView.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/ResultView.kt index 65894e6..f6d6dce 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/ResultView.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/ResultView.kt @@ -30,8 +30,8 @@ import com.google.accompanist.flowlayout.FlowRow import com.google.accompanist.flowlayout.MainAxisAlignment import me.ash.reader.R import me.ash.reader.data.entity.Group -import me.ash.reader.ui.component.SelectionChip -import me.ash.reader.ui.component.Subtitle +import me.ash.reader.ui.component.base.SelectionChip +import me.ash.reader.ui.component.base.Subtitle import me.ash.reader.ui.ext.roundClick import me.ash.reader.ui.theme.palette.alwaysLight 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 522341b..02a6e6e 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 @@ -25,9 +25,9 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.window.DialogProperties import androidx.hilt.navigation.compose.hiltViewModel import me.ash.reader.R -import me.ash.reader.ui.component.ClipboardTextField -import me.ash.reader.ui.component.Dialog -import me.ash.reader.ui.component.TextFieldDialog +import me.ash.reader.ui.component.base.ClipboardTextField +import me.ash.reader.ui.component.base.Dialog +import me.ash.reader.ui.component.base.TextFieldDialog import me.ash.reader.ui.ext.collectAsStateValue @OptIn( diff --git a/app/src/main/java/me/ash/reader/ui/page/home/flow/ArticleItem.kt b/app/src/main/java/me/ash/reader/ui/page/home/flow/ArticleItem.kt index 045f6cd..183265e 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/flow/ArticleItem.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/flow/ArticleItem.kt @@ -23,10 +23,10 @@ import coil.size.Scale import me.ash.reader.R import me.ash.reader.data.entity.ArticleWithFeed import me.ash.reader.data.preference.* -import me.ash.reader.ui.component.AsyncImage -import me.ash.reader.ui.component.Size_1000 +import me.ash.reader.ui.component.base.AsyncImage import me.ash.reader.ui.ext.formatAsString -import me.ash.reader.ui.page.home.FeedIcon +import me.ash.reader.ui.component.FeedIcon +import me.ash.reader.ui.component.base.Size_1000 @Composable fun ArticleItem( diff --git a/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowPage.kt b/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowPage.kt index 81dd07f..0c02b7e 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowPage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowPage.kt @@ -28,13 +28,13 @@ import me.ash.reader.R import me.ash.reader.data.model.getName import me.ash.reader.data.preference.* import me.ash.reader.data.repository.SyncWorker.Companion.getIsSyncing -import me.ash.reader.ui.component.DisplayText -import me.ash.reader.ui.component.FeedbackIconButton -import me.ash.reader.ui.component.SwipeRefresh +import me.ash.reader.ui.component.FilterBar +import me.ash.reader.ui.component.base.DisplayText +import me.ash.reader.ui.component.base.FeedbackIconButton +import me.ash.reader.ui.component.base.SwipeRefresh import me.ash.reader.ui.ext.collectAsStateValue import me.ash.reader.ui.ext.surfaceColorAtElevation import me.ash.reader.ui.page.common.RouteName -import me.ash.reader.ui.page.home.FilterBar import me.ash.reader.ui.page.home.FilterState import me.ash.reader.ui.page.home.HomeViewAction import me.ash.reader.ui.page.home.HomeViewModel diff --git a/app/src/main/java/me/ash/reader/ui/page/home/flow/MarkAsReadBar.kt b/app/src/main/java/me/ash/reader/ui/page/home/flow/MarkAsReadBar.kt index 9458e08..5393f19 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/flow/MarkAsReadBar.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/flow/MarkAsReadBar.kt @@ -23,7 +23,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import me.ash.reader.R -import me.ash.reader.ui.component.AnimatedPopup +import me.ash.reader.ui.component.base.AnimatedPopup import me.ash.reader.ui.theme.palette.alwaysLight @Composable diff --git a/app/src/main/java/me/ash/reader/ui/page/home/read/ReadBar.kt b/app/src/main/java/me/ash/reader/ui/page/home/read/ReadBar.kt index ec642b1..14c7ac4 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/read/ReadBar.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/read/ReadBar.kt @@ -23,7 +23,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.compose.ui.zIndex import me.ash.reader.R -import me.ash.reader.ui.component.CanBeDisabledIconButton +import me.ash.reader.ui.component.base.CanBeDisabledIconButton @Composable fun ReadBar( diff --git a/app/src/main/java/me/ash/reader/ui/page/home/read/ReadPage.kt b/app/src/main/java/me/ash/reader/ui/page/home/read/ReadPage.kt index 940819a..0acd12b 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/read/ReadPage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/read/ReadPage.kt @@ -23,7 +23,7 @@ import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavHostController import me.ash.reader.R import me.ash.reader.data.entity.ArticleWithFeed -import me.ash.reader.ui.component.FeedbackIconButton +import me.ash.reader.ui.component.base.FeedbackIconButton import me.ash.reader.ui.component.reader.reader import me.ash.reader.ui.ext.collectAsStateValue import me.ash.reader.ui.ext.drawVerticalScrollbar diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/SettingsPage.kt b/app/src/main/java/me/ash/reader/ui/page/settings/SettingsPage.kt index 620bc52..79d61f3 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/SettingsPage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/SettingsPage.kt @@ -20,9 +20,9 @@ import androidx.navigation.NavHostController import kotlinx.coroutines.flow.map import me.ash.reader.R import me.ash.reader.data.model.toVersion -import me.ash.reader.ui.component.Banner -import me.ash.reader.ui.component.DisplayText -import me.ash.reader.ui.component.FeedbackIconButton +import me.ash.reader.ui.component.base.Banner +import me.ash.reader.ui.component.base.DisplayText +import me.ash.reader.ui.component.base.FeedbackIconButton import me.ash.reader.ui.ext.DataStoreKeys import me.ash.reader.ui.ext.dataStore import me.ash.reader.ui.ext.getCurrentVersion diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/color/ColorAndStyle.kt b/app/src/main/java/me/ash/reader/ui/page/settings/color/ColorAndStyle.kt index 012a448..8b68ede 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/color/ColorAndStyle.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/color/ColorAndStyle.kt @@ -1,6 +1,5 @@ package me.ash.reader.ui.page.settings.color -import android.annotation.SuppressLint import android.content.Context import android.os.Build import androidx.compose.animation.* @@ -27,7 +26,7 @@ import androidx.compose.ui.unit.dp import androidx.navigation.NavHostController import me.ash.reader.R import me.ash.reader.data.preference.* -import me.ash.reader.ui.component.* +import me.ash.reader.ui.component.base.* import me.ash.reader.ui.page.common.RouteName import me.ash.reader.ui.page.settings.SettingItem import me.ash.reader.ui.svg.PALETTE @@ -36,7 +35,6 @@ import me.ash.reader.ui.theme.palette.* import me.ash.reader.ui.theme.palette.TonalPalettes.Companion.toTonalPalettes import me.ash.reader.ui.theme.palette.dynamic.extractTonalPalettesFromUserWallpaper -@SuppressLint("FlowOperatorInvokedInComposition") @OptIn(ExperimentalMaterial3Api::class) @Composable fun ColorAndStyle( @@ -157,7 +155,7 @@ fun ColorAndStyle( } }, ) { - Switch( + me.ash.reader.ui.component.base.Switch( activated = darkTheme.isDarkTheme() ) { darkThemeNot.put(context, scope) diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/color/DarkTheme.kt b/app/src/main/java/me/ash/reader/ui/page/settings/color/DarkTheme.kt index 83c0d11..e8d25c3 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/color/DarkTheme.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/color/DarkTheme.kt @@ -20,10 +20,10 @@ import me.ash.reader.data.preference.DarkThemePreference import me.ash.reader.data.preference.LocalAmoledDarkTheme import me.ash.reader.data.preference.LocalDarkTheme import me.ash.reader.data.preference.not -import me.ash.reader.ui.component.DisplayText -import me.ash.reader.ui.component.FeedbackIconButton -import me.ash.reader.ui.component.Subtitle -import me.ash.reader.ui.component.Switch +import me.ash.reader.ui.component.base.DisplayText +import me.ash.reader.ui.component.base.FeedbackIconButton +import me.ash.reader.ui.component.base.Subtitle +import me.ash.reader.ui.component.base.Switch import me.ash.reader.ui.page.settings.SettingItem import me.ash.reader.ui.theme.palette.onLight diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/color/feeds/FeedsPageStyle.kt b/app/src/main/java/me/ash/reader/ui/page/settings/color/feeds/FeedsPageStyle.kt index 2f7a95c..70d19c8 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/color/feeds/FeedsPageStyle.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/color/feeds/FeedsPageStyle.kt @@ -25,9 +25,9 @@ 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.preference.* -import me.ash.reader.ui.component.* +import me.ash.reader.ui.component.base.* import me.ash.reader.ui.ext.surfaceColorAtElevation -import me.ash.reader.ui.page.home.FilterBar +import me.ash.reader.ui.component.FilterBar import me.ash.reader.ui.page.home.feeds.GroupItem import me.ash.reader.ui.page.settings.SettingItem import me.ash.reader.ui.theme.palette.onDark diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/color/flow/FlowPageStyle.kt b/app/src/main/java/me/ash/reader/ui/page/settings/color/flow/FlowPageStyle.kt index 9471ece..ed8fb63 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/color/flow/FlowPageStyle.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/color/flow/FlowPageStyle.kt @@ -27,9 +27,9 @@ import me.ash.reader.data.entity.ArticleWithFeed import me.ash.reader.data.entity.Feed import me.ash.reader.data.model.Filter import me.ash.reader.data.preference.* -import me.ash.reader.ui.component.* +import me.ash.reader.ui.component.base.* import me.ash.reader.ui.ext.surfaceColorAtElevation -import me.ash.reader.ui.page.home.FilterBar +import me.ash.reader.ui.component.FilterBar import me.ash.reader.ui.page.home.flow.ArticleItem import me.ash.reader.ui.page.settings.SettingItem import me.ash.reader.ui.theme.palette.onDark @@ -158,7 +158,7 @@ fun FlowPageStyle( (!articleListFeedIcon).put(context, scope) }, ) { - Switch(activated = articleListFeedIcon.value) { + me.ash.reader.ui.component.base.Switch(activated = articleListFeedIcon.value) { (!articleListFeedIcon).put(context, scope) } } @@ -168,7 +168,7 @@ fun FlowPageStyle( (!articleListFeedName).put(context, scope) }, ) { - Switch(activated = articleListFeedName.value) { + me.ash.reader.ui.component.base.Switch(activated = articleListFeedName.value) { (!articleListFeedName).put(context, scope) } } @@ -178,7 +178,7 @@ fun FlowPageStyle( (!articleListImage).put(context, scope) }, ) { - Switch(activated = articleListImage.value) { + me.ash.reader.ui.component.base.Switch(activated = articleListImage.value) { (!articleListImage).put(context, scope) } } @@ -188,7 +188,7 @@ fun FlowPageStyle( (!articleListDesc).put(context, scope) }, ) { - Switch(activated = articleListDesc.value) { + me.ash.reader.ui.component.base.Switch(activated = articleListDesc.value) { (!articleListDesc).put(context, scope) } } @@ -198,7 +198,7 @@ fun FlowPageStyle( (!articleListTime).put(context, scope) }, ) { - Switch(activated = articleListTime.value) { + me.ash.reader.ui.component.base.Switch(activated = articleListTime.value) { (!articleListTime).put(context, scope) } } @@ -208,7 +208,7 @@ fun FlowPageStyle( (!articleListStickyDate).put(context, scope) }, ) { - Switch(activated = articleListStickyDate.value) { + me.ash.reader.ui.component.base.Switch(activated = articleListStickyDate.value) { (!articleListStickyDate).put(context, scope) } } @@ -242,7 +242,7 @@ fun FlowPageStyle( (!filterBarFilled).put(context, scope) }, ) { - Switch(activated = filterBarFilled.value) { + me.ash.reader.ui.component.base.Switch(activated = filterBarFilled.value) { (!filterBarFilled).put(context, scope) } } diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/interaction/Interaction.kt b/app/src/main/java/me/ash/reader/ui/page/settings/interaction/Interaction.kt index ca362b6..144e432 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/interaction/Interaction.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/interaction/Interaction.kt @@ -1,6 +1,5 @@ package me.ash.reader.ui.page.settings.interaction -import android.annotation.SuppressLint import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn @@ -17,13 +16,13 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import me.ash.reader.R import me.ash.reader.ui.component.* +import me.ash.reader.ui.component.base.* import me.ash.reader.ui.ext.DataStoreKeys import me.ash.reader.ui.ext.dataStore import me.ash.reader.ui.ext.put import me.ash.reader.ui.page.settings.SettingItem import me.ash.reader.ui.theme.palette.onLight -@SuppressLint("FlowOperatorInvokedInComposition") @OptIn(ExperimentalMaterial3Api::class) @Composable fun Interaction( diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/languages/Languages.kt b/app/src/main/java/me/ash/reader/ui/page/settings/languages/Languages.kt index 66edf01..927f1d2 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/languages/Languages.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/languages/Languages.kt @@ -23,9 +23,9 @@ import androidx.navigation.NavHostController import me.ash.reader.R import me.ash.reader.data.preference.LanguagesPreference import me.ash.reader.data.preference.LocalLanguages -import me.ash.reader.ui.component.Banner -import me.ash.reader.ui.component.DisplayText -import me.ash.reader.ui.component.FeedbackIconButton +import me.ash.reader.ui.component.base.Banner +import me.ash.reader.ui.component.base.DisplayText +import me.ash.reader.ui.component.base.FeedbackIconButton import me.ash.reader.ui.page.settings.SettingItem import me.ash.reader.ui.theme.palette.onLight diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/tips/TipsAndSupport.kt b/app/src/main/java/me/ash/reader/ui/page/settings/tips/TipsAndSupport.kt index b9f0b76..2da3e1d 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/tips/TipsAndSupport.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/tips/TipsAndSupport.kt @@ -36,8 +36,8 @@ import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavHostController import me.ash.reader.R -import me.ash.reader.ui.component.CurlyCornerShape -import me.ash.reader.ui.component.FeedbackIconButton +import me.ash.reader.ui.component.base.CurlyCornerShape +import me.ash.reader.ui.component.base.FeedbackIconButton import me.ash.reader.ui.ext.* import me.ash.reader.ui.theme.palette.alwaysLight import me.ash.reader.ui.theme.palette.onLight diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/tips/UpdateDialog.kt b/app/src/main/java/me/ash/reader/ui/page/settings/tips/UpdateDialog.kt index d1e1cf1..2dcd32b 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/tips/UpdateDialog.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/tips/UpdateDialog.kt @@ -35,7 +35,7 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import me.ash.reader.R import me.ash.reader.data.source.Download -import me.ash.reader.ui.component.Dialog +import me.ash.reader.ui.component.base.Dialog import me.ash.reader.ui.ext.* @SuppressLint("FlowOperatorInvokedInComposition") diff --git a/app/src/main/java/me/ash/reader/ui/page/startup/StartupPage.kt b/app/src/main/java/me/ash/reader/ui/page/startup/StartupPage.kt index cc30fbd..88d96b0 100644 --- a/app/src/main/java/me/ash/reader/ui/page/startup/StartupPage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/startup/StartupPage.kt @@ -18,9 +18,9 @@ import androidx.navigation.NavHostController import com.ireward.htmlcompose.HtmlText import kotlinx.coroutines.launch import me.ash.reader.R -import me.ash.reader.ui.component.DisplayText -import me.ash.reader.ui.component.DynamicSVGImage -import me.ash.reader.ui.component.Tips +import me.ash.reader.ui.component.base.DisplayText +import me.ash.reader.ui.component.base.DynamicSVGImage +import me.ash.reader.ui.component.base.Tips import me.ash.reader.ui.ext.DataStoreKeys import me.ash.reader.ui.ext.dataStore import me.ash.reader.ui.ext.put From 27e8780d82875e66ec83ea9a10c0350a1c31d76e Mon Sep 17 00:00:00 2001 From: Ash Date: Wed, 18 May 2022 03:59:39 +0800 Subject: [PATCH 10/18] Update Preferences --- .../NewVersionDownloadUrlPreference.kt | 23 +++++++++++ .../preference/NewVersionLogPreference.kt | 23 +++++++++++ .../preference/NewVersionNumberPreference.kt | 25 ++++++++++++ .../NewVersionPublishDatePreference.kt | 23 +++++++++++ .../preference/NewVersionSizePreference.kt | 28 ++++++++++++++ .../ash/reader/data/preference/Preference.kt | 7 ++++ .../me/ash/reader/data/preference/Settings.kt | 22 +++++++++++ .../preference/SkipVersionNumberPreference.kt | 25 ++++++++++++ .../reader/data/repository/AppRepository.kt | 22 ++++++----- .../java/me/ash/reader/ui/ext/DataStoreExt.kt | 16 ++------ .../reader/ui/page/home/feeds/FeedsPage.kt | 25 +++++------- .../reader/ui/page/settings/SettingsPage.kt | 27 +++++-------- .../ui/page/settings/tips/UpdateDialog.kt | 38 ++++++------------- 13 files changed, 222 insertions(+), 82 deletions(-) create mode 100644 app/src/main/java/me/ash/reader/data/preference/NewVersionDownloadUrlPreference.kt create mode 100644 app/src/main/java/me/ash/reader/data/preference/NewVersionLogPreference.kt create mode 100644 app/src/main/java/me/ash/reader/data/preference/NewVersionNumberPreference.kt create mode 100644 app/src/main/java/me/ash/reader/data/preference/NewVersionPublishDatePreference.kt create mode 100644 app/src/main/java/me/ash/reader/data/preference/NewVersionSizePreference.kt create mode 100644 app/src/main/java/me/ash/reader/data/preference/SkipVersionNumberPreference.kt diff --git a/app/src/main/java/me/ash/reader/data/preference/NewVersionDownloadUrlPreference.kt b/app/src/main/java/me/ash/reader/data/preference/NewVersionDownloadUrlPreference.kt new file mode 100644 index 0000000..0b0a5f8 --- /dev/null +++ b/app/src/main/java/me/ash/reader/data/preference/NewVersionDownloadUrlPreference.kt @@ -0,0 +1,23 @@ +package me.ash.reader.data.preference + +import android.content.Context +import androidx.datastore.preferences.core.Preferences +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import me.ash.reader.ui.ext.DataStoreKeys +import me.ash.reader.ui.ext.dataStore +import me.ash.reader.ui.ext.put + +object NewVersionDownloadUrlPreference { + const val default = "" + + fun put(context: Context, scope: CoroutineScope, value: String) { + scope.launch(Dispatchers.IO) { + context.dataStore.put(DataStoreKeys.NewVersionDownloadUrl, value) + } + } + + fun fromPreferences(preferences: Preferences) = + preferences[DataStoreKeys.NewVersionDownloadUrl.key] ?: default +} \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/data/preference/NewVersionLogPreference.kt b/app/src/main/java/me/ash/reader/data/preference/NewVersionLogPreference.kt new file mode 100644 index 0000000..151e7bf --- /dev/null +++ b/app/src/main/java/me/ash/reader/data/preference/NewVersionLogPreference.kt @@ -0,0 +1,23 @@ +package me.ash.reader.data.preference + +import android.content.Context +import androidx.datastore.preferences.core.Preferences +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import me.ash.reader.ui.ext.DataStoreKeys +import me.ash.reader.ui.ext.dataStore +import me.ash.reader.ui.ext.put + +object NewVersionLogPreference { + const val default = "" + + fun put(context: Context, scope: CoroutineScope, value: String) { + scope.launch(Dispatchers.IO) { + context.dataStore.put(DataStoreKeys.NewVersionLog, value) + } + } + + fun fromPreferences(preferences: Preferences) = + preferences[DataStoreKeys.NewVersionLog.key] ?: default +} \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/data/preference/NewVersionNumberPreference.kt b/app/src/main/java/me/ash/reader/data/preference/NewVersionNumberPreference.kt new file mode 100644 index 0000000..29424b1 --- /dev/null +++ b/app/src/main/java/me/ash/reader/data/preference/NewVersionNumberPreference.kt @@ -0,0 +1,25 @@ +package me.ash.reader.data.preference + +import android.content.Context +import androidx.datastore.preferences.core.Preferences +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import me.ash.reader.data.model.Version +import me.ash.reader.data.model.toVersion +import me.ash.reader.ui.ext.DataStoreKeys +import me.ash.reader.ui.ext.dataStore +import me.ash.reader.ui.ext.put + +object NewVersionNumberPreference { + val default = Version() + + fun put(context: Context, scope: CoroutineScope, value: String) { + scope.launch(Dispatchers.IO) { + context.dataStore.put(DataStoreKeys.NewVersionNumber, value) + } + } + + fun fromPreferences(preferences: Preferences) = + preferences[DataStoreKeys.NewVersionNumber.key].toVersion() +} \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/data/preference/NewVersionPublishDatePreference.kt b/app/src/main/java/me/ash/reader/data/preference/NewVersionPublishDatePreference.kt new file mode 100644 index 0000000..b7fd390 --- /dev/null +++ b/app/src/main/java/me/ash/reader/data/preference/NewVersionPublishDatePreference.kt @@ -0,0 +1,23 @@ +package me.ash.reader.data.preference + +import android.content.Context +import androidx.datastore.preferences.core.Preferences +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import me.ash.reader.ui.ext.DataStoreKeys +import me.ash.reader.ui.ext.dataStore +import me.ash.reader.ui.ext.put + +object NewVersionPublishDatePreference { + const val default = "" + + fun put(context: Context, scope: CoroutineScope, value: String) { + scope.launch(Dispatchers.IO) { + context.dataStore.put(DataStoreKeys.NewVersionPublishDate, value) + } + } + + fun fromPreferences(preferences: Preferences) = + preferences[DataStoreKeys.NewVersionPublishDate.key] ?: default +} \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/data/preference/NewVersionSizePreference.kt b/app/src/main/java/me/ash/reader/data/preference/NewVersionSizePreference.kt new file mode 100644 index 0000000..eac3dff --- /dev/null +++ b/app/src/main/java/me/ash/reader/data/preference/NewVersionSizePreference.kt @@ -0,0 +1,28 @@ +package me.ash.reader.data.preference + +import android.content.Context +import androidx.datastore.preferences.core.Preferences +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import me.ash.reader.ui.ext.DataStoreKeys +import me.ash.reader.ui.ext.dataStore +import me.ash.reader.ui.ext.put + +object NewVersionSizePreference { + const val default = "" + + fun Int.formatSize(): String = + (this / 1024f / 1024f) + .takeIf { it > 0f } + ?.run { " ${String.format("%.2f", this)} MB" } ?: "" + + fun put(context: Context, scope: CoroutineScope, value: String) { + scope.launch(Dispatchers.IO) { + context.dataStore.put(DataStoreKeys.NewVersionSize, value) + } + } + + fun fromPreferences(preferences: Preferences) = + preferences[DataStoreKeys.NewVersionSize.key] ?: default +} \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/data/preference/Preference.kt b/app/src/main/java/me/ash/reader/data/preference/Preference.kt index 0ea515e..2be1edf 100644 --- a/app/src/main/java/me/ash/reader/data/preference/Preference.kt +++ b/app/src/main/java/me/ash/reader/data/preference/Preference.kt @@ -10,6 +10,13 @@ sealed class Preference { fun Preferences.toSettings(): Settings { return Settings( + newVersionNumber = NewVersionNumberPreference.fromPreferences(this), + skipVersionNumber = SkipVersionNumberPreference.fromPreferences(this), + newVersionPublishDate = NewVersionPublishDatePreference.fromPreferences(this), + newVersionLog = NewVersionLogPreference.fromPreferences(this), + newVersionSize = NewVersionSizePreference.fromPreferences(this), + newVersionDownloadUrl = NewVersionDownloadUrlPreference.fromPreferences(this), + themeIndex = ThemeIndexPreference.fromPreferences(this), customPrimaryColor = CustomPrimaryColorPreference.fromPreferences(this), darkTheme = DarkThemePreference.fromPreferences(this), diff --git a/app/src/main/java/me/ash/reader/data/preference/Settings.kt b/app/src/main/java/me/ash/reader/data/preference/Settings.kt index 632662b..f0bd8c1 100644 --- a/app/src/main/java/me/ash/reader/data/preference/Settings.kt +++ b/app/src/main/java/me/ash/reader/data/preference/Settings.kt @@ -7,10 +7,18 @@ import androidx.compose.runtime.compositionLocalOf import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalContext import kotlinx.coroutines.flow.map +import me.ash.reader.data.model.Version import me.ash.reader.ui.ext.collectAsStateValue import me.ash.reader.ui.ext.dataStore data class Settings( + val newVersionNumber: Version = NewVersionNumberPreference.default, + val skipVersionNumber: Version = SkipVersionNumberPreference.default, + val newVersionPublishDate: String = NewVersionPublishDatePreference.default, + val newVersionLog: String = NewVersionLogPreference.default, + val newVersionSize: String = NewVersionSizePreference.default, + val newVersionDownloadUrl: String = NewVersionDownloadUrlPreference.default, + val themeIndex: Int = ThemeIndexPreference.default, val customPrimaryColor: String = CustomPrimaryColorPreference.default, val darkTheme: DarkThemePreference = DarkThemePreference.default, @@ -53,6 +61,13 @@ fun SettingsProvider( }.collectAsStateValue(initial = Settings()) CompositionLocalProvider( + LocalNewVersionNumber provides settings.newVersionNumber, + LocalSkipVersionNumber provides settings.skipVersionNumber, + LocalNewVersionPublishDate provides settings.newVersionPublishDate, + LocalNewVersionLog provides settings.newVersionLog, + LocalNewVersionSize provides settings.newVersionSize, + LocalNewVersionDownloadUrl provides settings.newVersionDownloadUrl, + LocalThemeIndex provides settings.themeIndex, LocalCustomPrimaryColor provides settings.customPrimaryColor, LocalDarkTheme provides settings.darkTheme, @@ -85,6 +100,13 @@ fun SettingsProvider( } } +val LocalNewVersionNumber = compositionLocalOf { NewVersionNumberPreference.default } +val LocalSkipVersionNumber = compositionLocalOf { SkipVersionNumberPreference.default } +val LocalNewVersionPublishDate = compositionLocalOf { NewVersionPublishDatePreference.default } +val LocalNewVersionLog = compositionLocalOf { NewVersionLogPreference.default } +val LocalNewVersionSize = compositionLocalOf { NewVersionSizePreference.default } +val LocalNewVersionDownloadUrl = compositionLocalOf { NewVersionDownloadUrlPreference.default } + val LocalThemeIndex = compositionLocalOf { ThemeIndexPreference.default } val LocalCustomPrimaryColor = diff --git a/app/src/main/java/me/ash/reader/data/preference/SkipVersionNumberPreference.kt b/app/src/main/java/me/ash/reader/data/preference/SkipVersionNumberPreference.kt new file mode 100644 index 0000000..d862c42 --- /dev/null +++ b/app/src/main/java/me/ash/reader/data/preference/SkipVersionNumberPreference.kt @@ -0,0 +1,25 @@ +package me.ash.reader.data.preference + +import android.content.Context +import androidx.datastore.preferences.core.Preferences +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import me.ash.reader.data.model.Version +import me.ash.reader.data.model.toVersion +import me.ash.reader.ui.ext.DataStoreKeys +import me.ash.reader.ui.ext.dataStore +import me.ash.reader.ui.ext.put + +object SkipVersionNumberPreference { + val default = Version() + + fun put(context: Context, scope: CoroutineScope, value: String) { + scope.launch(Dispatchers.IO) { + context.dataStore.put(DataStoreKeys.SkipVersionNumber, value) + } + } + + fun fromPreferences(preferences: Preferences) = + preferences[DataStoreKeys.SkipVersionNumber.key].toVersion() +} \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/data/repository/AppRepository.kt b/app/src/main/java/me/ash/reader/data/repository/AppRepository.kt index bfa0216..a48c01c 100644 --- a/app/src/main/java/me/ash/reader/data/repository/AppRepository.kt +++ b/app/src/main/java/me/ash/reader/data/repository/AppRepository.kt @@ -11,10 +11,15 @@ import me.ash.reader.R import me.ash.reader.data.model.toVersion import me.ash.reader.data.module.DispatcherIO import me.ash.reader.data.module.DispatcherMain +import me.ash.reader.data.preference.* +import me.ash.reader.data.preference.NewVersionSizePreference.formatSize import me.ash.reader.data.source.AppNetworkDataSource import me.ash.reader.data.source.Download import me.ash.reader.data.source.downloadToFileWithProgress -import me.ash.reader.ui.ext.* +import me.ash.reader.ui.ext.getCurrentVersion +import me.ash.reader.ui.ext.getLatestApk +import me.ash.reader.ui.ext.showToast +import me.ash.reader.ui.ext.skipVersionNumber import javax.inject.Inject class AppRepository @Inject constructor( @@ -46,7 +51,7 @@ class AppRepository @Inject constructor( } val latest = response.body()!! val latestVersion = latest.tag_name.toVersion() -// val latestVersion = "0.7.3".toVersion() +// val latestVersion = "1.0.0".toVersion() val skipVersion = context.skipVersionNumber.toVersion() val currentVersion = context.getCurrentVersion() val latestLog = latest.body ?: "" @@ -57,14 +62,11 @@ class AppRepository @Inject constructor( Log.i("RLog", "current version $currentVersion") if (latestVersion.whetherNeedUpdate(currentVersion, skipVersion)) { Log.i("RLog", "new version $latestVersion") - context.dataStore.put( - DataStoreKeys.NewVersionNumber, - latestVersion.toString() - ) - context.dataStore.put(DataStoreKeys.NewVersionLog, latestLog) - context.dataStore.put(DataStoreKeys.NewVersionPublishDate, latestPublishDate) - context.dataStore.put(DataStoreKeys.NewVersionSize, latestSize) - context.dataStore.put(DataStoreKeys.NewVersionDownloadUrl, latestDownloadUrl) + NewVersionNumberPreference.put(context, this, latestVersion.toString()) + NewVersionLogPreference.put(context, this, latestLog) + NewVersionPublishDatePreference.put(context, this, latestPublishDate) + NewVersionSizePreference.put(context, this, latestSize.formatSize()) + NewVersionDownloadUrlPreference.put(context, this, latestDownloadUrl) true } else { false diff --git a/app/src/main/java/me/ash/reader/ui/ext/DataStoreExt.kt b/app/src/main/java/me/ash/reader/ui/ext/DataStoreExt.kt index 53c3e0c..25b09a2 100644 --- a/app/src/main/java/me/ash/reader/ui/ext/DataStoreExt.kt +++ b/app/src/main/java/me/ash/reader/ui/ext/DataStoreExt.kt @@ -15,16 +15,6 @@ import java.io.IOException val Context.dataStore: DataStore by preferencesDataStore(name = "settings") -val Context.newVersionPublishDate: String - get() = this.dataStore.get(DataStoreKeys.NewVersionPublishDate) ?: "" -val Context.newVersionLog: String - get() = this.dataStore.get(DataStoreKeys.NewVersionLog) ?: "" -val Context.newVersionSize: Int - get() = this.dataStore.get(DataStoreKeys.NewVersionSize) ?: 0 -val Context.newVersionDownloadUrl: String - get() = this.dataStore.get(DataStoreKeys.NewVersionDownloadUrl) ?: "" -val Context.newVersionNumber: String - get() = this.dataStore.get(DataStoreKeys.NewVersionNumber) ?: "" val Context.skipVersionNumber: String get() = this.dataStore.get(DataStoreKeys.SkipVersionNumber) ?: "" val Context.isFirstLaunch: Boolean @@ -93,9 +83,9 @@ sealed class DataStoreKeys { get() = stringPreferencesKey("newVersionLog") } - object NewVersionSize : DataStoreKeys() { - override val key: Preferences.Key - get() = intPreferencesKey("newVersionSize") + object NewVersionSize : DataStoreKeys() { + override val key: Preferences.Key + get() = stringPreferencesKey("newVersionSize") } object NewVersionDownloadUrl : DataStoreKeys() { diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedsPage.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedsPage.kt index d13a200..5cb238e 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedsPage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedsPage.kt @@ -1,5 +1,6 @@ package me.ash.reader.ui.page.home.feeds +import android.annotation.SuppressLint import androidx.activity.compose.BackHandler import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts @@ -25,19 +26,20 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavHostController -import kotlinx.coroutines.flow.map import me.ash.reader.R import me.ash.reader.data.model.getName -import me.ash.reader.data.model.toVersion import me.ash.reader.data.preference.* import me.ash.reader.data.repository.SyncWorker.Companion.getIsSyncing +import me.ash.reader.ui.component.FilterBar import me.ash.reader.ui.component.base.Banner import me.ash.reader.ui.component.base.DisplayText import me.ash.reader.ui.component.base.FeedbackIconButton import me.ash.reader.ui.component.base.Subtitle -import me.ash.reader.ui.ext.* +import me.ash.reader.ui.ext.collectAsStateValue +import me.ash.reader.ui.ext.findActivity +import me.ash.reader.ui.ext.getCurrentVersion +import me.ash.reader.ui.ext.surfaceColorAtElevation import me.ash.reader.ui.page.common.RouteName -import me.ash.reader.ui.component.FilterBar import me.ash.reader.ui.page.home.FilterState import me.ash.reader.ui.page.home.HomeViewAction import me.ash.reader.ui.page.home.HomeViewModel @@ -48,6 +50,7 @@ import me.ash.reader.ui.page.home.feeds.subscribe.SubscribeViewAction import me.ash.reader.ui.page.home.feeds.subscribe.SubscribeViewModel import me.ash.reader.ui.theme.palette.onDark +@SuppressLint("FlowOperatorInvokedInComposition") @OptIn( ExperimentalMaterial3Api::class, com.google.accompanist.pager.ExperimentalPagerApi::class, androidx.compose.foundation.ExperimentalFoundationApi::class @@ -71,16 +74,8 @@ fun FeedsPage( val feedsViewState = feedsViewModel.viewState.collectAsStateValue() val filterState = homeViewModel.filterState.collectAsStateValue() - val skipVersion = context.dataStore.data - .map { it[DataStoreKeys.SkipVersionNumber.key] ?: "" } - .collectAsState(initial = "") - .value - .toVersion() - val latestVersion = context.dataStore.data - .map { it[DataStoreKeys.NewVersionNumber.key] ?: "" } - .collectAsState(initial = "") - .value - .toVersion() + val newVersion = LocalNewVersionNumber.current + val skipVersion = LocalSkipVersionNumber.current val currentVersion by remember { mutableStateOf(context.getCurrentVersion()) } val owner = LocalLifecycleOwner.current @@ -145,7 +140,7 @@ fun FeedsPage( imageVector = Icons.Outlined.Settings, contentDescription = stringResource(R.string.settings), tint = MaterialTheme.colorScheme.onSurface, - showBadge = latestVersion.whetherNeedUpdate(currentVersion, skipVersion), + showBadge = newVersion.whetherNeedUpdate(currentVersion, skipVersion), ) { navController.navigate(RouteName.SETTINGS) { launchSingleTop = true diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/SettingsPage.kt b/app/src/main/java/me/ash/reader/ui/page/settings/SettingsPage.kt index 79d61f3..de11561 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/SettingsPage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/SettingsPage.kt @@ -9,7 +9,10 @@ import androidx.compose.material.icons.outlined.* import androidx.compose.material.icons.rounded.ArrowBack import androidx.compose.material.icons.rounded.Close import androidx.compose.material3.* -import androidx.compose.runtime.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource @@ -17,14 +20,12 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.zIndex import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavHostController -import kotlinx.coroutines.flow.map import me.ash.reader.R -import me.ash.reader.data.model.toVersion +import me.ash.reader.data.preference.LocalNewVersionNumber +import me.ash.reader.data.preference.LocalSkipVersionNumber import me.ash.reader.ui.component.base.Banner import me.ash.reader.ui.component.base.DisplayText import me.ash.reader.ui.component.base.FeedbackIconButton -import me.ash.reader.ui.ext.DataStoreKeys -import me.ash.reader.ui.ext.dataStore import me.ash.reader.ui.ext.getCurrentVersion import me.ash.reader.ui.page.common.RouteName import me.ash.reader.ui.page.settings.tips.UpdateDialog @@ -40,16 +41,8 @@ fun SettingsPage( updateViewModel: UpdateViewModel = hiltViewModel(), ) { val context = LocalContext.current - val skipVersion = context.dataStore.data - .map { it[DataStoreKeys.SkipVersionNumber.key] ?: "" } - .collectAsState(initial = "") - .value - .toVersion() - val latestVersion = context.dataStore.data - .map { it[DataStoreKeys.NewVersionNumber.key] ?: "" } - .collectAsState(initial = "") - .value - .toVersion() + val newVersion = LocalNewVersionNumber.current + val skipVersion = LocalSkipVersionNumber.current val currentVersion by remember { mutableStateOf(context.getCurrentVersion()) } Scaffold( @@ -81,13 +74,13 @@ fun SettingsPage( } item { Box { - if (latestVersion.whetherNeedUpdate(currentVersion, skipVersion)) { + if (newVersion.whetherNeedUpdate(currentVersion, skipVersion)) { Banner( modifier = Modifier.zIndex(1f), title = stringResource(R.string.get_new_updates), desc = stringResource( R.string.get_new_updates_desc, - latestVersion.toString(), + newVersion.toString(), ), icon = Icons.Outlined.Lightbulb, action = { diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/tips/UpdateDialog.kt b/app/src/main/java/me/ash/reader/ui/page/settings/tips/UpdateDialog.kt index 2dcd32b..da40e9c 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/tips/UpdateDialog.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/tips/UpdateDialog.kt @@ -31,12 +31,12 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.launch import me.ash.reader.R +import me.ash.reader.data.preference.* import me.ash.reader.data.source.Download import me.ash.reader.ui.component.base.Dialog -import me.ash.reader.ui.ext.* +import me.ash.reader.ui.ext.collectAsStateValue +import me.ash.reader.ui.ext.installLatestApk @SuppressLint("FlowOperatorInvokedInComposition") @Composable @@ -47,24 +47,10 @@ fun UpdateDialog( val viewState = updateViewModel.viewState.collectAsStateValue() val downloadState = viewState.downloadFlow.collectAsState(initial = Download.NotYet).value val scope = rememberCoroutineScope { Dispatchers.IO } - val newVersionNumber = context.dataStore.data - .map { it[DataStoreKeys.NewVersionNumber.key] ?: "" } - .collectAsState(initial = "") - .value - val newVersionPublishDate = context.dataStore.data - .map { it[DataStoreKeys.NewVersionPublishDate.key] ?: "" } - .collectAsState(initial = "") - .value - val newVersionLog = context.dataStore.data - .map { it[DataStoreKeys.NewVersionLog.key] ?: "" } - .collectAsState(initial = "") - .value - val newVersionSize = " " + context.dataStore.data - .map { it[DataStoreKeys.NewVersionSize.key] ?: 0 } - .map { it / 1024f / 1024f } - .map { if (it > 0f) " ${String.format("%.2f", it)} MB" else "" } - .collectAsState(initial = 0) - .value + val newVersionNumber = LocalNewVersionNumber.current + val newVersionPublishDate = LocalNewVersionPublishDate.current + val newVersionLog = LocalNewVersionLog.current + val newVersionSize = LocalNewVersionSize.current val settings = rememberLauncherForActivityResult( ActivityResultContracts.StartActivityForResult() @@ -104,7 +90,7 @@ fun UpdateDialog( Text(text = stringResource(R.string.change_log)) Spacer(modifier = Modifier.height(16.dp)) Text( - text = "$newVersionPublishDate$newVersionSize", + text = "$newVersionPublishDate $newVersionSize", color = MaterialTheme.colorScheme.outline.copy(alpha = 0.7f), style = MaterialTheme.typography.bodyMedium, ) @@ -125,7 +111,7 @@ fun UpdateDialog( context.startActivity( Intent( Intent.ACTION_VIEW, - Uri.parse(context.getString(R.string.github_link)), + Uri.parse("${context.getString(R.string.github_link)}/releases/latest"), ) ) // Disable automatic updates in F-Droid @@ -162,10 +148,8 @@ fun UpdateDialog( if (downloadState !is Download.Progress) { TextButton( onClick = { - scope.launch { - context.dataStore.put(DataStoreKeys.SkipVersionNumber, newVersionNumber) - updateViewModel.dispatch(UpdateViewAction.Hide) - } + SkipVersionNumberPreference.put(context, scope, newVersionNumber.toString()) + updateViewModel.dispatch(UpdateViewAction.Hide) } ) { Text(text = stringResource(R.string.skip_this_version)) From dcbb41f3ab90868dbea0d02dc9443091b7c2e927 Mon Sep 17 00:00:00 2001 From: Ash Date: Wed, 18 May 2022 05:41:21 +0800 Subject: [PATCH 11/18] Extract Scaffold component --- .../preference/InitialFilterPreference.kt | 45 +++++ .../data/preference/InitialPagePreference.kt | 42 +++++ .../ash/reader/data/preference/Preference.kt | 3 + .../me/ash/reader/data/preference/Settings.kt | 10 ++ .../me/ash/reader/ui/component/FilterBar.kt | 4 +- .../ash/reader/ui/component/base/Scaffold.kt | 69 ++++++++ .../java/me/ash/reader/ui/ext/DataStoreExt.kt | 2 +- .../me/ash/reader/ui/page/common/HomeEntry.kt | 28 +-- .../reader/ui/page/home/feeds/FeedsPage.kt | 86 ++++----- .../ash/reader/ui/page/home/flow/FlowPage.kt | 124 ++++++------- .../ash/reader/ui/page/home/read/ReadPage.kt | 13 +- .../reader/ui/page/settings/SettingsPage.kt | 36 ++-- ...{ColorAndStyle.kt => ColorAndStylePage.kt} | 33 ++-- .../color/{DarkTheme.kt => DarkThemePage.kt} | 40 ++--- ...eedsPageStyle.kt => FeedsPageStylePage.kt} | 42 ++--- ...{FlowPageStyle.kt => FlowPageStylePage.kt} | 43 ++--- .../page/settings/interaction/Interaction.kt | 164 ------------------ .../settings/interaction/InteractionPage.kt | 106 +++++++++++ .../{Languages.kt => LanguagesPage.kt} | 41 ++--- ...ipsAndSupport.kt => TipsAndSupportPage.kt} | 54 +++--- .../ui/page/settings/tips/UpdateDialog.kt | 2 - .../ash/reader/ui/page/startup/StartupPage.kt | 24 +-- 22 files changed, 487 insertions(+), 524 deletions(-) create mode 100644 app/src/main/java/me/ash/reader/data/preference/InitialFilterPreference.kt create mode 100644 app/src/main/java/me/ash/reader/data/preference/InitialPagePreference.kt create mode 100644 app/src/main/java/me/ash/reader/ui/component/base/Scaffold.kt rename app/src/main/java/me/ash/reader/ui/page/settings/color/{ColorAndStyle.kt => ColorAndStylePage.kt} (93%) rename app/src/main/java/me/ash/reader/ui/page/settings/color/{DarkTheme.kt => DarkThemePage.kt} (71%) rename app/src/main/java/me/ash/reader/ui/page/settings/color/feeds/{FeedsPageStyle.kt => FeedsPageStylePage.kt} (92%) rename app/src/main/java/me/ash/reader/ui/page/settings/color/flow/{FlowPageStyle.kt => FlowPageStylePage.kt} (93%) delete mode 100644 app/src/main/java/me/ash/reader/ui/page/settings/interaction/Interaction.kt create mode 100644 app/src/main/java/me/ash/reader/ui/page/settings/interaction/InteractionPage.kt rename app/src/main/java/me/ash/reader/ui/page/settings/languages/{Languages.kt => LanguagesPage.kt} (73%) rename app/src/main/java/me/ash/reader/ui/page/settings/tips/{TipsAndSupport.kt => TipsAndSupportPage.kt} (90%) diff --git a/app/src/main/java/me/ash/reader/data/preference/InitialFilterPreference.kt b/app/src/main/java/me/ash/reader/data/preference/InitialFilterPreference.kt new file mode 100644 index 0000000..4f20d2e --- /dev/null +++ b/app/src/main/java/me/ash/reader/data/preference/InitialFilterPreference.kt @@ -0,0 +1,45 @@ +package me.ash.reader.data.preference + +import android.content.Context +import androidx.datastore.preferences.core.Preferences +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch +import me.ash.reader.R +import me.ash.reader.ui.ext.DataStoreKeys +import me.ash.reader.ui.ext.dataStore +import me.ash.reader.ui.ext.put + +sealed class InitialFilterPreference(val value: Int) : Preference() { + object Starred : InitialFilterPreference(0) + object Unread : InitialFilterPreference(1) + object All : InitialFilterPreference(2) + + override fun put(context: Context, scope: CoroutineScope) { + scope.launch { + context.dataStore.put( + DataStoreKeys.InitialFilter, + value + ) + } + } + + fun getDesc(context: Context): String = + when (this) { + Starred -> context.getString(R.string.starred) + Unread -> context.getString(R.string.unread) + All -> context.getString(R.string.all) + } + + companion object { + val default = All + val values = listOf(Starred, Unread, All) + + fun fromPreferences(preferences: Preferences) = + when (preferences[DataStoreKeys.InitialFilter.key]) { + 0 -> Starred + 1 -> Unread + 2 -> All + else -> default + } + } +} \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/data/preference/InitialPagePreference.kt b/app/src/main/java/me/ash/reader/data/preference/InitialPagePreference.kt new file mode 100644 index 0000000..fbf954b --- /dev/null +++ b/app/src/main/java/me/ash/reader/data/preference/InitialPagePreference.kt @@ -0,0 +1,42 @@ +package me.ash.reader.data.preference + +import android.content.Context +import androidx.datastore.preferences.core.Preferences +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch +import me.ash.reader.R +import me.ash.reader.ui.ext.DataStoreKeys +import me.ash.reader.ui.ext.dataStore +import me.ash.reader.ui.ext.put + +sealed class InitialPagePreference(val value: Int) : Preference() { + object FeedsPage : InitialPagePreference(0) + object FlowPage : InitialPagePreference(1) + + override fun put(context: Context, scope: CoroutineScope) { + scope.launch { + context.dataStore.put( + DataStoreKeys.InitialPage, + value + ) + } + } + + fun getDesc(context: Context): String = + when (this) { + FeedsPage -> context.getString(R.string.feeds_page) + FlowPage -> context.getString(R.string.flow_page) + } + + companion object { + val default = FeedsPage + val values = listOf(FeedsPage, FlowPage) + + fun fromPreferences(preferences: Preferences) = + when (preferences[DataStoreKeys.InitialPage.key]) { + 0 -> FeedsPage + 1 -> FlowPage + else -> default + } + } +} \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/data/preference/Preference.kt b/app/src/main/java/me/ash/reader/data/preference/Preference.kt index 2be1edf..e4fba2b 100644 --- a/app/src/main/java/me/ash/reader/data/preference/Preference.kt +++ b/app/src/main/java/me/ash/reader/data/preference/Preference.kt @@ -45,6 +45,9 @@ fun Preferences.toSettings(): Settings { ), flowArticleListTonalElevation = FlowArticleListTonalElevationPreference.fromPreferences(this), + initialPage = InitialPagePreference.fromPreferences(this), + initialFilter = InitialFilterPreference.fromPreferences(this), + languages = LanguagesPreference.fromPreferences(this), ) } \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/data/preference/Settings.kt b/app/src/main/java/me/ash/reader/data/preference/Settings.kt index f0bd8c1..b5edcf2 100644 --- a/app/src/main/java/me/ash/reader/data/preference/Settings.kt +++ b/app/src/main/java/me/ash/reader/data/preference/Settings.kt @@ -45,6 +45,9 @@ data class Settings( val flowArticleListDateStickyHeader: FlowArticleListDateStickyHeaderPreference = FlowArticleListDateStickyHeaderPreference.default, val flowArticleListTonalElevation: FlowArticleListTonalElevationPreference = FlowArticleListTonalElevationPreference.default, + val initialPage: InitialPagePreference = InitialPagePreference.default, + val initialFilter: InitialFilterPreference = InitialFilterPreference.default, + val languages: LanguagesPreference = LanguagesPreference.default, ) @@ -94,6 +97,9 @@ fun SettingsProvider( LocalFlowFilterBarPadding provides settings.flowFilterBarPadding, LocalFlowFilterBarTonalElevation provides settings.flowFilterBarTonalElevation, + LocalInitialPage provides settings.initialPage, + LocalInitialFilter provides settings.initialFilter, + LocalLanguages provides settings.languages, ) { content() @@ -156,5 +162,9 @@ val LocalFlowArticleListDateStickyHeader = val LocalFlowArticleListTonalElevation = compositionLocalOf { FlowArticleListTonalElevationPreference.default } +val LocalInitialPage = compositionLocalOf { InitialPagePreference.default } +val LocalInitialFilter = + compositionLocalOf { InitialFilterPreference.default } + val LocalLanguages = compositionLocalOf { LanguagesPreference.default } diff --git a/app/src/main/java/me/ash/reader/ui/component/FilterBar.kt b/app/src/main/java/me/ash/reader/ui/component/FilterBar.kt index ba30443..7027374 100644 --- a/app/src/main/java/me/ash/reader/ui/component/FilterBar.kt +++ b/app/src/main/java/me/ash/reader/ui/component/FilterBar.kt @@ -4,7 +4,6 @@ import android.os.Build import android.view.SoundEffectConstants import androidx.compose.foundation.background import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.width import androidx.compose.material3.* import androidx.compose.runtime.Composable @@ -34,8 +33,7 @@ fun FilterBar( NavigationBar( modifier = Modifier - .background(MaterialTheme.colorScheme.surfaceColorAtElevation(filterBarTonalElevation)) - .navigationBarsPadding(), + .background(MaterialTheme.colorScheme.surfaceColorAtElevation(filterBarTonalElevation)), tonalElevation = filterBarTonalElevation, ) { Spacer(modifier = Modifier.width(filterBarPadding)) diff --git a/app/src/main/java/me/ash/reader/ui/component/base/Scaffold.kt b/app/src/main/java/me/ash/reader/ui/component/base/Scaffold.kt new file mode 100644 index 0000000..d3a7856 --- /dev/null +++ b/app/src/main/java/me/ash/reader/ui/component/base/Scaffold.kt @@ -0,0 +1,69 @@ +package me.ash.reader.ui.component.base + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.navigationBarsPadding +import androidx.compose.foundation.layout.statusBarsPadding +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.SmallTopAppBar +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import me.ash.reader.ui.ext.surfaceColorAtElevation +import me.ash.reader.ui.theme.palette.onDark + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun Scaffold( + containerColor: Color = MaterialTheme.colorScheme.surface, + topBarTonalElevation: Dp = 0.dp, + containerTonalElevation: Dp = 0.dp, + navigationIcon: (@Composable () -> Unit)? = null, + actions: (@Composable RowScope.() -> Unit)? = null, + bottomBar: (@Composable () -> Unit)? = null, + floatingActionButton: (@Composable () -> Unit)? = null, + content: @Composable () -> Unit = {}, +) { + androidx.compose.material3.Scaffold( + modifier = Modifier + .background( + MaterialTheme.colorScheme.surfaceColorAtElevation( + topBarTonalElevation, + color = containerColor + ) + ) + .statusBarsPadding() + .run { + if (bottomBar != null || floatingActionButton != null) { + navigationBarsPadding() + } else { + this + } + }, + containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation( + containerTonalElevation, + color = containerColor + ) onDark MaterialTheme.colorScheme.surface, + topBar = { + if (navigationIcon != null || actions != null) { + SmallTopAppBar( + title = {}, + colors = TopAppBarDefaults.smallTopAppBarColors( + containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation( + topBarTonalElevation, color = containerColor + ), + ), + navigationIcon = { navigationIcon?.invoke() }, + actions = { actions?.invoke(this) }, + ) + } + }, + content = { content() }, + bottomBar = { bottomBar?.invoke() }, + floatingActionButton = { floatingActionButton?.invoke() }, + ) +} \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/ui/ext/DataStoreExt.kt b/app/src/main/java/me/ash/reader/ui/ext/DataStoreExt.kt index 25b09a2..52ca3b1 100644 --- a/app/src/main/java/me/ash/reader/ui/ext/DataStoreExt.kt +++ b/app/src/main/java/me/ash/reader/ui/ext/DataStoreExt.kt @@ -85,7 +85,7 @@ sealed class DataStoreKeys { object NewVersionSize : DataStoreKeys() { override val key: Preferences.Key - get() = stringPreferencesKey("newVersionSize") + get() = stringPreferencesKey("newVersionSizeString") } object NewVersionDownloadUrl : DataStoreKeys() { diff --git a/app/src/main/java/me/ash/reader/ui/page/common/HomeEntry.kt b/app/src/main/java/me/ash/reader/ui/page/common/HomeEntry.kt index 3855951..547409b 100644 --- a/app/src/main/java/me/ash/reader/ui/page/common/HomeEntry.kt +++ b/app/src/main/java/me/ash/reader/ui/page/common/HomeEntry.kt @@ -21,13 +21,13 @@ 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.settings.SettingsPage -import me.ash.reader.ui.page.settings.color.ColorAndStyle -import me.ash.reader.ui.page.settings.color.DarkTheme -import me.ash.reader.ui.page.settings.color.feeds.FeedsPageStyle -import me.ash.reader.ui.page.settings.color.flow.FlowPageStyle -import me.ash.reader.ui.page.settings.interaction.Interaction -import me.ash.reader.ui.page.settings.languages.Languages -import me.ash.reader.ui.page.settings.tips.TipsAndSupport +import me.ash.reader.ui.page.settings.color.ColorAndStylePage +import me.ash.reader.ui.page.settings.color.DarkThemePage +import me.ash.reader.ui.page.settings.color.feeds.FeedsPageStylePage +import me.ash.reader.ui.page.settings.color.flow.FlowPageStylePage +import me.ash.reader.ui.page.settings.interaction.InteractionPage +import me.ash.reader.ui.page.settings.languages.LanguagesPage +import me.ash.reader.ui.page.settings.tips.TipsAndSupportPage import me.ash.reader.ui.page.startup.StartupPage import me.ash.reader.ui.theme.AppTheme @@ -124,31 +124,31 @@ fun HomeEntry( // Color & Style animatedComposable(route = RouteName.COLOR_AND_STYLE) { - ColorAndStyle(navController) + ColorAndStylePage(navController) } animatedComposable(route = RouteName.DARK_THEME) { - DarkTheme(navController) + DarkThemePage(navController) } animatedComposable(route = RouteName.FEEDS_PAGE_STYLE) { - FeedsPageStyle(navController) + FeedsPageStylePage(navController) } animatedComposable(route = RouteName.FLOW_PAGE_STYLE) { - FlowPageStyle(navController) + FlowPageStylePage(navController) } // Interaction animatedComposable(route = RouteName.INTERACTION) { - Interaction(navController) + InteractionPage(navController) } // Languages animatedComposable(route = RouteName.LANGUAGES) { - Languages(navController = navController) + LanguagesPage(navController = navController) } // Tips & Support animatedComposable(route = RouteName.TIPS_AND_SUPPORT) { - TipsAndSupport(navController) + TipsAndSupportPage(navController) } } } diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedsPage.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedsPage.kt index 5cb238e..c33a89d 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedsPage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedsPage.kt @@ -1,11 +1,9 @@ package me.ash.reader.ui.page.home.feeds -import android.annotation.SuppressLint import androidx.activity.compose.BackHandler import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.animation.core.* -import androidx.compose.foundation.background import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn @@ -15,7 +13,8 @@ import androidx.compose.material.icons.outlined.KeyboardArrowRight import androidx.compose.material.icons.outlined.Settings import androidx.compose.material.icons.rounded.Add import androidx.compose.material.icons.rounded.Refresh -import androidx.compose.material3.* +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.draw.rotate @@ -38,7 +37,6 @@ import me.ash.reader.ui.component.base.Subtitle import me.ash.reader.ui.ext.collectAsStateValue import me.ash.reader.ui.ext.findActivity import me.ash.reader.ui.ext.getCurrentVersion -import me.ash.reader.ui.ext.surfaceColorAtElevation import me.ash.reader.ui.page.common.RouteName import me.ash.reader.ui.page.home.FilterState import me.ash.reader.ui.page.home.HomeViewAction @@ -48,11 +46,9 @@ 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 -import me.ash.reader.ui.theme.palette.onDark -@SuppressLint("FlowOperatorInvokedInComposition") @OptIn( - ExperimentalMaterial3Api::class, com.google.accompanist.pager.ExperimentalPagerApi::class, + com.google.accompanist.pager.ExperimentalPagerApi::class, androidx.compose.foundation.ExperimentalFoundationApi::class ) @Composable @@ -119,52 +115,38 @@ fun FeedsPage( context.findActivity()?.moveTaskToBack(false) } - Scaffold( - modifier = Modifier - .background(MaterialTheme.colorScheme.surfaceColorAtElevation(topBarTonalElevation.value.dp)) - .statusBarsPadding(), - containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation( - groupListTonalElevation.value.dp - ) onDark MaterialTheme.colorScheme.surface, - topBar = { - SmallTopAppBar( - colors = TopAppBarDefaults.smallTopAppBarColors( - containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation( - topBarTonalElevation.value.dp - ), - ), - title = {}, - navigationIcon = { - FeedbackIconButton( - modifier = Modifier.size(20.dp), - imageVector = Icons.Outlined.Settings, - contentDescription = stringResource(R.string.settings), - tint = MaterialTheme.colorScheme.onSurface, - showBadge = newVersion.whetherNeedUpdate(currentVersion, skipVersion), - ) { - navController.navigate(RouteName.SETTINGS) { - launchSingleTop = true - } - } - }, - actions = { - FeedbackIconButton( - modifier = Modifier.rotate(if (isSyncing) angle else 0f), - imageVector = Icons.Rounded.Refresh, - contentDescription = stringResource(R.string.refresh), - tint = MaterialTheme.colorScheme.onSurface, - ) { - if (!isSyncing) homeViewModel.dispatch(HomeViewAction.Sync) - } - FeedbackIconButton( - imageVector = Icons.Rounded.Add, - contentDescription = stringResource(R.string.subscribe), - tint = MaterialTheme.colorScheme.onSurface, - ) { - subscribeViewModel.dispatch(SubscribeViewAction.Show) - } + me.ash.reader.ui.component.base.Scaffold( + topBarTonalElevation = topBarTonalElevation.value.dp, + containerTonalElevation = groupListTonalElevation.value.dp, + navigationIcon = { + FeedbackIconButton( + modifier = Modifier.size(20.dp), + imageVector = Icons.Outlined.Settings, + contentDescription = stringResource(R.string.settings), + tint = MaterialTheme.colorScheme.onSurface, + showBadge = newVersion.whetherNeedUpdate(currentVersion, skipVersion), + ) { + navController.navigate(RouteName.SETTINGS) { + launchSingleTop = true } - ) + } + }, + actions = { + FeedbackIconButton( + modifier = Modifier.rotate(if (isSyncing) angle else 0f), + imageVector = Icons.Rounded.Refresh, + contentDescription = stringResource(R.string.refresh), + tint = MaterialTheme.colorScheme.onSurface, + ) { + if (!isSyncing) homeViewModel.dispatch(HomeViewAction.Sync) + } + FeedbackIconButton( + imageVector = Icons.Rounded.Add, + contentDescription = stringResource(R.string.subscribe), + tint = MaterialTheme.colorScheme.onSurface, + ) { + subscribeViewModel.dispatch(SubscribeViewAction.Show) + } }, content = { LazyColumn { diff --git a/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowPage.kt b/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowPage.kt index 0c02b7e..fc8c6b1 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowPage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowPage.kt @@ -2,7 +2,6 @@ package me.ash.reader.ui.page.home.flow import androidx.activity.compose.BackHandler import androidx.compose.animation.* -import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.rememberLazyListState @@ -10,7 +9,7 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.ArrowBack import androidx.compose.material.icons.rounded.DoneAll import androidx.compose.material.icons.rounded.Search -import androidx.compose.material3.* +import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester @@ -33,15 +32,12 @@ import me.ash.reader.ui.component.base.DisplayText import me.ash.reader.ui.component.base.FeedbackIconButton import me.ash.reader.ui.component.base.SwipeRefresh import me.ash.reader.ui.ext.collectAsStateValue -import me.ash.reader.ui.ext.surfaceColorAtElevation 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.theme.palette.onDark @OptIn( - ExperimentalMaterial3Api::class, com.google.accompanist.pager.ExperimentalPagerApi::class, androidx.compose.ui.ExperimentalComposeUiApi::class, ) @@ -105,75 +101,61 @@ fun FlowPage( onSearch = false } - Scaffold( - modifier = Modifier - .background(MaterialTheme.colorScheme.surfaceColorAtElevation(topBarTonalElevation.value.dp)) - .statusBarsPadding(), - containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation( - articleListTonalElevation.value.dp - ) onDark MaterialTheme.colorScheme.surface, - topBar = { - SmallTopAppBar( - title = {}, - colors = TopAppBarDefaults.smallTopAppBarColors( - containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation( - topBarTonalElevation.value.dp - ), - ), - navigationIcon = { - FeedbackIconButton( - imageVector = Icons.Rounded.ArrowBack, - contentDescription = stringResource(R.string.back), - tint = MaterialTheme.colorScheme.onSurface - ) { + me.ash.reader.ui.component.base.Scaffold( + topBarTonalElevation = topBarTonalElevation.value.dp, + containerTonalElevation = articleListTonalElevation.value.dp, + navigationIcon = { + FeedbackIconButton( + imageVector = Icons.Rounded.ArrowBack, + contentDescription = stringResource(R.string.back), + tint = MaterialTheme.colorScheme.onSurface + ) { + onSearch = false + if (navController.previousBackStackEntry == null) { + navController.navigate(RouteName.FEEDS) { + launchSingleTop = true + } + } else { + navController.popBackStack() + } + } + }, + actions = { + AnimatedVisibility( + visible = !filterState.filter.isStarred(), + enter = fadeIn() + expandVertically(), + exit = fadeOut() + shrinkVertically(), + ) { + FeedbackIconButton( + imageVector = Icons.Rounded.DoneAll, + contentDescription = stringResource(R.string.mark_all_as_read), + tint = if (markAsRead) { + MaterialTheme.colorScheme.primary + } else { + MaterialTheme.colorScheme.onSurface + }, + ) { + scope.launch { + viewState.listState.scrollToItem(0) + markAsRead = !markAsRead onSearch = false - if (navController.previousBackStackEntry == null) { - navController.navigate(RouteName.FEEDS) { - launchSingleTop = true - } - } else { - navController.popBackStack() - } - } - }, - actions = { - AnimatedVisibility( - visible = !filterState.filter.isStarred(), - enter = fadeIn() + expandVertically(), - exit = fadeOut() + shrinkVertically(), - ) { - FeedbackIconButton( - imageVector = Icons.Rounded.DoneAll, - contentDescription = stringResource(R.string.mark_all_as_read), - tint = if (markAsRead) { - MaterialTheme.colorScheme.primary - } else { - MaterialTheme.colorScheme.onSurface - }, - ) { - scope.launch { - viewState.listState.scrollToItem(0) - markAsRead = !markAsRead - onSearch = false - } - } - } - FeedbackIconButton( - imageVector = Icons.Rounded.Search, - contentDescription = stringResource(R.string.search), - tint = if (onSearch) { - MaterialTheme.colorScheme.primary - } else { - MaterialTheme.colorScheme.onSurface - }, - ) { - scope.launch { - viewState.listState.scrollToItem(0) - onSearch = !onSearch - } } } - ) + } + FeedbackIconButton( + imageVector = Icons.Rounded.Search, + contentDescription = stringResource(R.string.search), + tint = if (onSearch) { + MaterialTheme.colorScheme.primary + } else { + MaterialTheme.colorScheme.onSurface + }, + ) { + scope.launch { + viewState.listState.scrollToItem(0) + onSearch = !onSearch + } + } }, content = { SwipeRefresh( diff --git a/app/src/main/java/me/ash/reader/ui/page/home/read/ReadPage.kt b/app/src/main/java/me/ash/reader/ui/page/home/read/ReadPage.kt index 0acd12b..3f4958c 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/read/ReadPage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/read/ReadPage.kt @@ -11,7 +11,10 @@ import androidx.compose.foundation.text.selection.SelectionContainer import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.Share import androidx.compose.material.icons.rounded.Close -import androidx.compose.material3.* +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.SmallTopAppBar +import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -28,7 +31,6 @@ import me.ash.reader.ui.component.reader.reader import me.ash.reader.ui.ext.collectAsStateValue import me.ash.reader.ui.ext.drawVerticalScrollbar -@OptIn(ExperimentalMaterial3Api::class) @Composable fun ReadPage( navController: NavHostController, @@ -54,9 +56,7 @@ fun ReadPage( } } - Scaffold( - containerColor = MaterialTheme.colorScheme.surface, - topBar = {}, + me.ash.reader.ui.component.base.Scaffold( content = { Box(Modifier.fillMaxSize()) { Box( @@ -102,8 +102,7 @@ fun ReadPage( ) } } - }, - bottomBar = {} + } ) } diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/SettingsPage.kt b/app/src/main/java/me/ash/reader/ui/page/settings/SettingsPage.kt index de11561..df492d9 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/SettingsPage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/SettingsPage.kt @@ -1,14 +1,13 @@ package me.ash.reader.ui.page.settings -import android.annotation.SuppressLint -import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.* import androidx.compose.material.icons.rounded.ArrowBack import androidx.compose.material.icons.rounded.Close -import androidx.compose.material3.* +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -33,8 +32,6 @@ 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 -@SuppressLint("FlowOperatorInvokedInComposition") -@OptIn(ExperimentalMaterial3Api::class) @Composable fun SettingsPage( navController: NavHostController, @@ -45,27 +42,16 @@ fun SettingsPage( val skipVersion = LocalSkipVersionNumber.current val currentVersion by remember { mutableStateOf(context.getCurrentVersion()) } - Scaffold( - modifier = Modifier - .background(MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface) - .statusBarsPadding() - .navigationBarsPadding(), + me.ash.reader.ui.component.base.Scaffold( containerColor = MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface, - topBar = { - SmallTopAppBar( - colors = TopAppBarDefaults.smallTopAppBarColors(containerColor = MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface), - title = {}, - navigationIcon = { - FeedbackIconButton( - imageVector = Icons.Rounded.ArrowBack, - contentDescription = stringResource(R.string.back), - tint = MaterialTheme.colorScheme.onSurface - ) { - navController.popBackStack() - } - }, - actions = {} - ) + navigationIcon = { + FeedbackIconButton( + imageVector = Icons.Rounded.ArrowBack, + contentDescription = stringResource(R.string.back), + tint = MaterialTheme.colorScheme.onSurface + ) { + navController.popBackStack() + } }, content = { LazyColumn { diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/color/ColorAndStyle.kt b/app/src/main/java/me/ash/reader/ui/page/settings/color/ColorAndStylePage.kt similarity index 93% rename from app/src/main/java/me/ash/reader/ui/page/settings/color/ColorAndStyle.kt rename to app/src/main/java/me/ash/reader/ui/page/settings/color/ColorAndStylePage.kt index 8b68ede..09645b7 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/color/ColorAndStyle.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/color/ColorAndStylePage.kt @@ -37,7 +37,7 @@ import me.ash.reader.ui.theme.palette.dynamic.extractTonalPalettesFromUserWallpa @OptIn(ExperimentalMaterial3Api::class) @Composable -fun ColorAndStyle( +fun ColorAndStylePage( navController: NavHostController, ) { val context = LocalContext.current @@ -50,29 +50,16 @@ fun ColorAndStyle( val wallpaperTonalPalettes = extractTonalPalettesFromUserWallpaper() var radioButtonSelected by remember { mutableStateOf(if (themeIndex > 4) 0 else 1) } - Scaffold( - modifier = Modifier - .background(MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface) - .statusBarsPadding() - .navigationBarsPadding(), + me.ash.reader.ui.component.base.Scaffold( containerColor = MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface, - topBar = { - SmallTopAppBar( - colors = TopAppBarDefaults.smallTopAppBarColors( - containerColor = MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface - ), - title = {}, - navigationIcon = { - FeedbackIconButton( - imageVector = Icons.Rounded.ArrowBack, - contentDescription = stringResource(R.string.back), - tint = MaterialTheme.colorScheme.onSurface - ) { - navController.popBackStack() - } - }, - actions = {} - ) + navigationIcon = { + FeedbackIconButton( + imageVector = Icons.Rounded.ArrowBack, + contentDescription = stringResource(R.string.back), + tint = MaterialTheme.colorScheme.onSurface + ) { + navController.popBackStack() + } }, content = { LazyColumn { diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/color/DarkTheme.kt b/app/src/main/java/me/ash/reader/ui/page/settings/color/DarkThemePage.kt similarity index 71% rename from app/src/main/java/me/ash/reader/ui/page/settings/color/DarkTheme.kt rename to app/src/main/java/me/ash/reader/ui/page/settings/color/DarkThemePage.kt index e8d25c3..588ac2d 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/color/DarkTheme.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/color/DarkThemePage.kt @@ -1,13 +1,12 @@ package me.ash.reader.ui.page.settings.color -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.ArrowBack -import androidx.compose.material3.* +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.RadioButton import androidx.compose.runtime.Composable import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier @@ -29,7 +28,7 @@ import me.ash.reader.ui.theme.palette.onLight @OptIn(ExperimentalMaterial3Api::class) @Composable -fun DarkTheme( +fun DarkThemePage( navController: NavHostController, ) { val context = LocalContext.current @@ -37,29 +36,16 @@ fun DarkTheme( val amoledDarkTheme = LocalAmoledDarkTheme.current val scope = rememberCoroutineScope() - Scaffold( - modifier = Modifier - .background(MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface) - .statusBarsPadding() - .navigationBarsPadding(), + me.ash.reader.ui.component.base.Scaffold( containerColor = MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface, - topBar = { - SmallTopAppBar( - colors = TopAppBarDefaults.smallTopAppBarColors( - containerColor = MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface - ), - title = {}, - navigationIcon = { - FeedbackIconButton( - imageVector = Icons.Rounded.ArrowBack, - contentDescription = stringResource(R.string.back), - tint = MaterialTheme.colorScheme.onSurface - ) { - navController.popBackStack() - } - }, - actions = {} - ) + navigationIcon = { + FeedbackIconButton( + imageVector = Icons.Rounded.ArrowBack, + contentDescription = stringResource(R.string.back), + tint = MaterialTheme.colorScheme.onSurface + ) { + navController.popBackStack() + } }, content = { LazyColumn { diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/color/feeds/FeedsPageStyle.kt b/app/src/main/java/me/ash/reader/ui/page/settings/color/feeds/FeedsPageStylePage.kt similarity index 92% rename from app/src/main/java/me/ash/reader/ui/page/settings/color/feeds/FeedsPageStyle.kt rename to app/src/main/java/me/ash/reader/ui/page/settings/color/feeds/FeedsPageStylePage.kt index 70d19c8..361b283 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/color/feeds/FeedsPageStyle.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/color/feeds/FeedsPageStylePage.kt @@ -10,7 +10,9 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.Add import androidx.compose.material.icons.rounded.ArrowBack import androidx.compose.material.icons.rounded.Refresh -import androidx.compose.material3.* +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.SmallTopAppBar +import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -22,20 +24,19 @@ import androidx.compose.ui.unit.dp import androidx.navigation.NavHostController import me.ash.reader.R 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.preference.* +import me.ash.reader.ui.component.FilterBar import me.ash.reader.ui.component.base.* import me.ash.reader.ui.ext.surfaceColorAtElevation -import me.ash.reader.ui.component.FilterBar import me.ash.reader.ui.page.home.feeds.GroupItem import me.ash.reader.ui.page.settings.SettingItem import me.ash.reader.ui.theme.palette.onDark import me.ash.reader.ui.theme.palette.onLight -@OptIn(ExperimentalMaterial3Api::class) @Composable -fun FeedsPageStyle( +fun FeedsPageStylePage( navController: NavHostController, ) { val context = LocalContext.current @@ -57,29 +58,16 @@ fun FeedsPageStyle( var filterBarPaddingValue: Int? by remember { mutableStateOf(filterBarPadding) } - Scaffold( - modifier = Modifier - .background(MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface) - .statusBarsPadding() - .navigationBarsPadding(), + me.ash.reader.ui.component.base.Scaffold( containerColor = MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface, - topBar = { - SmallTopAppBar( - colors = TopAppBarDefaults.smallTopAppBarColors( - containerColor = MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface - ), - title = {}, - navigationIcon = { - FeedbackIconButton( - imageVector = Icons.Rounded.ArrowBack, - contentDescription = stringResource(R.string.back), - tint = MaterialTheme.colorScheme.onSurface - ) { - navController.popBackStack() - } - }, - actions = {} - ) + navigationIcon = { + FeedbackIconButton( + imageVector = Icons.Rounded.ArrowBack, + contentDescription = stringResource(R.string.back), + tint = MaterialTheme.colorScheme.onSurface + ) { + navController.popBackStack() + } }, content = { LazyColumn { diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/color/flow/FlowPageStyle.kt b/app/src/main/java/me/ash/reader/ui/page/settings/color/flow/FlowPageStylePage.kt similarity index 93% rename from app/src/main/java/me/ash/reader/ui/page/settings/color/flow/FlowPageStyle.kt rename to app/src/main/java/me/ash/reader/ui/page/settings/color/flow/FlowPageStylePage.kt index ed8fb63..d437012 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/color/flow/FlowPageStyle.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/color/flow/FlowPageStylePage.kt @@ -1,6 +1,5 @@ package me.ash.reader.ui.page.settings.color.flow -import android.annotation.SuppressLint import androidx.compose.animation.animateContentSize import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -11,7 +10,10 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.ArrowBack import androidx.compose.material.icons.rounded.DoneAll import androidx.compose.material.icons.rounded.Search -import androidx.compose.material3.* +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.SmallTopAppBar +import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -27,19 +29,17 @@ import me.ash.reader.data.entity.ArticleWithFeed import me.ash.reader.data.entity.Feed import me.ash.reader.data.model.Filter import me.ash.reader.data.preference.* +import me.ash.reader.ui.component.FilterBar import me.ash.reader.ui.component.base.* import me.ash.reader.ui.ext.surfaceColorAtElevation -import me.ash.reader.ui.component.FilterBar import me.ash.reader.ui.page.home.flow.ArticleItem import me.ash.reader.ui.page.settings.SettingItem import me.ash.reader.ui.theme.palette.onDark import me.ash.reader.ui.theme.palette.onLight import java.util.* -@SuppressLint("FlowOperatorInvokedInComposition") -@OptIn(ExperimentalMaterial3Api::class) @Composable -fun FlowPageStyle( +fun FlowPageStylePage( navController: NavHostController, ) { val context = LocalContext.current @@ -66,29 +66,16 @@ fun FlowPageStyle( var filterBarPaddingValue: Int? by remember { mutableStateOf(filterBarPadding) } - Scaffold( - modifier = Modifier - .background(MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface) - .statusBarsPadding() - .navigationBarsPadding(), + me.ash.reader.ui.component.base.Scaffold( containerColor = MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface, - topBar = { - SmallTopAppBar( - colors = TopAppBarDefaults.smallTopAppBarColors( - containerColor = MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface - ), - title = {}, - navigationIcon = { - FeedbackIconButton( - imageVector = Icons.Rounded.ArrowBack, - contentDescription = stringResource(R.string.back), - tint = MaterialTheme.colorScheme.onSurface - ) { - navController.popBackStack() - } - }, - actions = {} - ) + navigationIcon = { + FeedbackIconButton( + imageVector = Icons.Rounded.ArrowBack, + contentDescription = stringResource(R.string.back), + tint = MaterialTheme.colorScheme.onSurface + ) { + navController.popBackStack() + } }, content = { LazyColumn { diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/interaction/Interaction.kt b/app/src/main/java/me/ash/reader/ui/page/settings/interaction/Interaction.kt deleted file mode 100644 index 144e432..0000000 --- a/app/src/main/java/me/ash/reader/ui/page/settings/interaction/Interaction.kt +++ /dev/null @@ -1,164 +0,0 @@ -package me.ash.reader.ui.page.settings.interaction - -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.rounded.ArrowBack -import androidx.compose.material3.* -import androidx.compose.runtime.* -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.unit.dp -import androidx.navigation.NavHostController -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.launch -import me.ash.reader.R -import me.ash.reader.ui.component.* -import me.ash.reader.ui.component.base.* -import me.ash.reader.ui.ext.DataStoreKeys -import me.ash.reader.ui.ext.dataStore -import me.ash.reader.ui.ext.put -import me.ash.reader.ui.page.settings.SettingItem -import me.ash.reader.ui.theme.palette.onLight - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun Interaction( - navController: NavHostController, -) { - val context = LocalContext.current - val scope = rememberCoroutineScope() - var initialPageDialogVisible by remember { mutableStateOf(false) } - var initialFilterDialogVisible by remember { mutableStateOf(false) } - - val initialPage = context.dataStore.data - .map { it[DataStoreKeys.InitialPage.key] ?: 0 } - .collectAsState(initial = 0).value - - val initialFilter = context.dataStore.data - .map { it[DataStoreKeys.InitialFilter.key] ?: 2 } - .collectAsState(initial = 2).value - - Scaffold( - modifier = Modifier - .background(MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface) - .statusBarsPadding() - .navigationBarsPadding(), - containerColor = MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface, - topBar = { - SmallTopAppBar( - colors = TopAppBarDefaults.smallTopAppBarColors( - containerColor = MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface - ), - title = {}, - navigationIcon = { - FeedbackIconButton( - imageVector = Icons.Rounded.ArrowBack, - contentDescription = stringResource(R.string.back), - tint = MaterialTheme.colorScheme.onSurface - ) { - navController.popBackStack() - } - }, - actions = {} - ) - }, - content = { - LazyColumn { - item { - DisplayText(text = stringResource(R.string.interaction), desc = "") - Spacer(modifier = Modifier.height(16.dp)) - } - item { - Subtitle( - modifier = Modifier.padding(horizontal = 24.dp), - text = stringResource(R.string.on_start), - ) - SettingItem( - title = stringResource(R.string.initial_page), - desc = when (initialPage) { - 0 -> stringResource(R.string.feeds_page) - 1 -> stringResource(R.string.flow_page) - else -> "" - }, - onClick = { - initialPageDialogVisible = true - }, - ) {} - SettingItem( - title = stringResource(R.string.initial_filter), - desc = when (initialFilter) { - 0 -> stringResource(R.string.starred) - 1 -> stringResource(R.string.unread) - 2 -> stringResource(R.string.all) - else -> "" - }, - onClick = { - initialFilterDialogVisible = true - }, - ) {} - } - } - } - ) - - RadioDialog( - visible = initialPageDialogVisible, - title = stringResource(R.string.initial_page), - options = listOf( - RadioDialogOption( - text = stringResource(R.string.feeds_page), - selected = initialPage == 0, - ) { - scope.launch { - context.dataStore.put(DataStoreKeys.InitialPage, 0) - } - }, - RadioDialogOption( - text = stringResource(R.string.flow_page), - selected = initialPage == 1, - ) { - scope.launch { - context.dataStore.put(DataStoreKeys.InitialPage, 1) - } - }, - ), - ) { - initialPageDialogVisible = false - } - - RadioDialog( - visible = initialFilterDialogVisible, - title = stringResource(R.string.initial_filter), - options = listOf( - RadioDialogOption( - text = stringResource(R.string.starred), - selected = initialFilter == 0, - ) { - scope.launch { - context.dataStore.put(DataStoreKeys.InitialFilter, 0) - } - }, - RadioDialogOption( - text = stringResource(R.string.unread), - selected = initialFilter == 1, - ) { - scope.launch { - context.dataStore.put(DataStoreKeys.InitialFilter, 1) - } - }, - RadioDialogOption( - text = stringResource(R.string.all), - selected = initialFilter == 2, - ) { - scope.launch { - context.dataStore.put(DataStoreKeys.InitialFilter, 2) - } - }, - ), - ) { - initialFilterDialogVisible = false - } -} diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/interaction/InteractionPage.kt b/app/src/main/java/me/ash/reader/ui/page/settings/interaction/InteractionPage.kt new file mode 100644 index 0000000..a532ba3 --- /dev/null +++ b/app/src/main/java/me/ash/reader/ui/page/settings/interaction/InteractionPage.kt @@ -0,0 +1,106 @@ +package me.ash.reader.ui.page.settings.interaction + +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.rounded.ArrowBack +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.navigation.NavHostController +import me.ash.reader.R +import me.ash.reader.data.preference.InitialFilterPreference +import me.ash.reader.data.preference.InitialPagePreference +import me.ash.reader.data.preference.LocalInitialFilter +import me.ash.reader.data.preference.LocalInitialPage +import me.ash.reader.ui.component.base.* +import me.ash.reader.ui.page.settings.SettingItem +import me.ash.reader.ui.theme.palette.onLight + +@Composable +fun InteractionPage( + navController: NavHostController, +) { + val context = LocalContext.current + val initialPage = LocalInitialPage.current + val initialFilter = LocalInitialFilter.current + val scope = rememberCoroutineScope() + var initialPageDialogVisible by remember { mutableStateOf(false) } + var initialFilterDialogVisible by remember { mutableStateOf(false) } + + me.ash.reader.ui.component.base.Scaffold( + containerColor = MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface, + navigationIcon = { + FeedbackIconButton( + imageVector = Icons.Rounded.ArrowBack, + contentDescription = stringResource(R.string.back), + tint = MaterialTheme.colorScheme.onSurface + ) { + navController.popBackStack() + } + }, + content = { + LazyColumn { + item { + DisplayText(text = stringResource(R.string.interaction), desc = "") + Spacer(modifier = Modifier.height(16.dp)) + } + item { + Subtitle( + modifier = Modifier.padding(horizontal = 24.dp), + text = stringResource(R.string.on_start), + ) + SettingItem( + title = stringResource(R.string.initial_page), + desc = initialPage.getDesc(context), + onClick = { + initialPageDialogVisible = true + }, + ) {} + SettingItem( + title = stringResource(R.string.initial_filter), + desc = initialFilter.getDesc(context), + onClick = { + initialFilterDialogVisible = true + }, + ) {} + } + } + } + ) + + RadioDialog( + visible = initialPageDialogVisible, + title = stringResource(R.string.initial_page), + options = InitialPagePreference.values.map { + RadioDialogOption( + text = it.getDesc(context), + selected = it == initialPage, + ) { + it.put(context, scope) + } + }, + ) { + initialPageDialogVisible = false + } + + RadioDialog( + visible = initialFilterDialogVisible, + title = stringResource(R.string.initial_filter), + options = InitialFilterPreference.values.map { + RadioDialogOption( + text = it.getDesc(context), + selected = it == initialFilter, + ) { + it.put(context, scope) + } + }, + ) { + initialFilterDialogVisible = false + } +} diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/languages/Languages.kt b/app/src/main/java/me/ash/reader/ui/page/settings/languages/LanguagesPage.kt similarity index 73% rename from app/src/main/java/me/ash/reader/ui/page/settings/languages/Languages.kt rename to app/src/main/java/me/ash/reader/ui/page/settings/languages/LanguagesPage.kt index 927f1d2..8b2fc9c 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/languages/Languages.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/languages/LanguagesPage.kt @@ -2,17 +2,17 @@ package me.ash.reader.ui.page.settings.languages import android.content.Intent import android.net.Uri -import androidx.compose.foundation.background import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.navigationBarsPadding -import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.KeyboardArrowRight import androidx.compose.material.icons.outlined.Lightbulb import androidx.compose.material.icons.rounded.ArrowBack -import androidx.compose.material3.* +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.RadioButton import androidx.compose.runtime.Composable import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier @@ -31,36 +31,23 @@ import me.ash.reader.ui.theme.palette.onLight @OptIn(ExperimentalMaterial3Api::class) @Composable -fun Languages( +fun LanguagesPage( navController: NavHostController, ) { val context = LocalContext.current val languages = LocalLanguages.current val scope = rememberCoroutineScope() - Scaffold( - modifier = Modifier - .background(MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface) - .statusBarsPadding() - .navigationBarsPadding(), + me.ash.reader.ui.component.base.Scaffold( containerColor = MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface, - topBar = { - SmallTopAppBar( - colors = TopAppBarDefaults.smallTopAppBarColors( - containerColor = MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface - ), - title = {}, - navigationIcon = { - FeedbackIconButton( - imageVector = Icons.Rounded.ArrowBack, - contentDescription = stringResource(R.string.back), - tint = MaterialTheme.colorScheme.onSurface - ) { - navController.popBackStack() - } - }, - actions = {} - ) + navigationIcon = { + FeedbackIconButton( + imageVector = Icons.Rounded.ArrowBack, + contentDescription = stringResource(R.string.back), + tint = MaterialTheme.colorScheme.onSurface + ) { + navController.popBackStack() + } }, content = { LazyColumn { diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/tips/TipsAndSupport.kt b/app/src/main/java/me/ash/reader/ui/page/settings/tips/TipsAndSupportPage.kt similarity index 90% rename from app/src/main/java/me/ash/reader/ui/page/settings/tips/TipsAndSupport.kt rename to app/src/main/java/me/ash/reader/ui/page/settings/tips/TipsAndSupportPage.kt index 2da3e1d..da76ae8 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/tips/TipsAndSupport.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/tips/TipsAndSupportPage.kt @@ -42,15 +42,13 @@ import me.ash.reader.ui.ext.* import me.ash.reader.ui.theme.palette.alwaysLight import me.ash.reader.ui.theme.palette.onLight -@OptIn(ExperimentalMaterial3Api::class) @Composable -fun TipsAndSupport( +fun TipsAndSupportPage( navController: NavHostController, updateViewModel: UpdateViewModel = hiltViewModel(), ) { val context = LocalContext.current val view = LocalView.current - val scope = rememberCoroutineScope() var currentVersion by remember { mutableStateOf("") } var clickTime by remember { mutableStateOf(System.currentTimeMillis() - 2000) } var pressAMP by remember { mutableStateOf(16f) } @@ -63,38 +61,26 @@ fun TipsAndSupport( currentVersion = context.getCurrentVersion().toString() } - Scaffold( - modifier = Modifier - .background(MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface) - .statusBarsPadding() - .navigationBarsPadding(), + me.ash.reader.ui.component.base.Scaffold( containerColor = MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface, - topBar = { - SmallTopAppBar( - colors = TopAppBarDefaults.smallTopAppBarColors( - containerColor = MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface - ), - title = {}, - navigationIcon = { - FeedbackIconButton( - imageVector = Icons.Rounded.ArrowBack, - contentDescription = stringResource(R.string.back), - tint = MaterialTheme.colorScheme.onSurface - ) { - navController.popBackStack() - } - }, - actions = { - FeedbackIconButton( - modifier = Modifier.size(20.dp), - imageVector = Icons.Rounded.Balance, - contentDescription = stringResource(R.string.open_source_licenses), - tint = MaterialTheme.colorScheme.onSurface - ) { - context.showToast(context.getString(R.string.coming_soon)) - } - } - ) + navigationIcon = { + FeedbackIconButton( + imageVector = Icons.Rounded.ArrowBack, + contentDescription = stringResource(R.string.back), + tint = MaterialTheme.colorScheme.onSurface + ) { + navController.popBackStack() + } + }, + actions = { + FeedbackIconButton( + modifier = Modifier.size(20.dp), + imageVector = Icons.Rounded.Balance, + contentDescription = stringResource(R.string.open_source_licenses), + tint = MaterialTheme.colorScheme.onSurface + ) { + context.showToast(context.getString(R.string.coming_soon)) + } }, content = { LazyColumn( diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/tips/UpdateDialog.kt b/app/src/main/java/me/ash/reader/ui/page/settings/tips/UpdateDialog.kt index da40e9c..d93b2e8 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/tips/UpdateDialog.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/tips/UpdateDialog.kt @@ -1,7 +1,6 @@ package me.ash.reader.ui.page.settings.tips import android.Manifest -import android.annotation.SuppressLint import android.content.Intent import android.net.Uri import android.provider.Settings @@ -38,7 +37,6 @@ import me.ash.reader.ui.component.base.Dialog import me.ash.reader.ui.ext.collectAsStateValue import me.ash.reader.ui.ext.installLatestApk -@SuppressLint("FlowOperatorInvokedInComposition") @Composable fun UpdateDialog( updateViewModel: UpdateViewModel = hiltViewModel(), diff --git a/app/src/main/java/me/ash/reader/ui/page/startup/StartupPage.kt b/app/src/main/java/me/ash/reader/ui/page/startup/StartupPage.kt index 88d96b0..cc955d9 100644 --- a/app/src/main/java/me/ash/reader/ui/page/startup/StartupPage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/startup/StartupPage.kt @@ -2,8 +2,9 @@ package me.ash.reader.ui.page.startup import android.content.Intent import android.net.Uri -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.CheckCircleOutline @@ -28,7 +29,6 @@ import me.ash.reader.ui.page.common.RouteName import me.ash.reader.ui.svg.SVGString import me.ash.reader.ui.svg.WELCOME -@OptIn(ExperimentalMaterial3Api::class) @Composable fun StartupPage( navController: NavHostController, @@ -36,12 +36,7 @@ fun StartupPage( val context = LocalContext.current val scope = rememberCoroutineScope() - Scaffold( - modifier = Modifier - .background(MaterialTheme.colorScheme.surface) - .statusBarsPadding() - .navigationBarsPadding(), - topBar = {}, + me.ash.reader.ui.component.base.Scaffold( content = { LazyColumn { item { @@ -87,16 +82,7 @@ fun StartupPage( } } }, - bottomBar = { -// Row( -// modifier = Modifier -// .fillMaxWidth() -// .padding(24.dp), -// horizontalArrangement = Arrangement.End, -// verticalAlignment = Alignment.CenterVertically, -// ) { -// } - }, + bottomBar = null, floatingActionButton = { ExtendedFloatingActionButton( onClick = { From efdff0e49c957e67b4766089097450e514265098 Mon Sep 17 00:00:00 2001 From: Ash Date: Sat, 21 May 2022 05:36:32 +0800 Subject: [PATCH 12/18] Rename UiState --- .../me/ash/reader/ui/page/common/HomeEntry.kt | 25 +- .../ash/reader/ui/page/home/HomeViewModel.kt | 72 ++--- .../ash/reader/ui/page/home/feeds/FeedItem.kt | 3 +- .../reader/ui/page/home/feeds/FeedsPage.kt | 46 ++-- .../ui/page/home/feeds/FeedsViewModel.kt | 65 ++--- .../reader/ui/page/home/feeds/GroupItem.kt | 3 +- .../home/feeds/drawer/feed/ClearFeedDialog.kt | 20 +- .../feeds/drawer/feed/DeleteFeedDialog.kt | 20 +- .../feeds/drawer/feed/FeedOptionDrawer.kt | 73 +++--- .../feeds/drawer/feed/FeedOptionViewModel.kt | 225 ++++++---------- .../group/AllAllowNotificationDialog.kt | 26 +- .../drawer/group/AllMoveToGroupDialog.kt | 28 +- .../drawer/group/AllParseFullContentDialog.kt | 26 +- .../feeds/drawer/group/ClearGroupDialog.kt | 20 +- .../feeds/drawer/group/DeleteGroupDialog.kt | 20 +- .../feeds/drawer/group/GroupOptionDrawer.kt | 59 ++--- .../drawer/group/GroupOptionViewModel.kt | 247 +++++++----------- .../home/feeds/subscribe/SubscribeDialog.kt | 82 +++--- .../feeds/subscribe/SubscribeViewModel.kt | 183 +++++-------- .../ash/reader/ui/page/home/flow/FlowPage.kt | 73 +++--- .../reader/ui/page/home/flow/FlowViewModel.kt | 54 +--- .../ui/page/home/{read => reading}/Header.kt | 2 +- .../ui/page/home/{read => reading}/ReadBar.kt | 2 +- .../ReadPage.kt => reading/ReadingPage.kt} | 46 ++-- .../ReadingViewModel.kt} | 105 +++----- .../reader/ui/page/settings/SettingsPage.kt | 3 +- .../page/settings/tips/TipsAndSupportPage.kt | 28 +- .../ui/page/settings/tips/UpdateDialog.kt | 10 +- .../ui/page/settings/tips/UpdateViewModel.kt | 60 ++--- 29 files changed, 648 insertions(+), 978 deletions(-) rename app/src/main/java/me/ash/reader/ui/page/home/{read => reading}/Header.kt (97%) rename app/src/main/java/me/ash/reader/ui/page/home/{read => reading}/ReadBar.kt (99%) rename app/src/main/java/me/ash/reader/ui/page/home/{read/ReadPage.kt => reading/ReadingPage.kt} (84%) rename app/src/main/java/me/ash/reader/ui/page/home/{read/ReadViewModel.kt => reading/ReadingViewModel.kt} (51%) diff --git a/app/src/main/java/me/ash/reader/ui/page/common/HomeEntry.kt b/app/src/main/java/me/ash/reader/ui/page/common/HomeEntry.kt index 547409b..92ca3b6 100644 --- a/app/src/main/java/me/ash/reader/ui/page/common/HomeEntry.kt +++ b/app/src/main/java/me/ash/reader/ui/page/common/HomeEntry.kt @@ -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 diff --git a/app/src/main/java/me/ash/reader/ui/page/home/HomeViewModel.kt b/app/src/main/java/me/ash/reader/ui/page/home/HomeViewModel.kt index 3f7f258..a250c56 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/HomeViewModel.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/HomeViewModel.kt @@ -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.asStateFlow() - private val _viewState = MutableStateFlow(HomeViewState()) - val viewState: StateFlow = _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> = 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() -} \ No newline at end of file +) \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedItem.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedItem.kt index f80a65d..63ed5e2 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedItem.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedItem.kt @@ -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), diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedsPage.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedsPage.kt index c33a89d..4feb513 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedsPage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedsPage.kt @@ -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 diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedsViewModel.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedsViewModel.kt index 6030473..6fd8243 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedsViewModel.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedsViewModel.kt @@ -31,21 +31,12 @@ class FeedsViewModel @Inject constructor( @DispatcherIO private val dispatcherIO: CoroutineDispatcher, ) : ViewModel() { - private val _viewState = MutableStateFlow(FeedsViewState()) - val viewState: StateFlow = _viewState.asStateFlow() + private val _feedsUiState = MutableStateFlow(FeedsUiState()) + val feedsUiState: StateFlow = _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 = 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() -} \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/GroupItem.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/GroupItem.kt index 3afdd20..65e1ab6 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/GroupItem.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/GroupItem.kt @@ -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) diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/feed/ClearFeedDialog.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/feed/ClearFeedDialog.kt index 0ead3a7..412c76a 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/feed/ClearFeedDialog.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/feed/ClearFeedDialog.kt @@ -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( diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/feed/DeleteFeedDialog.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/feed/DeleteFeedDialog.kt index c977abe..564d529 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/feed/DeleteFeedDialog.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/feed/DeleteFeedDialog.kt @@ -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( diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/feed/FeedOptionDrawer.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/feed/FeedOptionDrawer.kt index d3d03b0..5672846 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/feed/FeedOptionDrawer.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/feed/FeedOptionDrawer.kt @@ -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) } ) } \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/feed/FeedOptionViewModel.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/feed/FeedOptionViewModel.kt index 00db51a..6ed3941 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/feed/FeedOptionViewModel.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/feed/FeedOptionViewModel.kt @@ -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 = _viewState.asStateFlow() + private val _feedOptionUiState = MutableStateFlow(FeedOptionUiState()) + val feedOptionUiState: StateFlow = _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() -} diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/AllAllowNotificationDialog.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/AllAllowNotificationDialog.kt index 6ca6e7f..d9c5094 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/AllAllowNotificationDialog.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/AllAllowNotificationDialog.kt @@ -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( diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/AllMoveToGroupDialog.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/AllMoveToGroupDialog.kt index 7e39a85..79c2f53 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/AllMoveToGroupDialog.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/AllMoveToGroupDialog.kt @@ -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( diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/AllParseFullContentDialog.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/AllParseFullContentDialog.kt index bc8a92f..00e5abc 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/AllParseFullContentDialog.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/AllParseFullContentDialog.kt @@ -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( diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/ClearGroupDialog.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/ClearGroupDialog.kt index ce02471..44b81b9 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/ClearGroupDialog.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/ClearGroupDialog.kt @@ -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( diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/DeleteGroupDialog.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/DeleteGroupDialog.kt index ffa3a48..f9593dc 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/DeleteGroupDialog.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/DeleteGroupDialog.kt @@ -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( diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/GroupOptionDrawer.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/GroupOptionDrawer.kt index ca30d36..d153ee7 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/GroupOptionDrawer.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/GroupOptionDrawer.kt @@ -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)) diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/GroupOptionViewModel.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/GroupOptionViewModel.kt index e3226c8..1c2ee46 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/GroupOptionViewModel.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/GroupOptionViewModel.kt @@ -29,13 +29,13 @@ class GroupOptionViewModel @Inject constructor( @DispatcherIO private val dispatcherIO: CoroutineDispatcher, ) : ViewModel() { - private val _viewState = MutableStateFlow(GroupOptionViewState()) - val viewState: StateFlow = _viewState.asStateFlow() + private val _groupOptionUiState = MutableStateFlow(GroupOptionUiState()) + val groupOptionUiState: StateFlow = _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() -} 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 02a6e6e..675d160 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 @@ -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() } ) } \ No newline at end of file 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 243ed9c..df4e143 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 @@ -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 = _viewState.asStateFlow() + private val _subscribeUiState = MutableStateFlow(SubscribeUiState()) + val subscribeUiState: StateFlow = _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> = 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() -} diff --git a/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowPage.kt b/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowPage.kt index fc8c6b1..0aebc58 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowPage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowPage.kt @@ -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() } } ) diff --git a/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowViewModel.kt b/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowViewModel.kt index 257609b..4144f9d 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowViewModel.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowViewModel.kt @@ -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 = _viewState.asStateFlow() + private val _flowUiState = MutableStateFlow(FlowUiState()) + val flowUiState: StateFlow = _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, diff --git a/app/src/main/java/me/ash/reader/ui/page/home/read/Header.kt b/app/src/main/java/me/ash/reader/ui/page/home/reading/Header.kt similarity index 97% rename from app/src/main/java/me/ash/reader/ui/page/home/read/Header.kt rename to app/src/main/java/me/ash/reader/ui/page/home/reading/Header.kt index 44cccb2..40f2552 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/read/Header.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/reading/Header.kt @@ -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 diff --git a/app/src/main/java/me/ash/reader/ui/page/home/read/ReadBar.kt b/app/src/main/java/me/ash/reader/ui/page/home/reading/ReadBar.kt similarity index 99% rename from app/src/main/java/me/ash/reader/ui/page/home/read/ReadBar.kt rename to app/src/main/java/me/ash/reader/ui/page/home/reading/ReadBar.kt index 14c7ac4..e3bf3a2 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/read/ReadBar.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/reading/ReadBar.kt @@ -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 diff --git a/app/src/main/java/me/ash/reader/ui/page/home/read/ReadPage.kt b/app/src/main/java/me/ash/reader/ui/page/home/reading/ReadingPage.kt similarity index 84% rename from app/src/main/java/me/ash/reader/ui/page/home/read/ReadPage.kt rename to app/src/main/java/me/ash/reader/ui/page/home/reading/ReadingPage.kt index 3f4958c..b856446 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/read/ReadPage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/reading/ReadingPage.kt @@ -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() }, ) } diff --git a/app/src/main/java/me/ash/reader/ui/page/home/read/ReadViewModel.kt b/app/src/main/java/me/ash/reader/ui/page/home/reading/ReadingViewModel.kt similarity index 51% rename from app/src/main/java/me/ash/reader/ui/page/home/read/ReadViewModel.kt rename to app/src/main/java/me/ash/reader/ui/page/home/reading/ReadingViewModel.kt index 1aeadf2..4a345d3 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/read/ReadViewModel.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/reading/ReadingViewModel.kt @@ -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 = _viewState.asStateFlow() + private val _readingUiState = MutableStateFlow(ReadingUiState()) + val readingUiState: StateFlow = _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() -} \ No newline at end of file +) \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/SettingsPage.kt b/app/src/main/java/me/ash/reader/ui/page/settings/SettingsPage.kt index df492d9..a643c41 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/SettingsPage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/SettingsPage.kt @@ -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( diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/tips/TipsAndSupportPage.kt b/app/src/main/java/me/ash/reader/ui/page/settings/tips/TipsAndSupportPage.kt index da76ae8..f8f7752 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/tips/TipsAndSupportPage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/tips/TipsAndSupportPage.kt @@ -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() diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/tips/UpdateDialog.kt b/app/src/main/java/me/ash/reader/ui/page/settings/tips/UpdateDialog.kt index d93b2e8..8080431 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/tips/UpdateDialog.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/tips/UpdateDialog.kt @@ -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)) diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/tips/UpdateViewModel.kt b/app/src/main/java/me/ash/reader/ui/page/settings/tips/UpdateViewModel.kt index 1a4e54a..a90405e 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/tips/UpdateViewModel.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/tips/UpdateViewModel.kt @@ -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 = _viewState.asStateFlow() + private val _updateUiState = MutableStateFlow(UpdateUiState()) + val updateUiState: StateFlow = _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 = 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() -} \ No newline at end of file From 647ef7a0bd33f716fbe77f502bfd3167e620b4d1 Mon Sep 17 00:00:00 2001 From: Ash Date: Sat, 21 May 2022 18:42:27 +0800 Subject: [PATCH 13/18] Rename RY component --- .../2.json | 321 ++++++++++++++++++ app/src/main/AndroidManifest.xml | 2 +- .../java/me/ash/reader/{App.kt => RYApp.kt} | 14 +- .../ash/reader/data/module/DatabaseModule.kt | 22 +- .../ash/reader/data/module/RetrofitModule.kt | 6 +- .../{AppRepository.kt => RYRepository.kt} | 10 +- .../{ReaderDatabase.kt => RYDatabase.kt} | 10 +- ...rkDataSource.kt => RYNetworkDataSource.kt} | 8 +- .../ui/component/base/BlockRadioButton.kt | 2 +- .../ui/component/base/ClipboardTextField.kt | 2 +- .../ui/component/base/DynamicSVGImage.kt | 2 +- .../base/{AsyncImage.kt => RYAsyncImage.kt} | 2 +- .../component/base/{Dialog.kt => RYDialog.kt} | 2 +- .../base/{Scaffold.kt => RYScaffold.kt} | 2 +- .../component/base/{Switch.kt => RYSwitch.kt} | 4 +- .../base/{TextField.kt => RYTextField.kt} | 2 +- .../reader/ui/component/base/RadioDialog.kt | 2 +- .../ui/component/base/TextFieldDialog.kt | 2 +- .../ui/component/reader/HtmlToComposable.kt | 6 +- .../reader/ui/page/home/feeds/FeedsPage.kt | 7 +- .../home/feeds/drawer/feed/ClearFeedDialog.kt | 4 +- .../feeds/drawer/feed/DeleteFeedDialog.kt | 4 +- .../group/AllAllowNotificationDialog.kt | 4 +- .../drawer/group/AllMoveToGroupDialog.kt | 4 +- .../drawer/group/AllParseFullContentDialog.kt | 4 +- .../feeds/drawer/group/ClearGroupDialog.kt | 4 +- .../feeds/drawer/group/DeleteGroupDialog.kt | 4 +- .../home/feeds/subscribe/SubscribeDialog.kt | 4 +- .../reader/ui/page/home/flow/ArticleItem.kt | 4 +- .../ash/reader/ui/page/home/flow/FlowPage.kt | 3 +- .../ui/page/home/reading/ReadingPage.kt | 3 +- .../reader/ui/page/settings/SettingsPage.kt | 3 +- .../page/settings/color/ColorAndStylePage.kt | 6 +- .../ui/page/settings/color/DarkThemePage.kt | 9 +- .../color/feeds/FeedsPageStylePage.kt | 6 +- .../settings/color/flow/FlowPageStylePage.kt | 17 +- .../settings/interaction/InteractionPage.kt | 2 +- .../page/settings/languages/LanguagesPage.kt | 3 +- .../page/settings/tips/TipsAndSupportPage.kt | 3 +- .../ui/page/settings/tips/UpdateDialog.kt | 4 +- .../ui/page/settings/tips/UpdateViewModel.kt | 8 +- .../ash/reader/ui/page/startup/StartupPage.kt | 3 +- 42 files changed, 427 insertions(+), 107 deletions(-) create mode 100644 app/schemas/me.ash.reader.data.source.RYDatabase/2.json rename app/src/main/java/me/ash/reader/{App.kt => RYApp.kt} (89%) rename app/src/main/java/me/ash/reader/data/repository/{AppRepository.kt => RYRepository.kt} (92%) rename app/src/main/java/me/ash/reader/data/source/{ReaderDatabase.kt => RYDatabase.kt} (86%) rename app/src/main/java/me/ash/reader/data/source/{AppNetworkDataSource.kt => RYNetworkDataSource.kt} (93%) rename app/src/main/java/me/ash/reader/ui/component/base/{AsyncImage.kt => RYAsyncImage.kt} (99%) rename app/src/main/java/me/ash/reader/ui/component/base/{Dialog.kt => RYDialog.kt} (98%) rename app/src/main/java/me/ash/reader/ui/component/base/{Scaffold.kt => RYScaffold.kt} (99%) rename app/src/main/java/me/ash/reader/ui/component/base/{Switch.kt => RYSwitch.kt} (98%) rename app/src/main/java/me/ash/reader/ui/component/base/{TextField.kt => RYTextField.kt} (99%) diff --git a/app/schemas/me.ash.reader.data.source.RYDatabase/2.json b/app/schemas/me.ash.reader.data.source.RYDatabase/2.json new file mode 100644 index 0000000..5690ca5 --- /dev/null +++ b/app/schemas/me.ash.reader.data.source.RYDatabase/2.json @@ -0,0 +1,321 @@ +{ + "formatVersion": 1, + "database": { + "version": 2, + "identityHash": "98462c2e9c32394054102313366e7262", + "entities": [ + { + "tableName": "account", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `name` TEXT NOT NULL, `type` INTEGER NOT NULL, `updateAt` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "updateAt", + "columnName": "updateAt", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "feed", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `icon` TEXT, `url` TEXT NOT NULL, `groupId` TEXT NOT NULL, `accountId` INTEGER NOT NULL, `isNotification` INTEGER NOT NULL DEFAULT false, `isFullContent` INTEGER NOT NULL DEFAULT false, PRIMARY KEY(`id`), FOREIGN KEY(`groupId`) REFERENCES `group`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "icon", + "columnName": "icon", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "groupId", + "columnName": "groupId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isNotification", + "columnName": "isNotification", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "false" + }, + { + "fieldPath": "isFullContent", + "columnName": "isFullContent", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "false" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_feed_groupId", + "unique": false, + "columnNames": [ + "groupId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_feed_groupId` ON `${TABLE_NAME}` (`groupId`)" + }, + { + "name": "index_feed_accountId", + "unique": false, + "columnNames": [ + "accountId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_feed_accountId` ON `${TABLE_NAME}` (`accountId`)" + } + ], + "foreignKeys": [ + { + "table": "group", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "groupId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "article", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `date` INTEGER NOT NULL, `title` TEXT NOT NULL, `author` TEXT, `rawDescription` TEXT NOT NULL, `shortDescription` TEXT NOT NULL, `fullContent` TEXT, `img` TEXT, `link` TEXT NOT NULL, `feedId` TEXT NOT NULL, `accountId` INTEGER NOT NULL, `isUnread` INTEGER NOT NULL DEFAULT true, `isStarred` INTEGER NOT NULL DEFAULT false, `isReadLater` INTEGER NOT NULL DEFAULT false, PRIMARY KEY(`id`), FOREIGN KEY(`feedId`) REFERENCES `feed`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "date", + "columnName": "date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "author", + "columnName": "author", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "rawDescription", + "columnName": "rawDescription", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shortDescription", + "columnName": "shortDescription", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "fullContent", + "columnName": "fullContent", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "img", + "columnName": "img", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "link", + "columnName": "link", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "feedId", + "columnName": "feedId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isUnread", + "columnName": "isUnread", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "true" + }, + { + "fieldPath": "isStarred", + "columnName": "isStarred", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "false" + }, + { + "fieldPath": "isReadLater", + "columnName": "isReadLater", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "false" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_article_feedId", + "unique": false, + "columnNames": [ + "feedId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_article_feedId` ON `${TABLE_NAME}` (`feedId`)" + }, + { + "name": "index_article_accountId", + "unique": false, + "columnNames": [ + "accountId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_article_accountId` ON `${TABLE_NAME}` (`accountId`)" + } + ], + "foreignKeys": [ + { + "table": "feed", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "feedId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "group", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `accountId` INTEGER NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_group_accountId", + "unique": false, + "columnNames": [ + "accountId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_group_accountId` ON `${TABLE_NAME}` (`accountId`)" + } + ], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '98462c2e9c32394054102313366e7262')" + ] + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5830478..9976f51 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -6,7 +6,7 @@ { withContext(dispatcherMain) { @@ -85,7 +85,7 @@ class AppRepository @Inject constructor( withContext(dispatcherIO) { Log.i("RLog", "downloadFile start: $url") try { - return@withContext appNetworkDataSource.downloadFile(url) + return@withContext RYNetworkDataSource.downloadFile(url) .downloadToFileWithProgress(context.getLatestApk()) } catch (e: Exception) { e.printStackTrace() diff --git a/app/src/main/java/me/ash/reader/data/source/ReaderDatabase.kt b/app/src/main/java/me/ash/reader/data/source/RYDatabase.kt similarity index 86% rename from app/src/main/java/me/ash/reader/data/source/ReaderDatabase.kt rename to app/src/main/java/me/ash/reader/data/source/RYDatabase.kt index 2fa0971..e3cf064 100644 --- a/app/src/main/java/me/ash/reader/data/source/ReaderDatabase.kt +++ b/app/src/main/java/me/ash/reader/data/source/RYDatabase.kt @@ -18,21 +18,21 @@ import java.util.* entities = [Account::class, Feed::class, Article::class, Group::class], version = 2, ) -@TypeConverters(ReaderDatabase.Converters::class) -abstract class ReaderDatabase : RoomDatabase() { +@TypeConverters(RYDatabase.Converters::class) +abstract class RYDatabase : RoomDatabase() { abstract fun accountDao(): AccountDao abstract fun feedDao(): FeedDao abstract fun articleDao(): ArticleDao abstract fun groupDao(): GroupDao companion object { - private var instance: ReaderDatabase? = null + private var instance: RYDatabase? = null - fun getInstance(context: Context): ReaderDatabase { + fun getInstance(context: Context): RYDatabase { return instance ?: synchronized(this) { instance ?: Room.databaseBuilder( context.applicationContext, - ReaderDatabase::class.java, + RYDatabase::class.java, "Reader" ).addMigrations(*allMigrations).build().also { instance = it diff --git a/app/src/main/java/me/ash/reader/data/source/AppNetworkDataSource.kt b/app/src/main/java/me/ash/reader/data/source/RYNetworkDataSource.kt similarity index 93% rename from app/src/main/java/me/ash/reader/data/source/AppNetworkDataSource.kt rename to app/src/main/java/me/ash/reader/data/source/RYNetworkDataSource.kt index df0709e..f1e4fdc 100644 --- a/app/src/main/java/me/ash/reader/data/source/AppNetworkDataSource.kt +++ b/app/src/main/java/me/ash/reader/data/source/RYNetworkDataSource.kt @@ -14,7 +14,7 @@ import retrofit2.http.Streaming import retrofit2.http.Url import java.io.File -interface AppNetworkDataSource { +interface RYNetworkDataSource { @GET suspend fun getReleaseLatest(@Url url: String): Response @@ -23,14 +23,14 @@ interface AppNetworkDataSource { suspend fun downloadFile(@Url url: String): ResponseBody companion object { - private var instance: AppNetworkDataSource? = null + private var instance: RYNetworkDataSource? = null - fun getInstance(): AppNetworkDataSource { + fun getInstance(): RYNetworkDataSource { return instance ?: synchronized(this) { instance ?: Retrofit.Builder() .baseUrl("https://api.github.com/") .addConverterFactory(GsonConverterFactory.create()) - .build().create(AppNetworkDataSource::class.java).also { + .build().create(RYNetworkDataSource::class.java).also { instance = it } } diff --git a/app/src/main/java/me/ash/reader/ui/component/base/BlockRadioButton.kt b/app/src/main/java/me/ash/reader/ui/component/base/BlockRadioButton.kt index adc29f1..533f643 100644 --- a/app/src/main/java/me/ash/reader/ui/component/base/BlockRadioButton.kt +++ b/app/src/main/java/me/ash/reader/ui/component/base/BlockRadioButton.kt @@ -7,7 +7,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp @Composable -fun BlockRadioGroupButton( +fun BlockRadioButton( modifier: Modifier = Modifier, selected: Int = 0, onSelected: (Int) -> Unit, diff --git a/app/src/main/java/me/ash/reader/ui/component/base/ClipboardTextField.kt b/app/src/main/java/me/ash/reader/ui/component/base/ClipboardTextField.kt index e48d901..cff26ee 100644 --- a/app/src/main/java/me/ash/reader/ui/component/base/ClipboardTextField.kt +++ b/app/src/main/java/me/ash/reader/ui/component/base/ClipboardTextField.kt @@ -32,7 +32,7 @@ fun ClipboardTextField( ) { Column(modifier = modifier) { Spacer(modifier = Modifier.height(10.dp)) - TextField( + RYTextField( readOnly = readOnly, value = value, onValueChange = onValueChange, diff --git a/app/src/main/java/me/ash/reader/ui/component/base/DynamicSVGImage.kt b/app/src/main/java/me/ash/reader/ui/component/base/DynamicSVGImage.kt index da97da5..1240cd5 100644 --- a/app/src/main/java/me/ash/reader/ui/component/base/DynamicSVGImage.kt +++ b/app/src/main/java/me/ash/reader/ui/component/base/DynamicSVGImage.kt @@ -41,7 +41,7 @@ fun DynamicSVGImage( }, ) { Crossfade(targetState = pic) { - AsyncImage( + RYAsyncImage( contentDescription = contentDescription, data = it, placeholder = null, diff --git a/app/src/main/java/me/ash/reader/ui/component/base/AsyncImage.kt b/app/src/main/java/me/ash/reader/ui/component/base/RYAsyncImage.kt similarity index 99% rename from app/src/main/java/me/ash/reader/ui/component/base/AsyncImage.kt rename to app/src/main/java/me/ash/reader/ui/component/base/RYAsyncImage.kt index 039e2ce..c33cd0c 100644 --- a/app/src/main/java/me/ash/reader/ui/component/base/AsyncImage.kt +++ b/app/src/main/java/me/ash/reader/ui/component/base/RYAsyncImage.kt @@ -22,7 +22,7 @@ import me.ash.reader.R val Size_1000 = Size(1000, 1000) @Composable -fun AsyncImage( +fun RYAsyncImage( modifier: Modifier = Modifier, data: Any? = null, size: Size = Size.ORIGINAL, diff --git a/app/src/main/java/me/ash/reader/ui/component/base/Dialog.kt b/app/src/main/java/me/ash/reader/ui/component/base/RYDialog.kt similarity index 98% rename from app/src/main/java/me/ash/reader/ui/component/base/Dialog.kt rename to app/src/main/java/me/ash/reader/ui/component/base/RYDialog.kt index ff7f6c0..00d70f0 100644 --- a/app/src/main/java/me/ash/reader/ui/component/base/Dialog.kt +++ b/app/src/main/java/me/ash/reader/ui/component/base/RYDialog.kt @@ -6,7 +6,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.window.DialogProperties @Composable -fun Dialog( +fun RYDialog( modifier: Modifier = Modifier, visible: Boolean, properties: DialogProperties = DialogProperties(), diff --git a/app/src/main/java/me/ash/reader/ui/component/base/Scaffold.kt b/app/src/main/java/me/ash/reader/ui/component/base/RYScaffold.kt similarity index 99% rename from app/src/main/java/me/ash/reader/ui/component/base/Scaffold.kt rename to app/src/main/java/me/ash/reader/ui/component/base/RYScaffold.kt index d3a7856..c2c3ffe 100644 --- a/app/src/main/java/me/ash/reader/ui/component/base/Scaffold.kt +++ b/app/src/main/java/me/ash/reader/ui/component/base/RYScaffold.kt @@ -18,7 +18,7 @@ import me.ash.reader.ui.theme.palette.onDark @OptIn(ExperimentalMaterial3Api::class) @Composable -fun Scaffold( +fun RYScaffold( containerColor: Color = MaterialTheme.colorScheme.surface, topBarTonalElevation: Dp = 0.dp, containerTonalElevation: Dp = 0.dp, diff --git a/app/src/main/java/me/ash/reader/ui/component/base/Switch.kt b/app/src/main/java/me/ash/reader/ui/component/base/RYSwitch.kt similarity index 98% rename from app/src/main/java/me/ash/reader/ui/component/base/Switch.kt rename to app/src/main/java/me/ash/reader/ui/component/base/RYSwitch.kt index ea2640a..ce9b972 100644 --- a/app/src/main/java/me/ash/reader/ui/component/base/Switch.kt +++ b/app/src/main/java/me/ash/reader/ui/component/base/RYSwitch.kt @@ -31,7 +31,7 @@ import me.ash.reader.ui.theme.palette.onDark // TODO: ripple & swipe @Composable -fun Switch( +fun RYSwitch( modifier: Modifier = Modifier, activated: Boolean, enable: Boolean = true, @@ -101,7 +101,7 @@ fun SwitchHeadline( ) } Box(Modifier.padding(start = 20.dp)) { - Switch(activated = activated) + RYSwitch(activated = activated) } } } diff --git a/app/src/main/java/me/ash/reader/ui/component/base/TextField.kt b/app/src/main/java/me/ash/reader/ui/component/base/RYTextField.kt similarity index 99% rename from app/src/main/java/me/ash/reader/ui/component/base/TextField.kt rename to app/src/main/java/me/ash/reader/ui/component/base/RYTextField.kt index a697795..12dad9e 100644 --- a/app/src/main/java/me/ash/reader/ui/component/base/TextField.kt +++ b/app/src/main/java/me/ash/reader/ui/component/base/RYTextField.kt @@ -19,7 +19,7 @@ import kotlinx.coroutines.delay import me.ash.reader.R @Composable -fun TextField( +fun RYTextField( readOnly: Boolean, value: String, onValueChange: (String) -> Unit, diff --git a/app/src/main/java/me/ash/reader/ui/component/base/RadioDialog.kt b/app/src/main/java/me/ash/reader/ui/component/base/RadioDialog.kt index d7b7136..8363208 100644 --- a/app/src/main/java/me/ash/reader/ui/component/base/RadioDialog.kt +++ b/app/src/main/java/me/ash/reader/ui/component/base/RadioDialog.kt @@ -29,7 +29,7 @@ fun RadioDialog( options: List = emptyList(), onDismissRequest: () -> Unit = {}, ) { - Dialog( + RYDialog( modifier = modifier, visible = visible, onDismissRequest = onDismissRequest, diff --git a/app/src/main/java/me/ash/reader/ui/component/base/TextFieldDialog.kt b/app/src/main/java/me/ash/reader/ui/component/base/TextFieldDialog.kt index b92d2e7..4646188 100644 --- a/app/src/main/java/me/ash/reader/ui/component/base/TextFieldDialog.kt +++ b/app/src/main/java/me/ash/reader/ui/component/base/TextFieldDialog.kt @@ -35,7 +35,7 @@ fun TextFieldDialog( ) { val focusManager = LocalFocusManager.current - Dialog( + RYDialog( modifier = modifier, visible = visible, onDismissRequest = onDismissRequest, diff --git a/app/src/main/java/me/ash/reader/ui/component/reader/HtmlToComposable.kt b/app/src/main/java/me/ash/reader/ui/component/reader/HtmlToComposable.kt index 4288193..e5262ed 100644 --- a/app/src/main/java/me/ash/reader/ui/component/reader/HtmlToComposable.kt +++ b/app/src/main/java/me/ash/reader/ui/component/reader/HtmlToComposable.kt @@ -51,7 +51,7 @@ import coil.size.Precision import coil.size.Size import coil.size.pxOrElse import me.ash.reader.R -import me.ash.reader.ui.component.base.AsyncImage +import me.ash.reader.ui.component.base.RYAsyncImage import org.jsoup.Jsoup import org.jsoup.helper.StringUtil import org.jsoup.nodes.Element @@ -465,7 +465,7 @@ private fun TextComposer.appendTextChildren( // } ) { val imageSize = maxImageSize() - AsyncImage( + RYAsyncImage( modifier = Modifier.fillMaxWidth(), data = imageCandidates.getBestImageForMaxSize( pixelDensity = pixelDensity(), @@ -591,7 +591,7 @@ private fun TextComposer.appendTextChildren( BoxWithConstraints( modifier = Modifier.fillMaxWidth() ) { - AsyncImage( + RYAsyncImage( modifier = Modifier .clickable { onLinkClick(video.link) diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedsPage.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedsPage.kt index 4feb513..d05e283 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedsPage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedsPage.kt @@ -30,10 +30,7 @@ import me.ash.reader.data.model.getName import me.ash.reader.data.preference.* import me.ash.reader.data.repository.SyncWorker.Companion.getIsSyncing import me.ash.reader.ui.component.FilterBar -import me.ash.reader.ui.component.base.Banner -import me.ash.reader.ui.component.base.DisplayText -import me.ash.reader.ui.component.base.FeedbackIconButton -import me.ash.reader.ui.component.base.Subtitle +import me.ash.reader.ui.component.base.* import me.ash.reader.ui.ext.collectAsStateValue import me.ash.reader.ui.ext.findActivity import me.ash.reader.ui.ext.getCurrentVersion @@ -113,7 +110,7 @@ fun FeedsPage( context.findActivity()?.moveTaskToBack(false) } - me.ash.reader.ui.component.base.Scaffold( + RYScaffold( topBarTonalElevation = topBarTonalElevation.value.dp, containerTonalElevation = groupListTonalElevation.value.dp, navigationIcon = { diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/feed/ClearFeedDialog.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/feed/ClearFeedDialog.kt index 412c76a..5014237 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/feed/ClearFeedDialog.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/feed/ClearFeedDialog.kt @@ -11,7 +11,7 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.hilt.navigation.compose.hiltViewModel import me.ash.reader.R -import me.ash.reader.ui.component.base.Dialog +import me.ash.reader.ui.component.base.RYDialog import me.ash.reader.ui.ext.collectAsStateValue import me.ash.reader.ui.ext.showToast @@ -25,7 +25,7 @@ fun ClearFeedDialog( val scope = rememberCoroutineScope() val toastString = stringResource(R.string.clear_articles_in_feed_toast, feedName) - Dialog( + RYDialog( visible = feedOptionUiState.clearDialogVisible, onDismissRequest = { feedOptionViewModel.hideClearDialog() diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/feed/DeleteFeedDialog.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/feed/DeleteFeedDialog.kt index 564d529..1bd1f1d 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/feed/DeleteFeedDialog.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/feed/DeleteFeedDialog.kt @@ -11,7 +11,7 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.hilt.navigation.compose.hiltViewModel import me.ash.reader.R -import me.ash.reader.ui.component.base.Dialog +import me.ash.reader.ui.component.base.RYDialog import me.ash.reader.ui.ext.collectAsStateValue import me.ash.reader.ui.ext.showToast @@ -25,7 +25,7 @@ fun DeleteFeedDialog( val scope = rememberCoroutineScope() val toastString = stringResource(R.string.delete_toast, feedName) - Dialog( + RYDialog( visible = feedOptionUiState.deleteDialogVisible, onDismissRequest = { feedOptionViewModel.hideDeleteDialog() diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/AllAllowNotificationDialog.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/AllAllowNotificationDialog.kt index d9c5094..afe1068 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/AllAllowNotificationDialog.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/AllAllowNotificationDialog.kt @@ -11,7 +11,7 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.hilt.navigation.compose.hiltViewModel import me.ash.reader.R -import me.ash.reader.ui.component.base.Dialog +import me.ash.reader.ui.component.base.RYDialog import me.ash.reader.ui.ext.collectAsStateValue import me.ash.reader.ui.ext.showToast @@ -26,7 +26,7 @@ fun AllAllowNotificationDialog( val allowToastString = stringResource(R.string.all_allow_notification_toast, groupName) val denyToastString = stringResource(R.string.all_deny_notification_toast, groupName) - Dialog( + RYDialog( visible = groupOptionUiState.allAllowNotificationDialogVisible, onDismissRequest = { groupOptionViewModel.hideAllAllowNotificationDialog() diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/AllMoveToGroupDialog.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/AllMoveToGroupDialog.kt index 79c2f53..408c225 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/AllMoveToGroupDialog.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/AllMoveToGroupDialog.kt @@ -11,7 +11,7 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.hilt.navigation.compose.hiltViewModel import me.ash.reader.R -import me.ash.reader.ui.component.base.Dialog +import me.ash.reader.ui.component.base.RYDialog import me.ash.reader.ui.ext.collectAsStateValue import me.ash.reader.ui.ext.showToast @@ -28,7 +28,7 @@ fun AllMoveToGroupDialog( groupOptionUiState.targetGroup?.name ?: "" ) - Dialog( + RYDialog( visible = groupOptionUiState.allMoveToGroupDialogVisible, onDismissRequest = { groupOptionViewModel.hideAllMoveToGroupDialog() diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/AllParseFullContentDialog.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/AllParseFullContentDialog.kt index 00e5abc..7a5b747 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/AllParseFullContentDialog.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/AllParseFullContentDialog.kt @@ -11,7 +11,7 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.hilt.navigation.compose.hiltViewModel import me.ash.reader.R -import me.ash.reader.ui.component.base.Dialog +import me.ash.reader.ui.component.base.RYDialog import me.ash.reader.ui.ext.collectAsStateValue import me.ash.reader.ui.ext.showToast @@ -26,7 +26,7 @@ fun AllParseFullContentDialog( val allowToastString = stringResource(R.string.all_parse_full_content_toast, groupName) val denyToastString = stringResource(R.string.all_deny_parse_full_content_toast, groupName) - Dialog( + RYDialog( visible = groupOptionUiState.allParseFullContentDialogVisible, onDismissRequest = { groupOptionViewModel.hideAllParseFullContentDialog() diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/ClearGroupDialog.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/ClearGroupDialog.kt index 44b81b9..fadd2a2 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/ClearGroupDialog.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/ClearGroupDialog.kt @@ -11,7 +11,7 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.hilt.navigation.compose.hiltViewModel import me.ash.reader.R -import me.ash.reader.ui.component.base.Dialog +import me.ash.reader.ui.component.base.RYDialog import me.ash.reader.ui.ext.collectAsStateValue import me.ash.reader.ui.ext.showToast @@ -25,7 +25,7 @@ fun ClearGroupDialog( val scope = rememberCoroutineScope() val toastString = stringResource(R.string.clear_articles_in_group_toast, groupName) - Dialog( + RYDialog( visible = groupOptionUiState.clearDialogVisible, onDismissRequest = { groupOptionViewModel.hideClearDialog() diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/DeleteGroupDialog.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/DeleteGroupDialog.kt index f9593dc..9809fec 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/DeleteGroupDialog.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/DeleteGroupDialog.kt @@ -11,7 +11,7 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.hilt.navigation.compose.hiltViewModel import me.ash.reader.R -import me.ash.reader.ui.component.base.Dialog +import me.ash.reader.ui.component.base.RYDialog import me.ash.reader.ui.ext.collectAsStateValue import me.ash.reader.ui.ext.showToast @@ -25,7 +25,7 @@ fun DeleteGroupDialog( val scope = rememberCoroutineScope() val toastString = stringResource(R.string.delete_toast, groupName) - Dialog( + RYDialog( visible = groupOptionUiState.deleteDialogVisible, onDismissRequest = { groupOptionViewModel.hideDeleteDialog() 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 675d160..4e44430 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 @@ -26,7 +26,7 @@ import androidx.compose.ui.window.DialogProperties import androidx.hilt.navigation.compose.hiltViewModel import me.ash.reader.R import me.ash.reader.ui.component.base.ClipboardTextField -import me.ash.reader.ui.component.base.Dialog +import me.ash.reader.ui.component.base.RYDialog import me.ash.reader.ui.component.base.TextFieldDialog import me.ash.reader.ui.ext.collectAsStateValue @@ -60,7 +60,7 @@ fun SubscribeDialog( } } - Dialog( + RYDialog( modifier = Modifier.padding(horizontal = 44.dp), visible = subscribeUiState.visible, properties = DialogProperties(usePlatformDefaultWidth = false), diff --git a/app/src/main/java/me/ash/reader/ui/page/home/flow/ArticleItem.kt b/app/src/main/java/me/ash/reader/ui/page/home/flow/ArticleItem.kt index 183265e..819b0ca 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/flow/ArticleItem.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/flow/ArticleItem.kt @@ -23,7 +23,7 @@ import coil.size.Scale import me.ash.reader.R import me.ash.reader.data.entity.ArticleWithFeed import me.ash.reader.data.preference.* -import me.ash.reader.ui.component.base.AsyncImage +import me.ash.reader.ui.component.base.RYAsyncImage import me.ash.reader.ui.ext.formatAsString import me.ash.reader.ui.component.FeedIcon import me.ash.reader.ui.component.base.Size_1000 @@ -139,7 +139,7 @@ fun ArticleItem( // Image if (articleWithFeed.article.img != null && articleListImage.value) { - AsyncImage( + RYAsyncImage( modifier = Modifier .padding(start = 10.dp) .size(80.dp) diff --git a/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowPage.kt b/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowPage.kt index 0aebc58..b67e8f5 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowPage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowPage.kt @@ -30,6 +30,7 @@ import me.ash.reader.data.repository.SyncWorker.Companion.getIsSyncing import me.ash.reader.ui.component.FilterBar import me.ash.reader.ui.component.base.DisplayText import me.ash.reader.ui.component.base.FeedbackIconButton +import me.ash.reader.ui.component.base.RYScaffold import me.ash.reader.ui.component.base.SwipeRefresh import me.ash.reader.ui.ext.collectAsStateValue import me.ash.reader.ui.page.common.RouteName @@ -100,7 +101,7 @@ fun FlowPage( onSearch = false } - me.ash.reader.ui.component.base.Scaffold( + RYScaffold( topBarTonalElevation = topBarTonalElevation.value.dp, containerTonalElevation = articleListTonalElevation.value.dp, navigationIcon = { diff --git a/app/src/main/java/me/ash/reader/ui/page/home/reading/ReadingPage.kt b/app/src/main/java/me/ash/reader/ui/page/home/reading/ReadingPage.kt index b856446..e1bf4e6 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/reading/ReadingPage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/reading/ReadingPage.kt @@ -27,6 +27,7 @@ import androidx.navigation.NavHostController import me.ash.reader.R import me.ash.reader.data.entity.ArticleWithFeed import me.ash.reader.ui.component.base.FeedbackIconButton +import me.ash.reader.ui.component.base.RYScaffold import me.ash.reader.ui.component.reader.reader import me.ash.reader.ui.ext.collectAsStateValue import me.ash.reader.ui.ext.drawVerticalScrollbar @@ -56,7 +57,7 @@ fun ReadingPage( } } - me.ash.reader.ui.component.base.Scaffold( + RYScaffold( content = { Box(Modifier.fillMaxSize()) { Box( diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/SettingsPage.kt b/app/src/main/java/me/ash/reader/ui/page/settings/SettingsPage.kt index a643c41..9d13f7f 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/SettingsPage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/SettingsPage.kt @@ -25,6 +25,7 @@ import me.ash.reader.data.preference.LocalSkipVersionNumber import me.ash.reader.ui.component.base.Banner import me.ash.reader.ui.component.base.DisplayText import me.ash.reader.ui.component.base.FeedbackIconButton +import me.ash.reader.ui.component.base.RYScaffold import me.ash.reader.ui.ext.getCurrentVersion import me.ash.reader.ui.page.common.RouteName import me.ash.reader.ui.page.settings.tips.UpdateDialog @@ -41,7 +42,7 @@ fun SettingsPage( val skipVersion = LocalSkipVersionNumber.current val currentVersion by remember { mutableStateOf(context.getCurrentVersion()) } - me.ash.reader.ui.component.base.Scaffold( + RYScaffold( containerColor = MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface, navigationIcon = { FeedbackIconButton( diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/color/ColorAndStylePage.kt b/app/src/main/java/me/ash/reader/ui/page/settings/color/ColorAndStylePage.kt index 09645b7..818498e 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/color/ColorAndStylePage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/color/ColorAndStylePage.kt @@ -50,7 +50,7 @@ fun ColorAndStylePage( val wallpaperTonalPalettes = extractTonalPalettesFromUserWallpaper() var radioButtonSelected by remember { mutableStateOf(if (themeIndex > 4) 0 else 1) } - me.ash.reader.ui.component.base.Scaffold( + RYScaffold( containerColor = MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface, navigationIcon = { FeedbackIconButton( @@ -90,7 +90,7 @@ fun ColorAndStylePage( Spacer(modifier = Modifier.height(24.dp)) } item { - BlockRadioGroupButton( + BlockRadioButton( selected = radioButtonSelected, onSelected = { radioButtonSelected = it }, itemRadioGroups = listOf( @@ -142,7 +142,7 @@ fun ColorAndStylePage( } }, ) { - me.ash.reader.ui.component.base.Switch( + RYSwitch( activated = darkTheme.isDarkTheme() ) { darkThemeNot.put(context, scope) diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/color/DarkThemePage.kt b/app/src/main/java/me/ash/reader/ui/page/settings/color/DarkThemePage.kt index 588ac2d..88b7441 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/color/DarkThemePage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/color/DarkThemePage.kt @@ -19,10 +19,7 @@ import me.ash.reader.data.preference.DarkThemePreference import me.ash.reader.data.preference.LocalAmoledDarkTheme import me.ash.reader.data.preference.LocalDarkTheme import me.ash.reader.data.preference.not -import me.ash.reader.ui.component.base.DisplayText -import me.ash.reader.ui.component.base.FeedbackIconButton -import me.ash.reader.ui.component.base.Subtitle -import me.ash.reader.ui.component.base.Switch +import me.ash.reader.ui.component.base.* import me.ash.reader.ui.page.settings.SettingItem import me.ash.reader.ui.theme.palette.onLight @@ -36,7 +33,7 @@ fun DarkThemePage( val amoledDarkTheme = LocalAmoledDarkTheme.current val scope = rememberCoroutineScope() - me.ash.reader.ui.component.base.Scaffold( + RYScaffold( containerColor = MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface, navigationIcon = { FeedbackIconButton( @@ -75,7 +72,7 @@ fun DarkThemePage( (!amoledDarkTheme).put(context, scope) }, ) { - Switch(activated = amoledDarkTheme.value) { + RYSwitch(activated = amoledDarkTheme.value) { (!amoledDarkTheme).put(context, scope) } } diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/color/feeds/FeedsPageStylePage.kt b/app/src/main/java/me/ash/reader/ui/page/settings/color/feeds/FeedsPageStylePage.kt index 361b283..108e0c3 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/color/feeds/FeedsPageStylePage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/color/feeds/FeedsPageStylePage.kt @@ -58,7 +58,7 @@ fun FeedsPageStylePage( var filterBarPaddingValue: Int? by remember { mutableStateOf(filterBarPadding) } - me.ash.reader.ui.component.base.Scaffold( + RYScaffold( containerColor = MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface, navigationIcon = { FeedbackIconButton( @@ -132,7 +132,7 @@ fun FeedsPageStylePage( (!groupListExpand).put(context, scope) }, ) { - Switch(activated = groupListExpand.value) { + RYSwitch(activated = groupListExpand.value) { (!groupListExpand).put(context, scope) } } @@ -166,7 +166,7 @@ fun FeedsPageStylePage( (!filterBarFilled).put(context, scope) }, ) { - Switch(activated = filterBarFilled.value) { + RYSwitch(activated = filterBarFilled.value) { (!filterBarFilled).put(context, scope) } } diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/color/flow/FlowPageStylePage.kt b/app/src/main/java/me/ash/reader/ui/page/settings/color/flow/FlowPageStylePage.kt index d437012..0161668 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/color/flow/FlowPageStylePage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/color/flow/FlowPageStylePage.kt @@ -10,7 +10,6 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.ArrowBack import androidx.compose.material.icons.rounded.DoneAll import androidx.compose.material.icons.rounded.Search -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme import androidx.compose.material3.SmallTopAppBar import androidx.compose.material3.TopAppBarDefaults @@ -66,7 +65,7 @@ fun FlowPageStylePage( var filterBarPaddingValue: Int? by remember { mutableStateOf(filterBarPadding) } - me.ash.reader.ui.component.base.Scaffold( + RYScaffold( containerColor = MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface, navigationIcon = { FeedbackIconButton( @@ -145,7 +144,7 @@ fun FlowPageStylePage( (!articleListFeedIcon).put(context, scope) }, ) { - me.ash.reader.ui.component.base.Switch(activated = articleListFeedIcon.value) { + RYSwitch(activated = articleListFeedIcon.value) { (!articleListFeedIcon).put(context, scope) } } @@ -155,7 +154,7 @@ fun FlowPageStylePage( (!articleListFeedName).put(context, scope) }, ) { - me.ash.reader.ui.component.base.Switch(activated = articleListFeedName.value) { + RYSwitch(activated = articleListFeedName.value) { (!articleListFeedName).put(context, scope) } } @@ -165,7 +164,7 @@ fun FlowPageStylePage( (!articleListImage).put(context, scope) }, ) { - me.ash.reader.ui.component.base.Switch(activated = articleListImage.value) { + RYSwitch(activated = articleListImage.value) { (!articleListImage).put(context, scope) } } @@ -175,7 +174,7 @@ fun FlowPageStylePage( (!articleListDesc).put(context, scope) }, ) { - me.ash.reader.ui.component.base.Switch(activated = articleListDesc.value) { + RYSwitch(activated = articleListDesc.value) { (!articleListDesc).put(context, scope) } } @@ -185,7 +184,7 @@ fun FlowPageStylePage( (!articleListTime).put(context, scope) }, ) { - me.ash.reader.ui.component.base.Switch(activated = articleListTime.value) { + RYSwitch(activated = articleListTime.value) { (!articleListTime).put(context, scope) } } @@ -195,7 +194,7 @@ fun FlowPageStylePage( (!articleListStickyDate).put(context, scope) }, ) { - me.ash.reader.ui.component.base.Switch(activated = articleListStickyDate.value) { + RYSwitch(activated = articleListStickyDate.value) { (!articleListStickyDate).put(context, scope) } } @@ -229,7 +228,7 @@ fun FlowPageStylePage( (!filterBarFilled).put(context, scope) }, ) { - me.ash.reader.ui.component.base.Switch(activated = filterBarFilled.value) { + RYSwitch(activated = filterBarFilled.value) { (!filterBarFilled).put(context, scope) } } diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/interaction/InteractionPage.kt b/app/src/main/java/me/ash/reader/ui/page/settings/interaction/InteractionPage.kt index a532ba3..6ca2abc 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/interaction/InteractionPage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/interaction/InteractionPage.kt @@ -33,7 +33,7 @@ fun InteractionPage( var initialPageDialogVisible by remember { mutableStateOf(false) } var initialFilterDialogVisible by remember { mutableStateOf(false) } - me.ash.reader.ui.component.base.Scaffold( + RYScaffold( containerColor = MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface, navigationIcon = { FeedbackIconButton( diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/languages/LanguagesPage.kt b/app/src/main/java/me/ash/reader/ui/page/settings/languages/LanguagesPage.kt index 8b2fc9c..f7dddc1 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/languages/LanguagesPage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/languages/LanguagesPage.kt @@ -26,6 +26,7 @@ import me.ash.reader.data.preference.LocalLanguages import me.ash.reader.ui.component.base.Banner import me.ash.reader.ui.component.base.DisplayText import me.ash.reader.ui.component.base.FeedbackIconButton +import me.ash.reader.ui.component.base.RYScaffold import me.ash.reader.ui.page.settings.SettingItem import me.ash.reader.ui.theme.palette.onLight @@ -38,7 +39,7 @@ fun LanguagesPage( val languages = LocalLanguages.current val scope = rememberCoroutineScope() - me.ash.reader.ui.component.base.Scaffold( + RYScaffold( containerColor = MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface, navigationIcon = { FeedbackIconButton( diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/tips/TipsAndSupportPage.kt b/app/src/main/java/me/ash/reader/ui/page/settings/tips/TipsAndSupportPage.kt index f8f7752..c2d7b9a 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/tips/TipsAndSupportPage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/tips/TipsAndSupportPage.kt @@ -38,6 +38,7 @@ import androidx.navigation.NavHostController import me.ash.reader.R import me.ash.reader.ui.component.base.CurlyCornerShape import me.ash.reader.ui.component.base.FeedbackIconButton +import me.ash.reader.ui.component.base.RYScaffold import me.ash.reader.ui.ext.* import me.ash.reader.ui.theme.palette.alwaysLight import me.ash.reader.ui.theme.palette.onLight @@ -61,7 +62,7 @@ fun TipsAndSupportPage( currentVersion = context.getCurrentVersion().toString() } - me.ash.reader.ui.component.base.Scaffold( + RYScaffold( containerColor = MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface, navigationIcon = { FeedbackIconButton( diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/tips/UpdateDialog.kt b/app/src/main/java/me/ash/reader/ui/page/settings/tips/UpdateDialog.kt index 8080431..5bce517 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/tips/UpdateDialog.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/tips/UpdateDialog.kt @@ -33,7 +33,7 @@ import kotlinx.coroutines.Dispatchers import me.ash.reader.R import me.ash.reader.data.preference.* import me.ash.reader.data.source.Download -import me.ash.reader.ui.component.base.Dialog +import me.ash.reader.ui.component.base.RYDialog import me.ash.reader.ui.ext.collectAsStateValue import me.ash.reader.ui.ext.installLatestApk @@ -71,7 +71,7 @@ fun UpdateDialog( } } - Dialog( + RYDialog( modifier = Modifier.heightIn(max = 400.dp), visible = updateUiState.updateDialogVisible, onDismissRequest = { updateViewModel.hideDialog() }, diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/tips/UpdateViewModel.kt b/app/src/main/java/me/ash/reader/ui/page/settings/tips/UpdateViewModel.kt index a90405e..502759e 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/tips/UpdateViewModel.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/tips/UpdateViewModel.kt @@ -5,14 +5,14 @@ import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch -import me.ash.reader.data.repository.AppRepository +import me.ash.reader.data.repository.RYRepository import me.ash.reader.data.source.Download import me.ash.reader.ui.ext.notFdroid import javax.inject.Inject @HiltViewModel class UpdateViewModel @Inject constructor( - private val appRepository: AppRepository, + private val ryRepository: RYRepository, ) : ViewModel() { private val _updateUiState = MutableStateFlow(UpdateUiState()) val updateUiState: StateFlow = _updateUiState.asStateFlow() @@ -24,7 +24,7 @@ class UpdateViewModel @Inject constructor( if (notFdroid) { viewModelScope.launch { preProcessor() - appRepository.checkUpdate().let { + ryRepository.checkUpdate().let { it?.let { if (it) { showDialog() @@ -63,7 +63,7 @@ class UpdateViewModel @Inject constructor( } _updateUiState.update { it.copy( - downloadFlow = appRepository.downloadFile(url) + downloadFlow = ryRepository.downloadFile(url) ) } } diff --git a/app/src/main/java/me/ash/reader/ui/page/startup/StartupPage.kt b/app/src/main/java/me/ash/reader/ui/page/startup/StartupPage.kt index cc955d9..36ac290 100644 --- a/app/src/main/java/me/ash/reader/ui/page/startup/StartupPage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/startup/StartupPage.kt @@ -21,6 +21,7 @@ import kotlinx.coroutines.launch import me.ash.reader.R import me.ash.reader.ui.component.base.DisplayText import me.ash.reader.ui.component.base.DynamicSVGImage +import me.ash.reader.ui.component.base.RYScaffold import me.ash.reader.ui.component.base.Tips import me.ash.reader.ui.ext.DataStoreKeys import me.ash.reader.ui.ext.dataStore @@ -36,7 +37,7 @@ fun StartupPage( val context = LocalContext.current val scope = rememberCoroutineScope() - me.ash.reader.ui.component.base.Scaffold( + RYScaffold( content = { LazyColumn { item { From ae394da38734ab8faac0011674222181b934f905 Mon Sep 17 00:00:00 2001 From: Ash Date: Sun, 22 May 2022 05:06:49 +0800 Subject: [PATCH 14/18] Update dependencies --- app/build.gradle | 2 +- .../reader/ui/component/base/RYScaffold.kt | 18 +++++----- .../reader/ui/component/base/RYTextField.kt | 2 +- .../reader/ui/page/home/feeds/FeedsPage.kt | 4 +-- .../home/feeds/subscribe/SubscribeDialog.kt | 2 +- .../ash/reader/ui/page/home/flow/FlowPage.kt | 5 +-- .../ash/reader/ui/page/home/flow/SearchBar.kt | 2 +- .../reading/{ReadBar.kt => ReadingBar.kt} | 6 ++-- .../ui/page/home/reading/ReadingPage.kt | 17 +++++++--- .../reader/ui/page/settings/SettingsPage.kt | 2 ++ .../page/settings/color/ColorAndStylePage.kt | 4 ++- .../ui/page/settings/color/DarkThemePage.kt | 5 ++- .../color/feeds/FeedsPageStylePage.kt | 4 ++- .../settings/color/flow/FlowPageStylePage.kt | 4 ++- .../settings/interaction/InteractionPage.kt | 7 ++-- .../page/settings/languages/LanguagesPage.kt | 6 ++-- build.gradle | 33 ++++++++++++++----- 17 files changed, 79 insertions(+), 44 deletions(-) rename app/src/main/java/me/ash/reader/ui/page/home/reading/{ReadBar.kt => ReadingBar.kt} (97%) diff --git a/app/build.gradle b/app/build.gradle index 16cbc01..5964199 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -116,7 +116,7 @@ dependencies { implementation "org.conscrypt:conscrypt-android:2.5.2" // https://square.github.io/okhttp/changelogs/changelog/ - implementation "com.squareup.okhttp3:okhttp:5.0.0-alpha.6" + implementation "com.squareup.okhttp3:okhttp:$okhttp" implementation "com.squareup.retrofit2:retrofit:$retrofit2" implementation "com.squareup.retrofit2:converter-gson:$retrofit2" diff --git a/app/src/main/java/me/ash/reader/ui/component/base/RYScaffold.kt b/app/src/main/java/me/ash/reader/ui/component/base/RYScaffold.kt index c2c3ffe..773aed4 100644 --- a/app/src/main/java/me/ash/reader/ui/component/base/RYScaffold.kt +++ b/app/src/main/java/me/ash/reader/ui/component/base/RYScaffold.kt @@ -1,13 +1,8 @@ package me.ash.reader.ui.component.base import androidx.compose.foundation.background -import androidx.compose.foundation.layout.RowScope -import androidx.compose.foundation.layout.navigationBarsPadding -import androidx.compose.foundation.layout.statusBarsPadding -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.SmallTopAppBar -import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.foundation.layout.* +import androidx.compose.material3.* import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -28,7 +23,7 @@ fun RYScaffold( floatingActionButton: (@Composable () -> Unit)? = null, content: @Composable () -> Unit = {}, ) { - androidx.compose.material3.Scaffold( + Scaffold( modifier = Modifier .background( MaterialTheme.colorScheme.surfaceColorAtElevation( @@ -62,7 +57,12 @@ fun RYScaffold( ) } }, - content = { content() }, + content = { + Column { + Spacer(modifier = Modifier.height(it.calculateTopPadding())) + content() + } + }, bottomBar = { bottomBar?.invoke() }, floatingActionButton = { floatingActionButton?.invoke() }, ) diff --git a/app/src/main/java/me/ash/reader/ui/component/base/RYTextField.kt b/app/src/main/java/me/ash/reader/ui/component/base/RYTextField.kt index 12dad9e..e881e88 100644 --- a/app/src/main/java/me/ash/reader/ui/component/base/RYTextField.kt +++ b/app/src/main/java/me/ash/reader/ui/component/base/RYTextField.kt @@ -39,7 +39,7 @@ fun RYTextField( TextField( modifier = Modifier.focusRequester(focusRequester), colors = TextFieldDefaults.textFieldColors( - backgroundColor = Color.Transparent, + containerColor = Color.Transparent, ), maxLines = 1, enabled = !readOnly, diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedsPage.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedsPage.kt index d05e283..4ed4ee6 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedsPage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedsPage.kt @@ -67,7 +67,7 @@ fun FeedsPage( val newVersion = LocalNewVersionNumber.current val skipVersion = LocalSkipVersionNumber.current - val currentVersion by remember { mutableStateOf(context.getCurrentVersion()) } + val currentVersion = remember { context.getCurrentVersion() } val owner = LocalLifecycleOwner.current var isSyncing by remember { mutableStateOf(false) } @@ -89,7 +89,7 @@ fun FeedsPage( ) { result -> feedsViewModel.exportAsOpml { string -> result?.let { uri -> - context.contentResolver.openOutputStream(uri)?.let { outputStream -> + context.contentResolver.openOutputStream(uri)?.use { outputStream -> outputStream.write(string.toByteArray()) } } 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 4e44430..9d122b2 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 @@ -45,7 +45,7 @@ fun SubscribeDialog( val groupsState = subscribeUiState.groups.collectAsState(initial = emptyList()) val launcher = rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) { it?.let { uri -> - context.contentResolver.openInputStream(uri)?.let { inputStream -> + context.contentResolver.openInputStream(uri)?.use { inputStream -> subscribeViewModel.importFromInputStream(inputStream) } } diff --git a/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowPage.kt b/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowPage.kt index b67e8f5..3fba040 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowPage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowPage.kt @@ -240,10 +240,7 @@ fun FlowPage( } } item { - Spacer(modifier = Modifier.height(64.dp)) - if (pagingItems.loadState.source.refresh is LoadState.NotLoading && pagingItems.itemCount != 0) { - Spacer(modifier = Modifier.height(64.dp)) - } + Spacer(modifier = Modifier.height(128.dp)) Spacer(modifier = Modifier.windowInsetsBottomHeight(WindowInsets.navigationBars)) } } diff --git a/app/src/main/java/me/ash/reader/ui/page/home/flow/SearchBar.kt b/app/src/main/java/me/ash/reader/ui/page/home/flow/SearchBar.kt index c327c92..9c6e0b6 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/flow/SearchBar.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/flow/SearchBar.kt @@ -62,7 +62,7 @@ fun SearchBar( .fillMaxWidth() .focusRequester(focusRequester), colors = TextFieldDefaults.textFieldColors( - backgroundColor = Color.Transparent, + containerColor = Color.Transparent, focusedIndicatorColor = Color.Transparent, unfocusedIndicatorColor = Color.Transparent, ), diff --git a/app/src/main/java/me/ash/reader/ui/page/home/reading/ReadBar.kt b/app/src/main/java/me/ash/reader/ui/page/home/reading/ReadingBar.kt similarity index 97% rename from app/src/main/java/me/ash/reader/ui/page/home/reading/ReadBar.kt rename to app/src/main/java/me/ash/reader/ui/page/home/reading/ReadingBar.kt index e3bf3a2..a5af7e8 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/reading/ReadBar.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/reading/ReadingBar.kt @@ -26,7 +26,7 @@ import me.ash.reader.R import me.ash.reader.ui.component.base.CanBeDisabledIconButton @Composable -fun ReadBar( +fun ReadingBar( modifier: Modifier = Modifier, disabled: Boolean, isUnread: Boolean, @@ -40,9 +40,7 @@ fun ReadBar( var fullContent by remember { mutableStateOf(isFullContent) } Surface( - modifier = Modifier - .background(MaterialTheme.colorScheme.surface) - .navigationBarsPadding(), + modifier = modifier.background(MaterialTheme.colorScheme.surface), tonalElevation = 0.dp, ) { Box( diff --git a/app/src/main/java/me/ash/reader/ui/page/home/reading/ReadingPage.kt b/app/src/main/java/me/ash/reader/ui/page/home/reading/ReadingPage.kt index e1bf4e6..f6fef8c 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/reading/ReadingPage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/reading/ReadingPage.kt @@ -80,6 +80,7 @@ fun ReadingPage( articleWithFeed = readingUiState.articleWithFeed, isLoading = readingUiState.isLoading, listState = readingUiState.listState, + isShowToolBar = readingUiState.articleWithFeed == null || !isScrollDown, ) Box( modifier = Modifier @@ -186,6 +187,7 @@ private fun Content( articleWithFeed: ArticleWithFeed?, listState: LazyListState, isLoading: Boolean, + isShowToolBar: Boolean, ) { if (articleWithFeed == null) return val context = LocalContext.current @@ -195,7 +197,13 @@ private fun Content( modifier = Modifier .fillMaxSize() .statusBarsPadding() - .navigationBarsPadding() + .run { + if (isShowToolBar) { + navigationBarsPadding() + } else { + this + } + } .drawVerticalScrollbar(listState), state = listState, ) { @@ -242,8 +250,8 @@ private fun Content( ) } item { - Spacer(modifier = Modifier.height(64.dp)) - Spacer(modifier = Modifier.height(64.dp)) + Spacer(modifier = Modifier.height(128.dp)) + Spacer(modifier = Modifier.windowInsetsBottomHeight(WindowInsets.navigationBars)) } } } @@ -263,7 +271,8 @@ private fun BottomBar( enter = fadeIn() + expandVertically(), exit = fadeOut() + shrinkVertically(), ) { - ReadBar( + ReadingBar( + modifier = Modifier.navigationBarsPadding(), disabled = false, isUnread = articleWithFeed.article.isUnread, isStarred = articleWithFeed.article.isStarred, diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/SettingsPage.kt b/app/src/main/java/me/ash/reader/ui/page/settings/SettingsPage.kt index 9d13f7f..6821916 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/SettingsPage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/SettingsPage.kt @@ -138,6 +138,8 @@ fun SettingsPage( launchSingleTop = true } } + } + item { Spacer(modifier = Modifier.windowInsetsBottomHeight(WindowInsets.navigationBars)) } } diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/color/ColorAndStylePage.kt b/app/src/main/java/me/ash/reader/ui/page/settings/color/ColorAndStylePage.kt index 818498e..9f5b697 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/color/ColorAndStylePage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/color/ColorAndStylePage.kt @@ -182,7 +182,9 @@ fun ColorAndStylePage( enable = false, onClick = {}, ) {} - Spacer(modifier = Modifier.height(24.dp)) + } + item { + Spacer(modifier = Modifier.windowInsetsBottomHeight(WindowInsets.navigationBars)) } } } diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/color/DarkThemePage.kt b/app/src/main/java/me/ash/reader/ui/page/settings/color/DarkThemePage.kt index 88b7441..be16636 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/color/DarkThemePage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/color/DarkThemePage.kt @@ -1,6 +1,6 @@ package me.ash.reader.ui.page.settings.color -import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.ArrowBack @@ -77,6 +77,9 @@ fun DarkThemePage( } } } + item { + Spacer(modifier = Modifier.windowInsetsBottomHeight(WindowInsets.navigationBars)) + } } } ) diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/color/feeds/FeedsPageStylePage.kt b/app/src/main/java/me/ash/reader/ui/page/settings/color/feeds/FeedsPageStylePage.kt index 108e0c3..2c4095f 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/color/feeds/FeedsPageStylePage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/color/feeds/FeedsPageStylePage.kt @@ -185,7 +185,9 @@ fun FeedsPageStylePage( filterBarTonalElevationDialogVisible = true }, ) {} - Spacer(modifier = Modifier.height(24.dp)) + } + item { + Spacer(modifier = Modifier.windowInsetsBottomHeight(WindowInsets.navigationBars)) } } } diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/color/flow/FlowPageStylePage.kt b/app/src/main/java/me/ash/reader/ui/page/settings/color/flow/FlowPageStylePage.kt index 0161668..d8c7116 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/color/flow/FlowPageStylePage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/color/flow/FlowPageStylePage.kt @@ -247,7 +247,9 @@ fun FlowPageStylePage( filterBarTonalElevationDialogVisible = true }, ) {} - Spacer(modifier = Modifier.height(24.dp)) + } + item { + Spacer(modifier = Modifier.windowInsetsBottomHeight(WindowInsets.navigationBars)) } } } diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/interaction/InteractionPage.kt b/app/src/main/java/me/ash/reader/ui/page/settings/interaction/InteractionPage.kt index 6ca2abc..e8b39cc 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/interaction/InteractionPage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/interaction/InteractionPage.kt @@ -1,8 +1,6 @@ package me.ash.reader.ui.page.settings.interaction -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.ArrowBack @@ -70,6 +68,9 @@ fun InteractionPage( }, ) {} } + item { + Spacer(modifier = Modifier.windowInsetsBottomHeight(WindowInsets.navigationBars)) + } } } ) diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/languages/LanguagesPage.kt b/app/src/main/java/me/ash/reader/ui/page/settings/languages/LanguagesPage.kt index f7dddc1..0268562 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/languages/LanguagesPage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/languages/LanguagesPage.kt @@ -2,8 +2,7 @@ package me.ash.reader.ui.page.settings.languages import android.content.Intent import android.net.Uri -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.KeyboardArrowRight @@ -88,6 +87,9 @@ fun LanguagesPage( } } } + item { + Spacer(modifier = Modifier.windowInsetsBottomHeight(WindowInsets.navigationBars)) + } } } ) diff --git a/build.gradle b/build.gradle index 02bfb71..3cea2b7 100644 --- a/build.gradle +++ b/build.gradle @@ -1,20 +1,37 @@ buildscript { ext { - compose = '1.2.0-alpha07' - accompanist = '0.24.6-alpha' - material3 = '1.0.0-alpha09' - lifecycle = '2.5.0-alpha06' - navigation = '2.5.0-alpha04' + // https://developer.android.com/jetpack/androidx/releases/compose-ui + compose = '1.2.0-beta02' + // https://github.com/google/accompanist/releases + accompanist = '0.24.7-alpha' + // https://developer.android.com/jetpack/androidx/releases/compose-material3 + material3 = '1.0.0-alpha12' + // https://developer.android.com/jetpack/androidx/releases/lifecycle + lifecycle = '2.5.0-rc01' + // https://developer.android.com/jetpack/androidx/releases/navigation + navigation = '2.5.0-rc01' + // https://developer.android.com/jetpack/androidx/releases/paging paging = '3.1.1' + // https://developer.android.com/jetpack/androidx/releases/room room = '2.5.0-alpha01' + // https://developer.android.com/jetpack/androidx/releases/datastore datastore = '1.0.0' + // https://developer.android.com/jetpack/androidx/releases/work work = '2.8.0-alpha02' - profileinstaller = '1.2.0-alpha02' + // https://developer.android.com/jetpack/androidx/releases/profileinstaller + profileinstaller = '1.2.0-beta02' + // https://square.github.io/okhttp/changelogs/changelog/ + okhttp = '5.0.0-alpha.7' retrofit2 = '2.9.0' - coil = '2.0.0-rc03' + // https://coil-kt.github.io/coil/changelog/ + coil = '2.1.0' + // https://mvnrepository.com/artifact/com.rometools/rome rome = '1.18.0' + // https://github.com/dankito/Readability4J readability4j = '1.0.8' + // https://github.com/mdewilde/opml-parser opmlParser = '2.2.0' + // http://bigbadaboom.github.io/androidsvg/release_notes.html androidSVG = '1.4' } @@ -27,7 +44,7 @@ buildscript { plugins { id 'com.android.application' version '7.1.1' apply false id 'com.android.library' version '7.1.1' apply false - id 'org.jetbrains.kotlin.android' version '1.6.10' apply false + id 'org.jetbrains.kotlin.android' version '1.6.21' apply false } task clean(type: Delete) { From ba3620d84f9186f9825dd3896c4c9e4364028bd1 Mon Sep 17 00:00:00 2001 From: Ash Date: Mon, 23 May 2022 20:54:06 +0800 Subject: [PATCH 15/18] Optimize the reading page --- app/build.gradle | 2 +- .../reader/ui/component/base/AnimatedPopup.kt | 8 +- .../reader/ui/component/base/DisplayText.kt | 8 +- .../reader/ui/component/base/RYAsyncImage.kt | 74 +++-- .../component/base/RYExtensibleVisibility.kt | 15 + .../{SelectionChip.kt => RYSelectionChip.kt} | 2 +- .../ash/reader/ui/component/reader/Reader.kt | 3 +- .../java/me/ash/reader/ui/ext/ColorScheme.kt | 5 +- .../java/me/ash/reader/ui/ext/ContextExt.kt | 17 ++ .../me/ash/reader/ui/ext/LazyListStateExt.kt | 33 ++- .../me/ash/reader/ui/ext/ScrollbarsExt.kt | 13 +- .../reader/ui/page/home/feeds/GroupItem.kt | 8 +- .../feeds/drawer/group/GroupOptionDrawer.kt | 14 +- .../page/home/feeds/subscribe/ResultView.kt | 14 +- .../ash/reader/ui/page/home/flow/FlowPage.kt | 21 +- .../reader/ui/page/home/reading/BottomBar.kt | 131 +++++++++ .../reader/ui/page/home/reading/Content.kt | 101 +++++++ .../ash/reader/ui/page/home/reading/Header.kt | 41 +-- .../reader/ui/page/home/reading/ReadingBar.kt | 140 --------- .../ui/page/home/reading/ReadingPage.kt | 278 +++--------------- .../ui/page/home/reading/ReadingViewModel.kt | 11 +- .../ash/reader/ui/page/home/reading/TopBar.kt | 72 +++++ 22 files changed, 525 insertions(+), 486 deletions(-) create mode 100644 app/src/main/java/me/ash/reader/ui/component/base/RYExtensibleVisibility.kt rename app/src/main/java/me/ash/reader/ui/component/base/{SelectionChip.kt => RYSelectionChip.kt} (99%) create mode 100644 app/src/main/java/me/ash/reader/ui/page/home/reading/BottomBar.kt create mode 100644 app/src/main/java/me/ash/reader/ui/page/home/reading/Content.kt delete mode 100644 app/src/main/java/me/ash/reader/ui/page/home/reading/ReadingBar.kt create mode 100644 app/src/main/java/me/ash/reader/ui/page/home/reading/TopBar.kt diff --git a/app/build.gradle b/app/build.gradle index 5964199..fbd5484 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -166,9 +166,9 @@ dependencies { // https://developer.android.com/jetpack/androidx/releases/compose-material implementation "androidx.compose.material:material:$compose" implementation "androidx.compose.material:material-icons-extended:$compose" + debugImplementation "androidx.compose.ui:ui-tooling:$compose" implementation "androidx.compose.ui:ui-tooling-preview:$compose" androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose" - debugImplementation "androidx.compose.ui:ui-tooling:$compose" // hilt implementation "androidx.hilt:hilt-work:1.0.0" diff --git a/app/src/main/java/me/ash/reader/ui/component/base/AnimatedPopup.kt b/app/src/main/java/me/ash/reader/ui/component/base/AnimatedPopup.kt index d70044e..9b873e6 100644 --- a/app/src/main/java/me/ash/reader/ui/component/base/AnimatedPopup.kt +++ b/app/src/main/java/me/ash/reader/ui/component/base/AnimatedPopup.kt @@ -1,6 +1,6 @@ package me.ash.reader.ui.component.base -import androidx.compose.animation.* +import RYExtensibleVisibility import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.statusBars import androidx.compose.runtime.Composable @@ -38,11 +38,7 @@ fun AnimatedPopup( } }, ) { - AnimatedVisibility( - visible = visible, - enter = fadeIn() + expandVertically(), - exit = fadeOut() + shrinkVertically(), - ) { + RYExtensibleVisibility(visible = visible) { content() } } diff --git a/app/src/main/java/me/ash/reader/ui/component/base/DisplayText.kt b/app/src/main/java/me/ash/reader/ui/component/base/DisplayText.kt index 965b22d..56ee1a5 100644 --- a/app/src/main/java/me/ash/reader/ui/component/base/DisplayText.kt +++ b/app/src/main/java/me/ash/reader/ui/component/base/DisplayText.kt @@ -1,6 +1,6 @@ package me.ash.reader.ui.component.base -import androidx.compose.animation.* +import RYExtensibleVisibility import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height @@ -41,11 +41,7 @@ fun DisplayText( maxLines = 1, overflow = TextOverflow.Ellipsis, ) - AnimatedVisibility( - visible = desc.isNotEmpty(), - enter = fadeIn() + expandVertically(), - exit = fadeOut() + shrinkVertically(), - ) { + RYExtensibleVisibility(visible = desc.isNotEmpty()) { Text( modifier = Modifier.height(16.dp), text = desc, diff --git a/app/src/main/java/me/ash/reader/ui/component/base/RYAsyncImage.kt b/app/src/main/java/me/ash/reader/ui/component/base/RYAsyncImage.kt index c33cd0c..bd86c62 100644 --- a/app/src/main/java/me/ash/reader/ui/component/base/RYAsyncImage.kt +++ b/app/src/main/java/me/ash/reader/ui/component/base/RYAsyncImage.kt @@ -1,7 +1,7 @@ package me.ash.reader.ui.component.base import androidx.annotation.DrawableRes -import androidx.compose.material3.MaterialTheme +import androidx.compose.foundation.Image import androidx.compose.runtime.Composable import androidx.compose.runtime.Immutable import androidx.compose.ui.Modifier @@ -10,10 +10,7 @@ import androidx.compose.ui.graphics.DefaultAlpha import androidx.compose.ui.graphics.drawscope.DrawScope import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.painterResource -import coil.compose.LocalImageLoader -import coil.request.ImageRequest +import coil.compose.rememberImagePainter import coil.size.Precision import coil.size.Scale import coil.size.Size @@ -33,34 +30,51 @@ fun RYAsyncImage( @DrawableRes placeholder: Int? = R.drawable.ic_hourglass_empty_black_24dp, @DrawableRes error: Int? = R.drawable.ic_broken_image_black_24dp, ) { - coil.compose.AsyncImage( - modifier = modifier, - model = ImageRequest - .Builder(LocalContext.current) - .data(data) - .crossfade(true) - .scale(scale) - .precision(precision) - .size(size) - .build(), + Image( + painter = rememberImagePainter( + data = data, + builder = { + if (placeholder != null) placeholder(placeholder) + if (error != null) error(error) + crossfade(true) + scale(scale) + precision(precision) + size(size) + }, + ), contentDescription = contentDescription, contentScale = contentScale, - imageLoader = LocalImageLoader.current, - placeholder = placeholder?.run { - forwardingPainter( - painter = painterResource(this), - colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onSurfaceVariant), - alpha = 0.1f, - ) - }, - error = error?.run { - forwardingPainter( - painter = painterResource(this), - colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onSurfaceVariant), - alpha = 0.1f, - ) - }, + modifier = modifier, ) + +// coil.compose.AsyncImage( +// modifier = modifier, +// model = ImageRequest +// .Builder(LocalContext.current) +// .data(data) +// .crossfade(true) +// .scale(scale) +// .precision(precision) +// .size(size) +// .build(), +// contentDescription = contentDescription, +// contentScale = contentScale, +// imageLoader = LocalImageLoader.current, +// placeholder = placeholder?.run { +// forwardingPainter( +// painter = painterResource(this), +// colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onSurfaceVariant), +// alpha = 0.1f, +// ) +// }, +// error = error?.run { +// forwardingPainter( +// painter = painterResource(this), +// colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onSurfaceVariant), +// alpha = 0.1f, +// ) +// }, +// ) } // From: https://gist.github.com/colinrtwhite/c2966e0b8584b4cdf0a5b05786b20ae1 diff --git a/app/src/main/java/me/ash/reader/ui/component/base/RYExtensibleVisibility.kt b/app/src/main/java/me/ash/reader/ui/component/base/RYExtensibleVisibility.kt new file mode 100644 index 0000000..b1485a5 --- /dev/null +++ b/app/src/main/java/me/ash/reader/ui/component/base/RYExtensibleVisibility.kt @@ -0,0 +1,15 @@ +import androidx.compose.animation.* +import androidx.compose.runtime.Composable + +@Composable +fun RYExtensibleVisibility( + visible: Boolean, + content: @Composable AnimatedVisibilityScope.() -> Unit +) { + AnimatedVisibility( + visible = visible, + enter = fadeIn() + expandVertically(), + exit = fadeOut() + shrinkVertically(), + content = content, + ) +} \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/ui/component/base/SelectionChip.kt b/app/src/main/java/me/ash/reader/ui/component/base/RYSelectionChip.kt similarity index 99% rename from app/src/main/java/me/ash/reader/ui/component/base/SelectionChip.kt rename to app/src/main/java/me/ash/reader/ui/component/base/RYSelectionChip.kt index dc9769b..68b14a9 100644 --- a/app/src/main/java/me/ash/reader/ui/component/base/SelectionChip.kt +++ b/app/src/main/java/me/ash/reader/ui/component/base/RYSelectionChip.kt @@ -26,7 +26,7 @@ import me.ash.reader.ui.theme.palette.alwaysLight @OptIn(ExperimentalMaterialApi::class) @Composable -fun SelectionChip( +fun RYSelectionChip( content: String, selected: Boolean, modifier: Modifier = Modifier, diff --git a/app/src/main/java/me/ash/reader/ui/component/reader/Reader.kt b/app/src/main/java/me/ash/reader/ui/component/reader/Reader.kt index 0a47d3a..464369b 100644 --- a/app/src/main/java/me/ash/reader/ui/component/reader/Reader.kt +++ b/app/src/main/java/me/ash/reader/ui/component/reader/Reader.kt @@ -27,7 +27,8 @@ import android.util.Log import androidx.compose.foundation.lazy.LazyListScope import me.ash.reader.R -fun LazyListScope.reader( +@Suppress("FunctionName") +fun LazyListScope.Reader( context: Context, link: String, content: String, diff --git a/app/src/main/java/me/ash/reader/ui/ext/ColorScheme.kt b/app/src/main/java/me/ash/reader/ui/ext/ColorScheme.kt index 1e2276f..a4c388d 100644 --- a/app/src/main/java/me/ash/reader/ui/ext/ColorScheme.kt +++ b/app/src/main/java/me/ash/reader/ui/ext/ColorScheme.kt @@ -1,16 +1,19 @@ package me.ash.reader.ui.ext import androidx.compose.material3.ColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.compositeOver import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import kotlin.math.ln +@Composable fun ColorScheme.surfaceColorAtElevation( elevation: Dp, color: Color = surface, -): Color = color.atElevation(surfaceTint, elevation) +): Color = remember(this, elevation, color) { color.atElevation(surfaceTint, elevation) } fun Color.atElevation( sourceColor: Color, diff --git a/app/src/main/java/me/ash/reader/ui/ext/ContextExt.kt b/app/src/main/java/me/ash/reader/ui/ext/ContextExt.kt index 2db93f0..3c63125 100644 --- a/app/src/main/java/me/ash/reader/ui/ext/ContextExt.kt +++ b/app/src/main/java/me/ash/reader/ui/ext/ContextExt.kt @@ -4,9 +4,11 @@ import android.app.Activity import android.content.Context import android.content.ContextWrapper import android.content.Intent +import android.net.Uri import android.util.Log import android.widget.Toast import androidx.core.content.FileProvider +import me.ash.reader.R import me.ash.reader.data.model.Version import me.ash.reader.data.model.toVersion import java.io.File @@ -53,4 +55,19 @@ fun Context.showToast(message: String?, duration: Int = Toast.LENGTH_SHORT) { fun Context.showToastLong(message: String?) { showToast(message, Toast.LENGTH_LONG) +} + +fun Context.share(content: String) { + startActivity(Intent.createChooser(Intent(Intent.ACTION_SEND).apply { + putExtra( + Intent.EXTRA_TEXT, + content, + ) + type = "text/plain" + }, getString(R.string.share))) +} + +fun Context.openURL(url: String? = null) { + url?.takeIf { it.trim().isNotEmpty() } + ?.let { startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(it))) } } \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/ui/ext/LazyListStateExt.kt b/app/src/main/java/me/ash/reader/ui/ext/LazyListStateExt.kt index ca9eb77..dccea7c 100644 --- a/app/src/main/java/me/ash/reader/ui/ext/LazyListStateExt.kt +++ b/app/src/main/java/me/ash/reader/ui/ext/LazyListStateExt.kt @@ -1,8 +1,7 @@ package me.ash.reader.ui.ext import androidx.compose.foundation.lazy.LazyListState -import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember +import androidx.compose.runtime.* import androidx.paging.compose.LazyPagingItems import kotlin.math.abs @@ -27,4 +26,34 @@ fun LazyPagingItems.rememberLazyListState(): LazyListState { // Return rememberLazyListState (normal case). else -> androidx.compose.foundation.lazy.rememberLazyListState() } +} + +/** + * TODO: To be improved + * + * Returns whether the LazyListState is currently in the + * downward scrolling state. + */ +@Composable +fun LazyListState.isScrollDown(): Boolean { + var isScrollDown by remember { mutableStateOf(false) } + var preItemIndex by remember { mutableStateOf(0) } + var preScrollStartOffset by remember { mutableStateOf(0) } + + LaunchedEffect(this) { + snapshotFlow { isScrollInProgress }.collect { + if (isScrollInProgress) { + isScrollDown = when { + firstVisibleItemIndex > preItemIndex -> true + firstVisibleItemScrollOffset < preItemIndex -> false + else -> firstVisibleItemScrollOffset > preScrollStartOffset + } + } else { + preItemIndex = firstVisibleItemIndex + preScrollStartOffset = firstVisibleItemScrollOffset + } + } + } + + return isScrollDown } \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/ui/ext/ScrollbarsExt.kt b/app/src/main/java/me/ash/reader/ui/ext/ScrollbarsExt.kt index ce85a11..d7c7184 100644 --- a/app/src/main/java/me/ash/reader/ui/ext/ScrollbarsExt.kt +++ b/app/src/main/java/me/ash/reader/ui/ext/ScrollbarsExt.kt @@ -52,6 +52,7 @@ import androidx.compose.ui.composed import androidx.compose.ui.draw.CacheDrawScope import androidx.compose.ui.draw.DrawResult import androidx.compose.ui.draw.drawWithCache +import androidx.compose.ui.geometry.CornerRadius import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Size import androidx.compose.ui.graphics.Color @@ -173,11 +174,15 @@ private fun CacheDrawScope.onDrawScrollbar( return { if (showScrollbar) { - drawRect( + drawRoundRect( color = color, topLeft = topLeft, size = size, - alpha = alpha() + alpha = alpha(), + cornerRadius = CornerRadius( + x = size.width, + y = size.width, + ) ) } } @@ -217,7 +222,7 @@ private fun Modifier.drawScrollbar( val alpha = remember { Animatable(0f) } LaunchedEffect(scrolled, alpha) { scrolled.collectLatest { - alpha.snapTo(1f) + alpha.snapTo(0.3f) delay(ViewConfiguration.getScrollDefaultDelay().toLong()) alpha.animateTo(0f, animationSpec = FadeOutAnimationSpec) } @@ -231,7 +236,7 @@ private fun Modifier.drawScrollbar( // Calculate thickness here to workaround https://issuetracker.google.com/issues/206972664 val thickness = with(LocalDensity.current) { Thickness.toPx() } - val color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.5f) + val color = MaterialTheme.colorScheme.onSurfaceVariant Modifier .nestedScroll(nestedScrollConnection) .drawWithCache { diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/GroupItem.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/GroupItem.kt index 65e1ab6..61f79d5 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/GroupItem.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/GroupItem.kt @@ -1,7 +1,7 @@ package me.ash.reader.ui.page.home.feeds +import RYExtensibleVisibility import android.view.HapticFeedbackConstants -import androidx.compose.animation.* import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.combinedClickable @@ -104,11 +104,7 @@ fun GroupItem( } } Spacer(modifier = Modifier.height(22.dp)) - AnimatedVisibility( - visible = expanded, - enter = fadeIn() + expandVertically(), - exit = fadeOut() + shrinkVertically(), - ) { + RYExtensibleVisibility(visible = expanded) { Column { feeds.forEach { feed -> FeedItem( diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/GroupOptionDrawer.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/GroupOptionDrawer.kt index d153ee7..b64d5aa 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/GroupOptionDrawer.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/group/GroupOptionDrawer.kt @@ -34,7 +34,7 @@ import kotlinx.coroutines.launch import me.ash.reader.R import me.ash.reader.data.entity.Group import me.ash.reader.ui.component.base.BottomDrawer -import me.ash.reader.ui.component.base.SelectionChip +import me.ash.reader.ui.component.base.RYSelectionChip import me.ash.reader.ui.component.base.Subtitle import me.ash.reader.ui.component.base.TextFieldDialog import me.ash.reader.ui.ext.* @@ -162,7 +162,7 @@ private fun Preset( crossAxisSpacing = 10.dp, mainAxisSpacing = 10.dp, ) { - SelectionChip( + RYSelectionChip( modifier = Modifier.animateContentSize(), content = stringResource(R.string.allow_notification), selected = false, @@ -178,7 +178,7 @@ private fun Preset( ) { groupOptionViewModel.showAllAllowNotificationDialog() } - SelectionChip( + RYSelectionChip( modifier = Modifier.animateContentSize(), content = stringResource(R.string.parse_full_content), selected = false, @@ -194,7 +194,7 @@ private fun Preset( ) { groupOptionViewModel.showAllParseFullContentDialog() } - SelectionChip( + RYSelectionChip( modifier = Modifier.animateContentSize(), content = stringResource(R.string.clear_articles), selected = false, @@ -202,7 +202,7 @@ private fun Preset( groupOptionViewModel.showClearDialog() } if (group?.id != context.currentAccountId.getDefaultGroupId()) { - SelectionChip( + RYSelectionChip( modifier = Modifier.animateContentSize(), content = stringResource(R.string.delete_group), selected = false, @@ -227,7 +227,7 @@ private fun FlowRowGroups( ) { groupOptionUiState.groups.forEach { if (it.id != group?.id) { - SelectionChip( + RYSelectionChip( modifier = Modifier.animateContentSize(), content = it.name, selected = false, @@ -248,7 +248,7 @@ private fun LazyRowGroups( LazyRow { items(groupOptionUiState.groups) { if (it.id != group?.id) { - SelectionChip( + RYSelectionChip( modifier = Modifier.animateContentSize(), content = it.name, selected = false, diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/ResultView.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/ResultView.kt index f6d6dce..692101e 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/ResultView.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/ResultView.kt @@ -30,7 +30,7 @@ import com.google.accompanist.flowlayout.FlowRow import com.google.accompanist.flowlayout.MainAxisAlignment import me.ash.reader.R import me.ash.reader.data.entity.Group -import me.ash.reader.ui.component.base.SelectionChip +import me.ash.reader.ui.component.base.RYSelectionChip import me.ash.reader.ui.component.base.Subtitle import me.ash.reader.ui.ext.roundClick import me.ash.reader.ui.theme.palette.alwaysLight @@ -127,7 +127,7 @@ private fun Preset( crossAxisSpacing = 10.dp, mainAxisSpacing = 10.dp, ) { - SelectionChip( + RYSelectionChip( modifier = Modifier.animateContentSize(), content = stringResource(R.string.allow_notification), selected = selectedAllowNotificationPreset, @@ -144,7 +144,7 @@ private fun Preset( ) { allowNotificationPresetOnClick() } - SelectionChip( + RYSelectionChip( modifier = Modifier.animateContentSize(), content = stringResource(R.string.parse_full_content), selected = selectedParseFullContentPreset, @@ -162,14 +162,14 @@ private fun Preset( parseFullContentPresetOnClick() } if (showUnsubscribe) { - SelectionChip( + RYSelectionChip( modifier = Modifier.animateContentSize(), content = stringResource(R.string.clear_articles), selected = false, ) { clearArticlesOnClick() } - SelectionChip( + RYSelectionChip( modifier = Modifier.animateContentSize(), content = stringResource(R.string.unsubscribe), selected = false, @@ -196,7 +196,7 @@ private fun AddToGroup( verticalAlignment = Alignment.CenterVertically, ) { items(groups) { - SelectionChip( + RYSelectionChip( modifier = Modifier.animateContentSize(), content = it.name, selected = it.id == selectedGroupId, @@ -215,7 +215,7 @@ private fun AddToGroup( mainAxisSpacing = 10.dp, ) { groups.forEach { - SelectionChip( + RYSelectionChip( modifier = Modifier.animateContentSize(), content = it.name, selected = it.id == selectedGroupId, diff --git a/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowPage.kt b/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowPage.kt index 3fba040..2b243ea 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowPage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowPage.kt @@ -1,7 +1,7 @@ package me.ash.reader.ui.page.home.flow +import RYExtensibleVisibility import androidx.activity.compose.BackHandler -import androidx.compose.animation.* import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.rememberLazyListState @@ -19,7 +19,6 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavHostController -import androidx.paging.LoadState import androidx.paging.compose.collectAsLazyPagingItems import kotlinx.coroutines.delay import kotlinx.coroutines.launch @@ -121,11 +120,7 @@ fun FlowPage( } }, actions = { - AnimatedVisibility( - visible = !filterUiState.filter.isStarred(), - enter = fadeIn() + expandVertically(), - exit = fadeOut() + shrinkVertically(), - ) { + RYExtensibleVisibility(visible = !filterUiState.filter.isStarred()) { FeedbackIconButton( imageVector = Icons.Rounded.DoneAll, contentDescription = stringResource(R.string.mark_all_as_read), @@ -171,11 +166,7 @@ fun FlowPage( ) { item { DisplayTextHeader(filterUiState, isSyncing, articleListFeedIcon.value) - AnimatedVisibility( - visible = markAsRead, - enter = fadeIn() + expandVertically(), - exit = fadeOut() + shrinkVertically(), - ) { + RYExtensibleVisibility(visible = markAsRead) { Spacer(modifier = Modifier.height((56 + 24 + 10).dp)) } MarkAsReadBar( @@ -193,11 +184,7 @@ fun FlowPage( markAsReadBefore = it, ) } - AnimatedVisibility( - visible = onSearch, - enter = fadeIn() + expandVertically(), - exit = fadeOut() + shrinkVertically(), - ) { + RYExtensibleVisibility(visible = onSearch) { SearchBar( value = homeUiState.searchContent, placeholder = when { diff --git a/app/src/main/java/me/ash/reader/ui/page/home/reading/BottomBar.kt b/app/src/main/java/me/ash/reader/ui/page/home/reading/BottomBar.kt new file mode 100644 index 0000000..0dbead5 --- /dev/null +++ b/app/src/main/java/me/ash/reader/ui/page/home/reading/BottomBar.kt @@ -0,0 +1,131 @@ +package me.ash.reader.ui.page.home.reading + +import RYExtensibleVisibility +import android.view.HapticFeedbackConstants +import androidx.compose.foundation.layout.* +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.FiberManualRecord +import androidx.compose.material.icons.outlined.Article +import androidx.compose.material.icons.outlined.FiberManualRecord +import androidx.compose.material.icons.outlined.Headphones +import androidx.compose.material.icons.rounded.Article +import androidx.compose.material.icons.rounded.ExpandMore +import androidx.compose.material.icons.rounded.Star +import androidx.compose.material.icons.rounded.StarOutline +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalView +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.compose.ui.zIndex +import me.ash.reader.R +import me.ash.reader.ui.component.base.CanBeDisabledIconButton + +@Composable +fun BottomBar( + isShow: Boolean, + isUnread: Boolean, + isStarred: Boolean, + isFullContent: Boolean, + onUnread: (isUnread: Boolean) -> Unit = {}, + onStarred: (isStarred: Boolean) -> Unit = {}, + onFullContent: (isFullContent: Boolean) -> Unit = {}, +) { + Box( + modifier = Modifier + .fillMaxSize() + .zIndex(1f), + contentAlignment = Alignment.BottomCenter + ) { + RYExtensibleVisibility(visible = isShow) { + val view = LocalView.current + + Surface(modifier = Modifier.navigationBarsPadding()) { + // TODO: Component styles await refactoring + Row( + modifier = Modifier + .fillMaxWidth() + .height(60.dp), + horizontalArrangement = Arrangement.SpaceAround, + verticalAlignment = Alignment.CenterVertically, + ) { + CanBeDisabledIconButton( + modifier = Modifier.size(40.dp), + disabled = false, + imageVector = if (isUnread) { + Icons.Filled.FiberManualRecord + } else { + Icons.Outlined.FiberManualRecord + }, + contentDescription = stringResource(if (isUnread) R.string.mark_as_read else R.string.mark_as_unread), + tint = if (isUnread) { + MaterialTheme.colorScheme.onSecondaryContainer + } else { + MaterialTheme.colorScheme.outline + }, + ) { + view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP) + onUnread(!isUnread) + } + CanBeDisabledIconButton( + modifier = Modifier.size(40.dp), + disabled = false, + imageVector = if (isStarred) { + Icons.Rounded.Star + } else { + Icons.Rounded.StarOutline + }, + contentDescription = stringResource(if (isStarred) R.string.mark_as_unstar else R.string.mark_as_starred), + tint = if (isStarred) { + MaterialTheme.colorScheme.onSecondaryContainer + } else { + MaterialTheme.colorScheme.outline + }, + ) { + view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP) + onStarred(!isStarred) + } + CanBeDisabledIconButton( + disabled = true, + modifier = Modifier.size(40.dp), + imageVector = Icons.Rounded.ExpandMore, + contentDescription = "Next Article", + tint = MaterialTheme.colorScheme.outline, + ) { + view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP) + } + CanBeDisabledIconButton( + modifier = Modifier.size(36.dp), + disabled = true, + imageVector = Icons.Outlined.Headphones, + contentDescription = "Add Tag", + tint = MaterialTheme.colorScheme.outline, + ) { + view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP) + } + CanBeDisabledIconButton( + disabled = false, + modifier = Modifier.size(40.dp), + imageVector = if (isFullContent) { + Icons.Rounded.Article + } else { + Icons.Outlined.Article + }, + contentDescription = stringResource(R.string.parse_full_content), + tint = if (isFullContent) { + MaterialTheme.colorScheme.onSecondaryContainer + } else { + MaterialTheme.colorScheme.outline + }, + ) { + view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP) + onFullContent(!isFullContent) + } + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/ui/page/home/reading/Content.kt b/app/src/main/java/me/ash/reader/ui/page/home/reading/Content.kt new file mode 100644 index 0000000..b1883ad --- /dev/null +++ b/app/src/main/java/me/ash/reader/ui/page/home/reading/Content.kt @@ -0,0 +1,101 @@ +package me.ash.reader.ui.page.home.reading + +import RYExtensibleVisibility +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.foundation.text.selection.DisableSelection +import androidx.compose.foundation.text.selection.SelectionContainer +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.unit.dp +import me.ash.reader.ui.component.reader.Reader +import me.ash.reader.ui.ext.drawVerticalScrollbar +import java.util.* + +@Composable +fun Content( + content: String, + feedName: String, + title: String, + author: String? = null, + link: String? = null, + publishedDate: Date, + listState: LazyListState, + isLoading: Boolean, + isShowToolBar: Boolean, +) { + val context = LocalContext.current + + SelectionContainer { + LazyColumn( + modifier = Modifier + .fillMaxSize() + .statusBarsPadding() + .run { + if (isShowToolBar) { + navigationBarsPadding() + } else { + this + } + } + .drawVerticalScrollbar(listState), + state = listState, + ) { + item { + // Top bar height + Spacer(modifier = Modifier.height(64.dp)) + // padding + Spacer(modifier = Modifier.height(22.dp)) + Column( + modifier = Modifier + .padding(horizontal = 12.dp) + ) { + DisableSelection { + Header( + feedName = feedName, + title = title, + author = author, + link = link, + publishedDate = publishedDate, + ) + } + } + } + item { + Spacer(modifier = Modifier.height(22.dp)) + RYExtensibleVisibility(visible = isLoading) { + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center, + ) { + Column { + Spacer(modifier = Modifier.height(22.dp)) + CircularProgressIndicator( + modifier = Modifier + .size(30.dp), + color = MaterialTheme.colorScheme.onSurface, + ) + Spacer(modifier = Modifier.height(22.dp)) + } + } + } + } + if (!isLoading) { + Reader( + context = context, + link = link ?: "", + content = content + ) + } + item { + Spacer(modifier = Modifier.height(128.dp)) + Spacer(modifier = Modifier.windowInsetsBottomHeight(WindowInsets.navigationBars)) + } + } + } +} diff --git a/app/src/main/java/me/ash/reader/ui/page/home/reading/Header.kt b/app/src/main/java/me/ash/reader/ui/page/home/reading/Header.kt index 40f2552..b50823a 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/reading/Header.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/reading/Header.kt @@ -1,62 +1,67 @@ package me.ash.reader.ui.page.home.reading -import android.content.Intent -import android.net.Uri import androidx.compose.foundation.layout.* import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp -import me.ash.reader.data.entity.ArticleWithFeed import me.ash.reader.ui.ext.formatAsString +import me.ash.reader.ui.ext.openURL import me.ash.reader.ui.ext.roundClick +import java.util.* @Composable fun Header( - articleWithFeed: ArticleWithFeed, + feedName: String, + title: String, + author: String? = null, + link: String? = null, + publishedDate: Date, ) { val context = LocalContext.current + val dateString = remember(publishedDate) { + publishedDate.formatAsString(context, atHourMinute = true) + } Column( modifier = Modifier .fillMaxWidth() .roundClick { - articleWithFeed.article.link.let { - if (it.isNotEmpty()) { - context.startActivity( - Intent(Intent.ACTION_VIEW, Uri.parse(articleWithFeed.article.link)) - ) - } - } + context.openURL(link) } .padding(12.dp) ) { Text( - text = articleWithFeed.article.date.formatAsString(context, atHourMinute = true), - color = MaterialTheme.colorScheme.outline.copy(alpha = 0.7f), + modifier = Modifier.alpha(0.7f), + text = dateString, + color = MaterialTheme.colorScheme.outline, style = MaterialTheme.typography.labelMedium, ) Spacer(modifier = Modifier.height(4.dp)) Text( - text = articleWithFeed.article.title, + text = title, color = MaterialTheme.colorScheme.onSurface, style = MaterialTheme.typography.headlineLarge, ) Spacer(modifier = Modifier.height(4.dp)) - articleWithFeed.article.author?.let { + author?.let { if (it.isNotEmpty()) { Text( + modifier = Modifier.alpha(0.7f), text = it, - color = MaterialTheme.colorScheme.outline.copy(alpha = 0.7f), + color = MaterialTheme.colorScheme.outline, style = MaterialTheme.typography.labelMedium, ) } } Text( - text = articleWithFeed.feed.name, - color = MaterialTheme.colorScheme.outline.copy(alpha = 0.7f), + modifier = Modifier.alpha(0.7f), + text = feedName, + color = MaterialTheme.colorScheme.outline, style = MaterialTheme.typography.labelMedium, ) } diff --git a/app/src/main/java/me/ash/reader/ui/page/home/reading/ReadingBar.kt b/app/src/main/java/me/ash/reader/ui/page/home/reading/ReadingBar.kt deleted file mode 100644 index a5af7e8..0000000 --- a/app/src/main/java/me/ash/reader/ui/page/home/reading/ReadingBar.kt +++ /dev/null @@ -1,140 +0,0 @@ -package me.ash.reader.ui.page.home.reading - -import android.view.HapticFeedbackConstants -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.* -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.FiberManualRecord -import androidx.compose.material.icons.outlined.Article -import androidx.compose.material.icons.outlined.FiberManualRecord -import androidx.compose.material.icons.outlined.Headphones -import androidx.compose.material.icons.rounded.Article -import androidx.compose.material.icons.rounded.ExpandMore -import androidx.compose.material.icons.rounded.Star -import androidx.compose.material.icons.rounded.StarOutline -import androidx.compose.material3.Divider -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Surface -import androidx.compose.runtime.* -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalView -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.unit.dp -import androidx.compose.ui.zIndex -import me.ash.reader.R -import me.ash.reader.ui.component.base.CanBeDisabledIconButton - -@Composable -fun ReadingBar( - modifier: Modifier = Modifier, - disabled: Boolean, - isUnread: Boolean, - isStarred: Boolean, - isFullContent: Boolean, - unreadOnClick: (afterIsUnread: Boolean) -> Unit = {}, - starredOnClick: (afterIsStarred: Boolean) -> Unit = {}, - fullContentOnClick: (afterIsFullContent: Boolean) -> Unit = {}, -) { - val view = LocalView.current - var fullContent by remember { mutableStateOf(isFullContent) } - - Surface( - modifier = modifier.background(MaterialTheme.colorScheme.surface), - tonalElevation = 0.dp, - ) { - Box( - modifier = Modifier.height(60.dp) - ) { - Box { - Divider( - modifier = Modifier - .fillMaxWidth() - .height(1.dp) - .zIndex(1f), - color = MaterialTheme.colorScheme.secondaryContainer.copy(alpha = 0.24f) - ) - } - Row( - modifier = Modifier.fillMaxSize(), - horizontalArrangement = Arrangement.SpaceAround, - verticalAlignment = Alignment.CenterVertically, - ) { - CanBeDisabledIconButton( - modifier = Modifier.size(40.dp), - disabled = disabled, - imageVector = if (isUnread) { - Icons.Filled.FiberManualRecord - } else { - Icons.Outlined.FiberManualRecord - }, - contentDescription = stringResource(if (isUnread) R.string.mark_as_read else R.string.mark_as_unread), - tint = if (isUnread) { - MaterialTheme.colorScheme.onSecondaryContainer - } else { - MaterialTheme.colorScheme.outline - }, - ) { - view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP) - unreadOnClick(!isUnread) - } - CanBeDisabledIconButton( - modifier = Modifier.size(40.dp), - disabled = disabled, - imageVector = if (isStarred) { - Icons.Rounded.Star - } else { - Icons.Rounded.StarOutline - }, - contentDescription = stringResource(if (isStarred) R.string.mark_as_unstar else R.string.mark_as_starred), - tint = if (isStarred) { - MaterialTheme.colorScheme.onSecondaryContainer - } else { - MaterialTheme.colorScheme.outline - }, - ) { - view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP) - starredOnClick(!isStarred) - } - CanBeDisabledIconButton( - disabled = true, - modifier = Modifier.size(40.dp), - imageVector = Icons.Rounded.ExpandMore, - contentDescription = "Next Article", - tint = MaterialTheme.colorScheme.outline, - ) { - view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP) - } - CanBeDisabledIconButton( - modifier = Modifier.size(36.dp), - disabled = true, - imageVector = Icons.Outlined.Headphones, - contentDescription = "Add Tag", - tint = MaterialTheme.colorScheme.outline, - ) { - view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP) - } - CanBeDisabledIconButton( - disabled = disabled, - modifier = Modifier.size(40.dp), - imageVector = if (fullContent) { - Icons.Rounded.Article - } else { - Icons.Outlined.Article - }, - contentDescription = stringResource(R.string.parse_full_content), - tint = if (fullContent) { - MaterialTheme.colorScheme.onSecondaryContainer - } else { - MaterialTheme.colorScheme.outline - }, - ) { - view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP) - val afterIsFullContent = !fullContent - fullContent = afterIsFullContent - fullContentOnClick(afterIsFullContent) - } - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/ui/page/home/reading/ReadingPage.kt b/app/src/main/java/me/ash/reader/ui/page/home/reading/ReadingPage.kt index f6fef8c..d198734 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/reading/ReadingPage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/reading/ReadingPage.kt @@ -1,36 +1,16 @@ package me.ash.reader.ui.page.home.reading -import android.content.Intent import android.util.Log -import androidx.compose.animation.* -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.LazyListState -import androidx.compose.foundation.text.selection.DisableSelection -import androidx.compose.foundation.text.selection.SelectionContainer -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.outlined.Share -import androidx.compose.material.icons.rounded.Close -import androidx.compose.material3.CircularProgressIndicator -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.SmallTopAppBar -import androidx.compose.material3.TopAppBarDefaults -import androidx.compose.runtime.* -import androidx.compose.ui.Alignment +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.unit.dp -import androidx.compose.ui.zIndex import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavHostController -import me.ash.reader.R -import me.ash.reader.data.entity.ArticleWithFeed -import me.ash.reader.ui.component.base.FeedbackIconButton import me.ash.reader.ui.component.base.RYScaffold -import me.ash.reader.ui.component.reader.reader import me.ash.reader.ui.ext.collectAsStateValue -import me.ash.reader.ui.ext.drawVerticalScrollbar +import me.ash.reader.ui.ext.isScrollDown @Composable fun ReadingPage( @@ -38,7 +18,8 @@ fun ReadingPage( readingViewModel: ReadingViewModel = hiltViewModel(), ) { val readingUiState = readingViewModel.readingUiState.collectAsStateValue() - val isScrollDown = readingUiState.listState.isScrollDown() + val isShowToolBar = + readingUiState.articleWithFeed != null && !readingUiState.listState.isScrollDown() LaunchedEffect(Unit) { navController.currentBackStackEntryFlow.collect { @@ -59,46 +40,48 @@ fun ReadingPage( RYScaffold( content = { - Box(Modifier.fillMaxSize()) { - Box( - modifier = Modifier - .fillMaxSize() - .zIndex(1f), - contentAlignment = Alignment.TopCenter - ) { - TopBar( - isShow = readingUiState.articleWithFeed == null || !isScrollDown, - title = readingUiState.articleWithFeed?.article?.title, - link = readingUiState.articleWithFeed?.article?.link, - onClose = { - navController.popBackStack() - }, + Log.i("RLog", "TopBar: recomposition") + + Box(modifier = Modifier.fillMaxSize()) { + // Top Bar + TopBar( + isShow = isShowToolBar, + title = readingUiState.articleWithFeed?.article?.title, + link = readingUiState.articleWithFeed?.article?.link, + onClose = { + navController.popBackStack() + }, + ) + + // Content + if (readingUiState.articleWithFeed != null) { + Content( + content = readingUiState.content ?: "", + feedName = readingUiState.articleWithFeed.feed.name, + title = readingUiState.articleWithFeed.article.title, + author = readingUiState.articleWithFeed.article.author, + link = readingUiState.articleWithFeed.article.link, + publishedDate = readingUiState.articleWithFeed.article.date, + isLoading = readingUiState.isLoading, + listState = readingUiState.listState, + isShowToolBar = isShowToolBar, ) } - Content( - content = readingUiState.content ?: "", - articleWithFeed = readingUiState.articleWithFeed, - isLoading = readingUiState.isLoading, - listState = readingUiState.listState, - isShowToolBar = readingUiState.articleWithFeed == null || !isScrollDown, - ) - Box( - modifier = Modifier - .fillMaxSize() - .zIndex(1f), - contentAlignment = Alignment.BottomCenter - ) { + // Bottom Bar + if (readingUiState.articleWithFeed != null) { BottomBar( - isShow = readingUiState.articleWithFeed != null && !isScrollDown, - articleWithFeed = readingUiState.articleWithFeed, - unreadOnClick = { + isShow = isShowToolBar, + isUnread = readingUiState.articleWithFeed.article.isUnread, + isStarred = readingUiState.articleWithFeed.article.isStarred, + isFullContent = readingUiState.isFullContent, + onUnread = { readingViewModel.markUnread(it) }, - starredOnClick = { + onStarred = { readingViewModel.markStarred(it) }, - fullContentOnClick = { afterIsFullContent -> - if (afterIsFullContent) readingViewModel.renderFullContent() + onFullContent = { + if (it) readingViewModel.renderFullContent() else readingViewModel.renderDescriptionContent() }, ) @@ -107,180 +90,3 @@ fun ReadingPage( } ) } - -@Composable -fun LazyListState.isScrollDown(): Boolean { - var isScrollDown by remember { mutableStateOf(false) } - var preItemIndex by remember { mutableStateOf(0) } - var preScrollStartOffset by remember { mutableStateOf(0) } - - LaunchedEffect(this) { - snapshotFlow { isScrollInProgress }.collect { - if (isScrollInProgress) { - isScrollDown = when { - firstVisibleItemIndex > preItemIndex -> true - firstVisibleItemScrollOffset < preItemIndex -> false - else -> firstVisibleItemScrollOffset > preScrollStartOffset - } - } else { - preItemIndex = firstVisibleItemIndex - preScrollStartOffset = firstVisibleItemScrollOffset - } - } - } - - return isScrollDown -} - -@Composable -private fun TopBar( - isShow: Boolean, - title: String? = "", - link: String? = "", - onClose: () -> Unit = {}, -) { - val context = LocalContext.current - - AnimatedVisibility( - visible = isShow, - enter = fadeIn() + expandVertically(), - exit = fadeOut() + shrinkVertically(), - ) { - SmallTopAppBar( - modifier = Modifier.statusBarsPadding(), - colors = TopAppBarDefaults.smallTopAppBarColors( - containerColor = MaterialTheme.colorScheme.surface, - ), - title = {}, - navigationIcon = { - FeedbackIconButton( - imageVector = Icons.Rounded.Close, - contentDescription = stringResource(R.string.close), - tint = MaterialTheme.colorScheme.onSurface - ) { - onClose() - } - }, - actions = { - FeedbackIconButton( - modifier = Modifier.size(20.dp), - imageVector = Icons.Outlined.Share, - contentDescription = stringResource(R.string.share), - tint = MaterialTheme.colorScheme.onSurface, - ) { - context.startActivity(Intent.createChooser(Intent(Intent.ACTION_SEND).apply { - putExtra( - Intent.EXTRA_TEXT, - title?.takeIf { it.isNotBlank() }?.let { it + "\n" } + link - ) - type = "text/plain" - }, context.getString(R.string.share))) - } - } - ) - } -} - -@Composable -private fun Content( - content: String, - articleWithFeed: ArticleWithFeed?, - listState: LazyListState, - isLoading: Boolean, - isShowToolBar: Boolean, -) { - if (articleWithFeed == null) return - val context = LocalContext.current - - SelectionContainer { - LazyColumn( - modifier = Modifier - .fillMaxSize() - .statusBarsPadding() - .run { - if (isShowToolBar) { - navigationBarsPadding() - } else { - this - } - } - .drawVerticalScrollbar(listState), - state = listState, - ) { - item { - Spacer(modifier = Modifier.height(64.dp)) - Spacer(modifier = Modifier.height(22.dp)) - Column( - modifier = Modifier - .padding(horizontal = 12.dp) - ) { - DisableSelection { - Header(articleWithFeed) - } - } - } - item { - Spacer(modifier = Modifier.height(22.dp)) - AnimatedVisibility( - visible = isLoading, - enter = fadeIn() + expandVertically(), - exit = fadeOut() + shrinkVertically(), - ) { - Box( - modifier = Modifier.fillMaxSize(), - contentAlignment = Alignment.Center, - ) { - Column { - Spacer(modifier = Modifier.height(22.dp)) - CircularProgressIndicator( - modifier = Modifier - .size(30.dp), - color = MaterialTheme.colorScheme.onSurface, - ) - Spacer(modifier = Modifier.height(22.dp)) - } - } - } - } - if (!isLoading) { - reader( - context = context, - link = articleWithFeed.article.link, - content = content - ) - } - item { - Spacer(modifier = Modifier.height(128.dp)) - Spacer(modifier = Modifier.windowInsetsBottomHeight(WindowInsets.navigationBars)) - } - } - } -} - -@Composable -private fun BottomBar( - isShow: Boolean, - articleWithFeed: ArticleWithFeed?, - unreadOnClick: (afterIsUnread: Boolean) -> Unit = {}, - starredOnClick: (afterIsStarred: Boolean) -> Unit = {}, - fullContentOnClick: (afterIsFullContent: Boolean) -> Unit = {}, -) { - articleWithFeed?.let { - AnimatedVisibility( - visible = isShow, - enter = fadeIn() + expandVertically(), - exit = fadeOut() + shrinkVertically(), - ) { - ReadingBar( - modifier = Modifier.navigationBarsPadding(), - disabled = false, - isUnread = articleWithFeed.article.isUnread, - isStarred = articleWithFeed.article.isStarred, - isFullContent = articleWithFeed.feed.isFullContent, - unreadOnClick = unreadOnClick, - starredOnClick = starredOnClick, - fullContentOnClick = fullContentOnClick, - ) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/ui/page/home/reading/ReadingViewModel.kt b/app/src/main/java/me/ash/reader/ui/page/home/reading/ReadingViewModel.kt index 4a345d3..e2ca168 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/reading/ReadingViewModel.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/reading/ReadingViewModel.kt @@ -28,7 +28,9 @@ class ReadingViewModel @Inject constructor( showLoading() viewModelScope.launch { _readingUiState.update { - it.copy(articleWithFeed = rssRepository.get().findArticleById(articleId)) + it.copy( + articleWithFeed = rssRepository.get().findArticleById(articleId) + ) } _readingUiState.value.articleWithFeed?.let { if (it.feed.isFullContent) internalRenderFullContent() @@ -43,6 +45,7 @@ class ReadingViewModel @Inject constructor( it.copy( content = it.articleWithFeed?.article?.fullContent ?: it.articleWithFeed?.article?.rawDescription ?: "", + isFullContent = false ) } } @@ -53,7 +56,7 @@ class ReadingViewModel @Inject constructor( } } - suspend fun internalRenderFullContent() { + private suspend fun internalRenderFullContent() { showLoading() try { _readingUiState.update { @@ -61,7 +64,8 @@ class ReadingViewModel @Inject constructor( content = rssHelper.parseFullContent( _readingUiState.value.articleWithFeed?.article?.link ?: "", _readingUiState.value.articleWithFeed?.article?.title ?: "" - ) + ), + isFullContent = true ) } } catch (e: Exception) { @@ -133,6 +137,7 @@ class ReadingViewModel @Inject constructor( data class ReadingUiState( val articleWithFeed: ArticleWithFeed? = null, val content: String? = null, + val isFullContent: Boolean = false, val isLoading: Boolean = true, val listState: LazyListState = LazyListState(), ) \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/ui/page/home/reading/TopBar.kt b/app/src/main/java/me/ash/reader/ui/page/home/reading/TopBar.kt new file mode 100644 index 0000000..4d88925 --- /dev/null +++ b/app/src/main/java/me/ash/reader/ui/page/home/reading/TopBar.kt @@ -0,0 +1,72 @@ +package me.ash.reader.ui.page.home.reading + +import RYExtensibleVisibility +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.statusBarsPadding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Share +import androidx.compose.material.icons.rounded.Close +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.SmallTopAppBar +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.compose.ui.zIndex +import me.ash.reader.R +import me.ash.reader.ui.component.base.FeedbackIconButton +import me.ash.reader.ui.ext.share + +@Composable +fun TopBar( + isShow: Boolean, + title: String? = "", + link: String? = "", + onClose: () -> Unit = {}, +) { + val context = LocalContext.current + + Box( + modifier = Modifier + .fillMaxSize() + .zIndex(1f), + contentAlignment = Alignment.TopCenter + ) { + RYExtensibleVisibility(visible = isShow) { + SmallTopAppBar( + modifier = Modifier.statusBarsPadding(), + colors = TopAppBarDefaults.smallTopAppBarColors( + containerColor = MaterialTheme.colorScheme.surface, + ), + title = {}, + navigationIcon = { + FeedbackIconButton( + imageVector = Icons.Rounded.Close, + contentDescription = stringResource(R.string.close), + tint = MaterialTheme.colorScheme.onSurface + ) { + onClose() + } + }, + actions = { + FeedbackIconButton( + modifier = Modifier.size(20.dp), + imageVector = Icons.Outlined.Share, + contentDescription = stringResource(R.string.share), + tint = MaterialTheme.colorScheme.onSurface, + ) { + context.share(title + ?.takeIf { it.isNotBlank() } + ?.let { it + "\n" } + link + ) + } + } + ) + } + } +} \ No newline at end of file From 66094f8075081db50b3ba9ebe2df3af7fff0791a Mon Sep 17 00:00:00 2001 From: Ash Date: Tue, 24 May 2022 17:47:24 +0800 Subject: [PATCH 16/18] Optimize the flow page --- .../reader/data/constant/ElevationTokens.kt | 10 +++++ .../java/me/ash/reader/data/entity/Article.kt | 10 ++--- .../FeedsFilterBarTonalElevationPreference.kt | 40 ++++++++++--------- .../FeedsGroupListTonalElevationPreference.kt | 37 ++++++++--------- .../FeedsTopBarTonalElevationPreference.kt | 37 ++++++++--------- ...FlowArticleListTonalElevationPreference.kt | 37 ++++++++--------- .../FlowFilterBarTonalElevationPreference.kt | 37 ++++++++--------- .../FlowTopBarTonalElevationPreference.kt | 37 ++++++++--------- .../reader/data/repository/OpmlRepository.kt | 2 +- .../ash/reader/data/repository/RssHelper.kt | 13 +++--- .../data/repository/StringsRepository.kt | 11 ++++- .../reader/ui/component/base/RYAsyncImage.kt | 2 +- .../ash/reader/ui/page/home/HomeViewModel.kt | 16 +++++++- .../feeds/subscribe/SubscribeViewModel.kt | 16 ++------ .../reader/ui/page/home/flow/ArticleItem.kt | 27 ++++++------- .../reader/ui/page/home/flow/ArticleList.kt | 10 ++--- .../ash/reader/ui/page/home/flow/FlowPage.kt | 32 +++++---------- .../reader/ui/page/home/flow/FlowViewModel.kt | 4 -- .../ash/reader/ui/page/home/flow/SearchBar.kt | 17 ++++---- .../reader/ui/page/home/flow/StickyHeader.kt | 8 ++-- .../ui/page/home/reading/ReadingViewModel.kt | 2 +- .../java/me/ash/reader/ui/theme/Shapes.kt | 15 +++++++ .../main/java/me/ash/reader/ui/theme/Theme.kt | 3 +- .../drawable/ic_broken_image_black_24dp.xml | 3 +- .../ic_hourglass_empty_black_24dp.xml | 3 +- 25 files changed, 230 insertions(+), 199 deletions(-) create mode 100644 app/src/main/java/me/ash/reader/data/constant/ElevationTokens.kt create mode 100644 app/src/main/java/me/ash/reader/ui/theme/Shapes.kt diff --git a/app/src/main/java/me/ash/reader/data/constant/ElevationTokens.kt b/app/src/main/java/me/ash/reader/data/constant/ElevationTokens.kt new file mode 100644 index 0000000..4c240e1 --- /dev/null +++ b/app/src/main/java/me/ash/reader/data/constant/ElevationTokens.kt @@ -0,0 +1,10 @@ +package me.ash.reader.data.constant + +object ElevationTokens { + const val Level0 = 0 + const val Level1 = 1 + const val Level2 = 3 + const val Level3 = 6 + const val Level4 = 8 + const val Level5 = 12 +} \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/data/entity/Article.kt b/app/src/main/java/me/ash/reader/data/entity/Article.kt index c18859e..30086fc 100644 --- a/app/src/main/java/me/ash/reader/data/entity/Article.kt +++ b/app/src/main/java/me/ash/reader/data/entity/Article.kt @@ -1,9 +1,6 @@ package me.ash.reader.data.entity -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.ForeignKey -import androidx.room.PrimaryKey +import androidx.room.* import java.util.* @Entity( @@ -45,4 +42,7 @@ data class Article( var isStarred: Boolean = false, @ColumnInfo(defaultValue = "false") var isReadLater: Boolean = false, -) \ No newline at end of file +) { + @Ignore + var dateString: String? = null +} \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/data/preference/FeedsFilterBarTonalElevationPreference.kt b/app/src/main/java/me/ash/reader/data/preference/FeedsFilterBarTonalElevationPreference.kt index ba08eb3..ecdd1cc 100644 --- a/app/src/main/java/me/ash/reader/data/preference/FeedsFilterBarTonalElevationPreference.kt +++ b/app/src/main/java/me/ash/reader/data/preference/FeedsFilterBarTonalElevationPreference.kt @@ -4,17 +4,18 @@ import android.content.Context import androidx.datastore.preferences.core.Preferences import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch +import me.ash.reader.data.constant.ElevationTokens import me.ash.reader.ui.ext.DataStoreKeys import me.ash.reader.ui.ext.dataStore import me.ash.reader.ui.ext.put sealed class FeedsFilterBarTonalElevationPreference(val value: Int) : Preference() { - object Level0 : FeedsFilterBarTonalElevationPreference(0) - object Level1 : FeedsFilterBarTonalElevationPreference(1) - object Level2 : FeedsFilterBarTonalElevationPreference(3) - object Level3 : FeedsFilterBarTonalElevationPreference(6) - object Level4 : FeedsFilterBarTonalElevationPreference(8) - object Level5 : FeedsFilterBarTonalElevationPreference(12) + object Level0 : FeedsFilterBarTonalElevationPreference(ElevationTokens.Level0) + object Level1 : FeedsFilterBarTonalElevationPreference(ElevationTokens.Level1) + object Level2 : FeedsFilterBarTonalElevationPreference(ElevationTokens.Level2) + object Level3 : FeedsFilterBarTonalElevationPreference(ElevationTokens.Level3) + object Level4 : FeedsFilterBarTonalElevationPreference(ElevationTokens.Level4) + object Level5 : FeedsFilterBarTonalElevationPreference(ElevationTokens.Level5) override fun put(context: Context, scope: CoroutineScope) { scope.launch { @@ -27,12 +28,12 @@ sealed class FeedsFilterBarTonalElevationPreference(val value: Int) : Preference fun getDesc(context: Context): String = when (this) { - Level0 -> "Level 0 (0dp)" - Level1 -> "Level 1 (1dp)" - Level2 -> "Level 2 (3dp)" - Level3 -> "Level 3 (6dp)" - Level4 -> "Level 4 (8dp)" - Level5 -> "Level 5 (12dp)" + Level0 -> "Level 0 (${ElevationTokens.Level0}dp)" + Level1 -> "Level 1 (${ElevationTokens.Level1}dp)" + Level2 -> "Level 2 (${ElevationTokens.Level2}dp)" + Level3 -> "Level 3 (${ElevationTokens.Level3}dp)" + Level4 -> "Level 4 (${ElevationTokens.Level4}dp)" + Level5 -> "Level 5 (${ElevationTokens.Level5}dp)" } companion object { @@ -41,13 +42,14 @@ sealed class FeedsFilterBarTonalElevationPreference(val value: Int) : Preference fun fromPreferences(preferences: Preferences) = when (preferences[DataStoreKeys.FeedsFilterBarTonalElevation.key]) { - 0 -> Level0 - 1 -> Level1 - 3 -> Level2 - 6 -> Level3 - 8 -> Level4 - 12 -> Level5 + ElevationTokens.Level0 -> Level0 + ElevationTokens.Level1 -> Level1 + ElevationTokens.Level2 -> Level2 + ElevationTokens.Level3 -> Level3 + ElevationTokens.Level4 -> Level4 + ElevationTokens.Level5 -> Level5 else -> default } } -} \ No newline at end of file +} + diff --git a/app/src/main/java/me/ash/reader/data/preference/FeedsGroupListTonalElevationPreference.kt b/app/src/main/java/me/ash/reader/data/preference/FeedsGroupListTonalElevationPreference.kt index c65afe0..0fbea55 100644 --- a/app/src/main/java/me/ash/reader/data/preference/FeedsGroupListTonalElevationPreference.kt +++ b/app/src/main/java/me/ash/reader/data/preference/FeedsGroupListTonalElevationPreference.kt @@ -4,17 +4,18 @@ import android.content.Context import androidx.datastore.preferences.core.Preferences import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch +import me.ash.reader.data.constant.ElevationTokens import me.ash.reader.ui.ext.DataStoreKeys import me.ash.reader.ui.ext.dataStore import me.ash.reader.ui.ext.put sealed class FeedsGroupListTonalElevationPreference(val value: Int) : Preference() { - object Level0 : FeedsGroupListTonalElevationPreference(0) - object Level1 : FeedsGroupListTonalElevationPreference(1) - object Level2 : FeedsGroupListTonalElevationPreference(3) - object Level3 : FeedsGroupListTonalElevationPreference(6) - object Level4 : FeedsGroupListTonalElevationPreference(8) - object Level5 : FeedsGroupListTonalElevationPreference(12) + object Level0 : FeedsGroupListTonalElevationPreference(ElevationTokens.Level0) + object Level1 : FeedsGroupListTonalElevationPreference(ElevationTokens.Level1) + object Level2 : FeedsGroupListTonalElevationPreference(ElevationTokens.Level2) + object Level3 : FeedsGroupListTonalElevationPreference(ElevationTokens.Level3) + object Level4 : FeedsGroupListTonalElevationPreference(ElevationTokens.Level4) + object Level5 : FeedsGroupListTonalElevationPreference(ElevationTokens.Level5) override fun put(context: Context, scope: CoroutineScope) { scope.launch { @@ -27,12 +28,12 @@ sealed class FeedsGroupListTonalElevationPreference(val value: Int) : Preference fun getDesc(context: Context): String = when (this) { - Level0 -> "Level 0 (0dp)" - Level1 -> "Level 1 (1dp)" - Level2 -> "Level 2 (3dp)" - Level3 -> "Level 3 (6dp)" - Level4 -> "Level 4 (8dp)" - Level5 -> "Level 5 (12dp)" + Level0 -> "Level 0 (${ElevationTokens.Level0}dp)" + Level1 -> "Level 1 (${ElevationTokens.Level1}dp)" + Level2 -> "Level 2 (${ElevationTokens.Level2}dp)" + Level3 -> "Level 3 (${ElevationTokens.Level3}dp)" + Level4 -> "Level 4 (${ElevationTokens.Level4}dp)" + Level5 -> "Level 5 (${ElevationTokens.Level5}dp)" } companion object { @@ -41,12 +42,12 @@ sealed class FeedsGroupListTonalElevationPreference(val value: Int) : Preference fun fromPreferences(preferences: Preferences) = when (preferences[DataStoreKeys.FeedsGroupListTonalElevation.key]) { - 0 -> Level0 - 1 -> Level1 - 3 -> Level2 - 6 -> Level3 - 8 -> Level4 - 12 -> Level5 + ElevationTokens.Level0 -> Level0 + ElevationTokens.Level1 -> Level1 + ElevationTokens.Level2 -> Level2 + ElevationTokens.Level3 -> Level3 + ElevationTokens.Level4 -> Level4 + ElevationTokens.Level5 -> Level5 else -> default } } diff --git a/app/src/main/java/me/ash/reader/data/preference/FeedsTopBarTonalElevationPreference.kt b/app/src/main/java/me/ash/reader/data/preference/FeedsTopBarTonalElevationPreference.kt index b541b4c..9769340 100644 --- a/app/src/main/java/me/ash/reader/data/preference/FeedsTopBarTonalElevationPreference.kt +++ b/app/src/main/java/me/ash/reader/data/preference/FeedsTopBarTonalElevationPreference.kt @@ -4,17 +4,18 @@ import android.content.Context import androidx.datastore.preferences.core.Preferences import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch +import me.ash.reader.data.constant.ElevationTokens import me.ash.reader.ui.ext.DataStoreKeys import me.ash.reader.ui.ext.dataStore import me.ash.reader.ui.ext.put sealed class FeedsTopBarTonalElevationPreference(val value: Int) : Preference() { - object Level0 : FeedsTopBarTonalElevationPreference(0) - object Level1 : FeedsTopBarTonalElevationPreference(1) - object Level2 : FeedsTopBarTonalElevationPreference(3) - object Level3 : FeedsTopBarTonalElevationPreference(6) - object Level4 : FeedsTopBarTonalElevationPreference(8) - object Level5 : FeedsTopBarTonalElevationPreference(12) + object Level0 : FeedsTopBarTonalElevationPreference(ElevationTokens.Level0) + object Level1 : FeedsTopBarTonalElevationPreference(ElevationTokens.Level1) + object Level2 : FeedsTopBarTonalElevationPreference(ElevationTokens.Level2) + object Level3 : FeedsTopBarTonalElevationPreference(ElevationTokens.Level3) + object Level4 : FeedsTopBarTonalElevationPreference(ElevationTokens.Level4) + object Level5 : FeedsTopBarTonalElevationPreference(ElevationTokens.Level5) override fun put(context: Context, scope: CoroutineScope) { scope.launch { @@ -27,12 +28,12 @@ sealed class FeedsTopBarTonalElevationPreference(val value: Int) : Preference() fun getDesc(context: Context): String = when (this) { - Level0 -> "Level 0 (0dp)" - Level1 -> "Level 1 (1dp)" - Level2 -> "Level 2 (3dp)" - Level3 -> "Level 3 (6dp)" - Level4 -> "Level 4 (8dp)" - Level5 -> "Level 5 (12dp)" + Level0 -> "Level 0 (${ElevationTokens.Level0}dp)" + Level1 -> "Level 1 (${ElevationTokens.Level1}dp)" + Level2 -> "Level 2 (${ElevationTokens.Level2}dp)" + Level3 -> "Level 3 (${ElevationTokens.Level3}dp)" + Level4 -> "Level 4 (${ElevationTokens.Level4}dp)" + Level5 -> "Level 5 (${ElevationTokens.Level5}dp)" } companion object { @@ -41,12 +42,12 @@ sealed class FeedsTopBarTonalElevationPreference(val value: Int) : Preference() fun fromPreferences(preferences: Preferences) = when (preferences[DataStoreKeys.FeedsTopBarTonalElevation.key]) { - 0 -> Level0 - 1 -> Level1 - 3 -> Level2 - 6 -> Level3 - 8 -> Level4 - 12 -> Level5 + ElevationTokens.Level0 -> Level0 + ElevationTokens.Level1 -> Level1 + ElevationTokens.Level2 -> Level2 + ElevationTokens.Level3 -> Level3 + ElevationTokens.Level4 -> Level4 + ElevationTokens.Level5 -> Level5 else -> default } } diff --git a/app/src/main/java/me/ash/reader/data/preference/FlowArticleListTonalElevationPreference.kt b/app/src/main/java/me/ash/reader/data/preference/FlowArticleListTonalElevationPreference.kt index 2d3dd1b..3ff0592 100644 --- a/app/src/main/java/me/ash/reader/data/preference/FlowArticleListTonalElevationPreference.kt +++ b/app/src/main/java/me/ash/reader/data/preference/FlowArticleListTonalElevationPreference.kt @@ -4,17 +4,18 @@ import android.content.Context import androidx.datastore.preferences.core.Preferences import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch +import me.ash.reader.data.constant.ElevationTokens import me.ash.reader.ui.ext.DataStoreKeys import me.ash.reader.ui.ext.dataStore import me.ash.reader.ui.ext.put sealed class FlowArticleListTonalElevationPreference(val value: Int) : Preference() { - object Level0 : FlowArticleListTonalElevationPreference(0) - object Level1 : FlowArticleListTonalElevationPreference(1) - object Level2 : FlowArticleListTonalElevationPreference(3) - object Level3 : FlowArticleListTonalElevationPreference(6) - object Level4 : FlowArticleListTonalElevationPreference(8) - object Level5 : FlowArticleListTonalElevationPreference(12) + object Level0 : FlowArticleListTonalElevationPreference(ElevationTokens.Level0) + object Level1 : FlowArticleListTonalElevationPreference(ElevationTokens.Level1) + object Level2 : FlowArticleListTonalElevationPreference(ElevationTokens.Level2) + object Level3 : FlowArticleListTonalElevationPreference(ElevationTokens.Level3) + object Level4 : FlowArticleListTonalElevationPreference(ElevationTokens.Level4) + object Level5 : FlowArticleListTonalElevationPreference(ElevationTokens.Level5) override fun put(context: Context, scope: CoroutineScope) { scope.launch { @@ -27,12 +28,12 @@ sealed class FlowArticleListTonalElevationPreference(val value: Int) : Preferenc fun getDesc(context: Context): String = when (this) { - Level0 -> "Level 0 (0dp)" - Level1 -> "Level 1 (1dp)" - Level2 -> "Level 2 (3dp)" - Level3 -> "Level 3 (6dp)" - Level4 -> "Level 4 (8dp)" - Level5 -> "Level 5 (12dp)" + Level0 -> "Level 0 (${ElevationTokens.Level0}dp)" + Level1 -> "Level 1 (${ElevationTokens.Level1}dp)" + Level2 -> "Level 2 (${ElevationTokens.Level2}dp)" + Level3 -> "Level 3 (${ElevationTokens.Level3}dp)" + Level4 -> "Level 4 (${ElevationTokens.Level4}dp)" + Level5 -> "Level 5 (${ElevationTokens.Level5}dp)" } companion object { @@ -41,12 +42,12 @@ sealed class FlowArticleListTonalElevationPreference(val value: Int) : Preferenc fun fromPreferences(preferences: Preferences) = when (preferences[DataStoreKeys.FlowArticleListTonalElevation.key]) { - 0 -> Level0 - 1 -> Level1 - 3 -> Level2 - 6 -> Level3 - 8 -> Level4 - 12 -> Level5 + ElevationTokens.Level0 -> Level0 + ElevationTokens.Level1 -> Level1 + ElevationTokens.Level2 -> Level2 + ElevationTokens.Level3 -> Level3 + ElevationTokens.Level4 -> Level4 + ElevationTokens.Level5 -> Level5 else -> default } } diff --git a/app/src/main/java/me/ash/reader/data/preference/FlowFilterBarTonalElevationPreference.kt b/app/src/main/java/me/ash/reader/data/preference/FlowFilterBarTonalElevationPreference.kt index 067d58c..45fcc5c 100644 --- a/app/src/main/java/me/ash/reader/data/preference/FlowFilterBarTonalElevationPreference.kt +++ b/app/src/main/java/me/ash/reader/data/preference/FlowFilterBarTonalElevationPreference.kt @@ -4,17 +4,18 @@ import android.content.Context import androidx.datastore.preferences.core.Preferences import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch +import me.ash.reader.data.constant.ElevationTokens import me.ash.reader.ui.ext.DataStoreKeys import me.ash.reader.ui.ext.dataStore import me.ash.reader.ui.ext.put sealed class FlowFilterBarTonalElevationPreference(val value: Int) : Preference() { - object Level0 : FlowFilterBarTonalElevationPreference(0) - object Level1 : FlowFilterBarTonalElevationPreference(1) - object Level2 : FlowFilterBarTonalElevationPreference(3) - object Level3 : FlowFilterBarTonalElevationPreference(6) - object Level4 : FlowFilterBarTonalElevationPreference(8) - object Level5 : FlowFilterBarTonalElevationPreference(12) + object Level0 : FlowFilterBarTonalElevationPreference(ElevationTokens.Level0) + object Level1 : FlowFilterBarTonalElevationPreference(ElevationTokens.Level1) + object Level2 : FlowFilterBarTonalElevationPreference(ElevationTokens.Level2) + object Level3 : FlowFilterBarTonalElevationPreference(ElevationTokens.Level3) + object Level4 : FlowFilterBarTonalElevationPreference(ElevationTokens.Level4) + object Level5 : FlowFilterBarTonalElevationPreference(ElevationTokens.Level5) override fun put(context: Context, scope: CoroutineScope) { scope.launch { @@ -27,12 +28,12 @@ sealed class FlowFilterBarTonalElevationPreference(val value: Int) : Preference( fun getDesc(context: Context): String = when (this) { - Level0 -> "Level 0 (0dp)" - Level1 -> "Level 1 (1dp)" - Level2 -> "Level 2 (3dp)" - Level3 -> "Level 3 (6dp)" - Level4 -> "Level 4 (8dp)" - Level5 -> "Level 5 (12dp)" + Level0 -> "Level 0 (${ElevationTokens.Level0}dp)" + Level1 -> "Level 1 (${ElevationTokens.Level1}dp)" + Level2 -> "Level 2 (${ElevationTokens.Level2}dp)" + Level3 -> "Level 3 (${ElevationTokens.Level3}dp)" + Level4 -> "Level 4 (${ElevationTokens.Level4}dp)" + Level5 -> "Level 5 (${ElevationTokens.Level5}dp)" } companion object { @@ -41,12 +42,12 @@ sealed class FlowFilterBarTonalElevationPreference(val value: Int) : Preference( fun fromPreferences(preferences: Preferences) = when (preferences[DataStoreKeys.FlowFilterBarTonalElevation.key]) { - 0 -> Level0 - 1 -> Level1 - 3 -> Level2 - 6 -> Level3 - 8 -> Level4 - 12 -> Level5 + ElevationTokens.Level0 -> Level0 + ElevationTokens.Level1 -> Level1 + ElevationTokens.Level2 -> Level2 + ElevationTokens.Level3 -> Level3 + ElevationTokens.Level4 -> Level4 + ElevationTokens.Level5 -> Level5 else -> default } } diff --git a/app/src/main/java/me/ash/reader/data/preference/FlowTopBarTonalElevationPreference.kt b/app/src/main/java/me/ash/reader/data/preference/FlowTopBarTonalElevationPreference.kt index 90938dc..20ad859 100644 --- a/app/src/main/java/me/ash/reader/data/preference/FlowTopBarTonalElevationPreference.kt +++ b/app/src/main/java/me/ash/reader/data/preference/FlowTopBarTonalElevationPreference.kt @@ -4,17 +4,18 @@ import android.content.Context import androidx.datastore.preferences.core.Preferences import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch +import me.ash.reader.data.constant.ElevationTokens import me.ash.reader.ui.ext.DataStoreKeys import me.ash.reader.ui.ext.dataStore import me.ash.reader.ui.ext.put sealed class FlowTopBarTonalElevationPreference(val value: Int) : Preference() { - object Level0 : FlowTopBarTonalElevationPreference(0) - object Level1 : FlowTopBarTonalElevationPreference(1) - object Level2 : FlowTopBarTonalElevationPreference(3) - object Level3 : FlowTopBarTonalElevationPreference(6) - object Level4 : FlowTopBarTonalElevationPreference(8) - object Level5 : FlowTopBarTonalElevationPreference(12) + object Level0 : FlowTopBarTonalElevationPreference(ElevationTokens.Level0) + object Level1 : FlowTopBarTonalElevationPreference(ElevationTokens.Level1) + object Level2 : FlowTopBarTonalElevationPreference(ElevationTokens.Level2) + object Level3 : FlowTopBarTonalElevationPreference(ElevationTokens.Level3) + object Level4 : FlowTopBarTonalElevationPreference(ElevationTokens.Level4) + object Level5 : FlowTopBarTonalElevationPreference(ElevationTokens.Level5) override fun put(context: Context, scope: CoroutineScope) { scope.launch { @@ -27,12 +28,12 @@ sealed class FlowTopBarTonalElevationPreference(val value: Int) : Preference() { fun getDesc(context: Context): String = when (this) { - Level0 -> "Level 0 (0dp)" - Level1 -> "Level 1 (1dp)" - Level2 -> "Level 2 (3dp)" - Level3 -> "Level 3 (6dp)" - Level4 -> "Level 4 (8dp)" - Level5 -> "Level 5 (12dp)" + Level0 -> "Level 0 (${ElevationTokens.Level0}dp)" + Level1 -> "Level 1 (${ElevationTokens.Level1}dp)" + Level2 -> "Level 2 (${ElevationTokens.Level2}dp)" + Level3 -> "Level 3 (${ElevationTokens.Level3}dp)" + Level4 -> "Level 4 (${ElevationTokens.Level4}dp)" + Level5 -> "Level 5 (${ElevationTokens.Level5}dp)" } companion object { @@ -41,12 +42,12 @@ sealed class FlowTopBarTonalElevationPreference(val value: Int) : Preference() { fun fromPreferences(preferences: Preferences) = when (preferences[DataStoreKeys.FlowTopBarTonalElevation.key]) { - 0 -> Level0 - 1 -> Level1 - 3 -> Level2 - 6 -> Level3 - 8 -> Level4 - 12 -> Level5 + ElevationTokens.Level0 -> Level0 + ElevationTokens.Level1 -> Level1 + ElevationTokens.Level2 -> Level2 + ElevationTokens.Level3 -> Level3 + ElevationTokens.Level4 -> Level4 + ElevationTokens.Level5 -> Level5 else -> default } } diff --git a/app/src/main/java/me/ash/reader/data/repository/OpmlRepository.kt b/app/src/main/java/me/ash/reader/data/repository/OpmlRepository.kt index 7af9c0e..774be76 100644 --- a/app/src/main/java/me/ash/reader/data/repository/OpmlRepository.kt +++ b/app/src/main/java/me/ash/reader/data/repository/OpmlRepository.kt @@ -54,7 +54,7 @@ class OpmlRepository @Inject constructor( Opml( "2.0", Head( - accountDao.queryById(context.currentAccountId).name, + accountDao.queryById(context.currentAccountId)?.name, Date().toString(), null, null, null, null, null, null, null, null, null, null, null, diff --git a/app/src/main/java/me/ash/reader/data/repository/RssHelper.kt b/app/src/main/java/me/ash/reader/data/repository/RssHelper.kt index 584fa48..25528d0 100644 --- a/app/src/main/java/me/ash/reader/data/repository/RssHelper.kt +++ b/app/src/main/java/me/ash/reader/data/repository/RssHelper.kt @@ -21,8 +21,6 @@ import net.dankito.readability4j.extended.Readability4JExtended import okhttp3.OkHttpClient import okhttp3.Request import java.net.URL -import java.text.ParsePosition -import java.text.SimpleDateFormat import java.util.* import javax.inject.Inject @@ -86,7 +84,12 @@ class RssHelper @Inject constructor( return withContext(dispatcherIO) { val a = mutableListOf
() val accountId = context.currentAccountId - val parseRss: SyndFeed = SyndFeedInput().build(XmlReader(URL(feed.url))) + val parseRss: SyndFeed = SyndFeedInput().build( + XmlReader(URL(feed.url).openConnection().apply { + connectTimeout = 5000 + readTimeout = 5000 + }) + ) parseRss.entries.forEach { if (latestLink != null && latestLink == it.link) return@withContext a val desc = it.description?.value @@ -111,13 +114,13 @@ class RssHelper @Inject constructor( date = it.publishedDate ?: it.updatedDate ?: Date(), title = Html.fromHtml(it.title.toString()).toString(), author = it.author, - rawDescription = (desc ?: content) ?: "", + rawDescription = (content ?: desc) ?: "", shortDescription = (Readability4JExtended("", desc ?: content ?: "") .parse().textContent ?: "") .take(100) .trim(), fullContent = content, - img = findImg((desc ?: content) ?: ""), + img = findImg((content ?: desc) ?: ""), link = it.link ?: "", ) ) diff --git a/app/src/main/java/me/ash/reader/data/repository/StringsRepository.kt b/app/src/main/java/me/ash/reader/data/repository/StringsRepository.kt index fa8d76b..5497c77 100644 --- a/app/src/main/java/me/ash/reader/data/repository/StringsRepository.kt +++ b/app/src/main/java/me/ash/reader/data/repository/StringsRepository.kt @@ -11,6 +11,13 @@ class StringsRepository @Inject constructor( private val context: Context, ) { fun getString(resId: Int, vararg formatArgs: Any) = context.getString(resId, *formatArgs) - fun getQuantityString(resId: Int, quantity: Int, vararg formatArgs: Any) = context.resources.getQuantityString(resId, quantity, *formatArgs) - fun formatAsString(date: Date?) = date?.formatAsString(context) + + fun getQuantityString(resId: Int, quantity: Int, vararg formatArgs: Any) = + context.resources.getQuantityString(resId, quantity, *formatArgs) + + fun formatAsString( + date: Date?, + onlyHourMinute: Boolean? = false, + atHourMinute: Boolean? = false + ) = date?.formatAsString(context, onlyHourMinute, atHourMinute) } diff --git a/app/src/main/java/me/ash/reader/ui/component/base/RYAsyncImage.kt b/app/src/main/java/me/ash/reader/ui/component/base/RYAsyncImage.kt index bd86c62..cfd4959 100644 --- a/app/src/main/java/me/ash/reader/ui/component/base/RYAsyncImage.kt +++ b/app/src/main/java/me/ash/reader/ui/component/base/RYAsyncImage.kt @@ -16,7 +16,7 @@ import coil.size.Scale import coil.size.Size import me.ash.reader.R -val Size_1000 = Size(1000, 1000) +val SIZE_1000 = Size(1000, 1000) @Composable fun RYAsyncImage( diff --git a/app/src/main/java/me/ash/reader/ui/page/home/HomeViewModel.kt b/app/src/main/java/me/ash/reader/ui/page/home/HomeViewModel.kt index a250c56..56bebff 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/HomeViewModel.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/HomeViewModel.kt @@ -50,7 +50,12 @@ class HomeViewModel @Inject constructor( fun fetchArticles() { _homeUiState.update { it.copy( - pagingData = Pager(PagingConfig(pageSize = 50)) { + pagingData = Pager( + config = PagingConfig( + pageSize = 100, + enablePlaceholders = false, + ) + ) { if (_homeUiState.value.searchContent.isNotBlank()) { rssRepository.get().searchArticles( content = _homeUiState.value.searchContent.trim(), @@ -68,7 +73,14 @@ class HomeViewModel @Inject constructor( ) } }.flow.map { - it.map { FlowItemView.Article(it) }.insertSeparators { before, after -> + it.map { + FlowItemView.Article(it.apply { + article.dateString = stringsRepository.formatAsString( + date = article.date, + onlyHourMinute = true + ) + }) + }.insertSeparators { before, after -> val beforeDate = stringsRepository.formatAsString(before?.articleWithFeed?.article?.date) val afterDate = 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 df4e143..a891ff4 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,16 +4,13 @@ import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.CoroutineDispatcher 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 import me.ash.reader.data.entity.Group -import me.ash.reader.data.module.DispatcherIO import me.ash.reader.data.repository.OpmlRepository import me.ash.reader.data.repository.RssHelper import me.ash.reader.data.repository.RssRepository @@ -28,8 +25,6 @@ class SubscribeViewModel @Inject constructor( private val rssRepository: RssRepository, private val rssHelper: RssHelper, private val stringsRepository: StringsRepository, - @DispatcherIO - private val dispatcherIO: CoroutineDispatcher, ) : ViewModel() { private val _subscribeUiState = MutableStateFlow(SubscribeUiState()) val subscribeUiState: StateFlow = _subscribeUiState.asStateFlow() @@ -55,7 +50,7 @@ class SubscribeViewModel @Inject constructor( } fun importFromInputStream(inputStream: InputStream) { - viewModelScope.launch(dispatcherIO) { + viewModelScope.launch { try { opmlRepository.saveToDatabase(inputStream) rssRepository.get().doSync() @@ -68,13 +63,10 @@ class SubscribeViewModel @Inject constructor( fun subscribe() { val feed = _subscribeUiState.value.feed ?: return val articles = _subscribeUiState.value.articles - viewModelScope.launch(dispatcherIO) { - val groupId = async { - _subscribeUiState.value.selectedGroupId - } + viewModelScope.launch { rssRepository.get().subscribe( feed.copy( - groupId = groupId.await(), + groupId = _subscribeUiState.value.selectedGroupId, isNotification = _subscribeUiState.value.allowNotificationPreset, isFullContent = _subscribeUiState.value.parseFullContentPreset, ), articles @@ -123,7 +115,7 @@ class SubscribeViewModel @Inject constructor( fun search() { searchJob?.cancel() - viewModelScope.launch(dispatcherIO) { + viewModelScope.launch { try { _subscribeUiState.update { it.copy( diff --git a/app/src/main/java/me/ash/reader/ui/page/home/flow/ArticleItem.kt b/app/src/main/java/me/ash/reader/ui/page/home/flow/ArticleItem.kt index 819b0ca..d741d1c 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/flow/ArticleItem.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/flow/ArticleItem.kt @@ -2,7 +2,6 @@ package me.ash.reader.ui.page.home.flow import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* -import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.Star import androidx.compose.material3.Icon @@ -23,10 +22,10 @@ import coil.size.Scale import me.ash.reader.R import me.ash.reader.data.entity.ArticleWithFeed import me.ash.reader.data.preference.* -import me.ash.reader.ui.component.base.RYAsyncImage -import me.ash.reader.ui.ext.formatAsString import me.ash.reader.ui.component.FeedIcon -import me.ash.reader.ui.component.base.Size_1000 +import me.ash.reader.ui.component.base.RYAsyncImage +import me.ash.reader.ui.component.base.SIZE_1000 +import me.ash.reader.ui.theme.SHAPE_20 @Composable fun ArticleItem( @@ -43,7 +42,7 @@ fun ArticleItem( Column( modifier = Modifier .padding(horizontal = 12.dp) - .clip(RoundedCornerShape(20.dp)) + .clip(SHAPE_20) .clickable { onClick(articleWithFeed) } .padding(horizontal = 12.dp, vertical = 12.dp) .alpha(if (articleWithFeed.article.isStarred || articleWithFeed.article.isUnread) 1f else 0.5f), @@ -80,21 +79,20 @@ fun ArticleItem( if (articleWithFeed.article.isStarred) { Icon( modifier = Modifier + .alpha(0.7f) .size(14.dp) .padding(end = 2.dp), imageVector = Icons.Rounded.Star, contentDescription = stringResource(R.string.starred), - tint = MaterialTheme.colorScheme.outline.copy(alpha = 0.7f), + tint = MaterialTheme.colorScheme.outline, ) } // Date Text( - text = articleWithFeed.article.date.formatAsString( - context, - onlyHourMinute = true - ), - color = MaterialTheme.colorScheme.outline.copy(alpha = 0.7f), + modifier = Modifier.alpha(0.7f), + text = articleWithFeed.article.dateString ?: "", + color = MaterialTheme.colorScheme.outline, style = MaterialTheme.typography.labelMedium, ) } @@ -128,8 +126,9 @@ fun ArticleItem( // Description if (articleListDesc.value && articleWithFeed.article.shortDescription.isNotBlank()) { Text( + modifier = Modifier.alpha(0.7f), text = articleWithFeed.article.shortDescription, - color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.7f), + color = MaterialTheme.colorScheme.onSurfaceVariant, style = MaterialTheme.typography.bodySmall, maxLines = 2, overflow = TextOverflow.Ellipsis, @@ -143,11 +142,11 @@ fun ArticleItem( modifier = Modifier .padding(start = 10.dp) .size(80.dp) - .clip(RoundedCornerShape(20.dp)), + .clip(SHAPE_20), data = articleWithFeed.article.img, scale = Scale.FILL, precision = Precision.INEXACT, - size = Size_1000, + size = SIZE_1000, contentScale = ContentScale.Crop, ) } diff --git a/app/src/main/java/me/ash/reader/ui/page/home/flow/ArticleList.kt b/app/src/main/java/me/ash/reader/ui/page/home/flow/ArticleList.kt index c0aaf27..6483ede 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/flow/ArticleList.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/flow/ArticleList.kt @@ -13,8 +13,8 @@ import me.ash.reader.data.entity.ArticleWithFeed @OptIn(ExperimentalFoundationApi::class) fun LazyListScope.ArticleList( pagingItems: LazyPagingItems, - articleListFeedIcon: Boolean, - articleListDateStickyHeader: Boolean, + isShowFeedIcon: Boolean, + isShowStickyHeader: Boolean, articleListTonalElevation: Int, onClick: (ArticleWithFeed) -> Unit = {}, ) { @@ -31,13 +31,13 @@ fun LazyListScope.ArticleList( } is FlowItemView.Date -> { if (item.showSpacer) item { Spacer(modifier = Modifier.height(40.dp)) } - if (articleListDateStickyHeader) { + if (isShowStickyHeader) { stickyHeader(key = item.date) { - StickyHeader(item.date, articleListFeedIcon, articleListTonalElevation) + StickyHeader(item.date, isShowFeedIcon, articleListTonalElevation) } } else { item(key = item.date) { - StickyHeader(item.date, articleListFeedIcon, articleListTonalElevation) + StickyHeader(item.date, isShowFeedIcon, articleListTonalElevation) } } } diff --git a/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowPage.kt b/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowPage.kt index 2b243ea..231dbed 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowPage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowPage.kt @@ -33,7 +33,6 @@ import me.ash.reader.ui.component.base.RYScaffold 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.HomeViewModel @OptIn( @@ -165,7 +164,15 @@ fun FlowPage( state = listState, ) { item { - DisplayTextHeader(filterUiState, isSyncing, articleListFeedIcon.value) + DisplayText( + modifier = Modifier.padding(start = if (articleListFeedIcon.value) 30.dp else 0.dp), + text = when { + filterUiState.group != null -> filterUiState.group.name + filterUiState.feed != null -> filterUiState.feed.name + else -> filterUiState.filter.getName() + }, + desc = if (isSyncing) stringResource(R.string.syncing) else "", + ) RYExtensibleVisibility(visible = markAsRead) { Spacer(modifier = Modifier.height((56 + 24 + 10).dp)) } @@ -217,8 +224,8 @@ fun FlowPage( } ArticleList( pagingItems = pagingItems, - articleListFeedIcon = articleListFeedIcon.value, - articleListDateStickyHeader = articleListDateStickyHeader.value, + isShowFeedIcon = articleListFeedIcon.value, + isShowStickyHeader = articleListDateStickyHeader.value, articleListTonalElevation = articleListTonalElevation.value, ) { onSearch = false @@ -248,20 +255,3 @@ fun FlowPage( } ) } - -@Composable -private fun DisplayTextHeader( - filterState: FilterState, - isSyncing: Boolean, - articleListFeedIcon: Boolean, -) { - DisplayText( - modifier = Modifier.padding(start = if (articleListFeedIcon) 30.dp else 0.dp), - text = when { - filterState.group != null -> filterState.group.name - filterState.feed != null -> filterState.feed.name - else -> filterState.filter.getName() - }, - desc = if (isSyncing) stringResource(R.string.syncing) else "", - ) -} diff --git a/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowViewModel.kt b/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowViewModel.kt index 4144f9d..392045f 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowViewModel.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/flow/FlowViewModel.kt @@ -11,7 +11,6 @@ import kotlinx.coroutines.launch import me.ash.reader.data.entity.ArticleWithFeed import me.ash.reader.data.repository.RssRepository import java.util.* -import javax.annotation.concurrent.Immutable import javax.inject.Inject @HiltViewModel @@ -77,10 +76,7 @@ enum class MarkAsReadBefore { All, } -@Immutable sealed class FlowItemView { - @Immutable class Article(val articleWithFeed: ArticleWithFeed) : FlowItemView() - @Immutable class Date(val date: String, val showSpacer: Boolean) : FlowItemView() } \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/ui/page/home/flow/SearchBar.kt b/app/src/main/java/me/ash/reader/ui/page/home/flow/SearchBar.kt index 9c6e0b6..9d07147 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/flow/SearchBar.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/flow/SearchBar.kt @@ -12,19 +12,19 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction -import androidx.compose.ui.text.style.BaselineShift import androidx.compose.ui.unit.dp import me.ash.reader.R +import me.ash.reader.data.constant.ElevationTokens @Composable fun SearchBar( - modifier: Modifier = Modifier, value: String, placeholder: String = "", focusRequester: FocusRequester = remember { FocusRequester() }, @@ -39,7 +39,7 @@ fun SearchBar( .padding(horizontal = 24.dp) .fillMaxWidth(), shape = CircleShape, - tonalElevation = 3.dp + tonalElevation = ElevationTokens.Level2.dp ) { Row( modifier = Modifier.fillMaxSize(), @@ -62,6 +62,7 @@ fun SearchBar( .fillMaxWidth() .focusRequester(focusRequester), colors = TextFieldDefaults.textFieldColors( + textColor = MaterialTheme.colorScheme.onSurfaceVariant, containerColor = Color.Transparent, focusedIndicatorColor = Color.Transparent, unfocusedIndicatorColor = Color.Transparent, @@ -70,17 +71,13 @@ fun SearchBar( onValueChange = { onValueChange(it) }, placeholder = { Text( + modifier = Modifier.alpha(0.7f), text = placeholder, style = MaterialTheme.typography.bodyLarge, - color = MaterialTheme.colorScheme.onSurfaceVariant.copy( - alpha = 0.7f - ), + color = MaterialTheme.colorScheme.onSurfaceVariant, ) }, - textStyle = MaterialTheme.typography.bodyLarge.copy( - color = MaterialTheme.colorScheme.onSurfaceVariant, - baselineShift = BaselineShift(0.1f) - ), + textStyle = MaterialTheme.typography.bodyLarge, singleLine = true, keyboardOptions = KeyboardOptions( imeAction = ImeAction.Done diff --git a/app/src/main/java/me/ash/reader/ui/page/home/flow/StickyHeader.kt b/app/src/main/java/me/ash/reader/ui/page/home/flow/StickyHeader.kt index dcda056..3f8518b 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/flow/StickyHeader.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/flow/StickyHeader.kt @@ -15,8 +15,8 @@ import me.ash.reader.ui.theme.palette.onDark @Composable fun StickyHeader( - currentItemDay: String, - articleListFeedIcon: Boolean, + dateString: String, + isShowFeedIcon: Boolean, articleListTonalElevation: Int, ) { Row( @@ -30,10 +30,10 @@ fun StickyHeader( ) { Text( modifier = Modifier.padding( - start = if (articleListFeedIcon) 54.dp else 24.dp, + start = if (isShowFeedIcon) 54.dp else 24.dp, bottom = 4.dp ), - text = currentItemDay, + text = dateString, color = MaterialTheme.colorScheme.primary, style = MaterialTheme.typography.labelLarge, ) diff --git a/app/src/main/java/me/ash/reader/ui/page/home/reading/ReadingViewModel.kt b/app/src/main/java/me/ash/reader/ui/page/home/reading/ReadingViewModel.kt index e2ca168..8095268 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/reading/ReadingViewModel.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/reading/ReadingViewModel.kt @@ -18,7 +18,7 @@ import javax.inject.Inject @HiltViewModel class ReadingViewModel @Inject constructor( - val rssRepository: RssRepository, + private val rssRepository: RssRepository, private val rssHelper: RssHelper, ) : ViewModel() { private val _readingUiState = MutableStateFlow(ReadingUiState()) diff --git a/app/src/main/java/me/ash/reader/ui/theme/Shapes.kt b/app/src/main/java/me/ash/reader/ui/theme/Shapes.kt new file mode 100644 index 0000000..670cab2 --- /dev/null +++ b/app/src/main/java/me/ash/reader/ui/theme/Shapes.kt @@ -0,0 +1,15 @@ +package me.ash.reader.ui.theme + +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Shapes +import androidx.compose.ui.unit.dp + +val Shapes = Shapes( + extraSmall = RoundedCornerShape(4.0.dp), + small = RoundedCornerShape(8.0.dp), + medium = RoundedCornerShape(12.0.dp), + large = RoundedCornerShape(16.0.dp), + extraLarge = RoundedCornerShape(28.0.dp) +) + +val SHAPE_20 = RoundedCornerShape(20.0.dp) \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/ui/theme/Theme.kt b/app/src/main/java/me/ash/reader/ui/theme/Theme.kt index f09a07d..0696c1a 100644 --- a/app/src/main/java/me/ash/reader/ui/theme/Theme.kt +++ b/app/src/main/java/me/ash/reader/ui/theme/Theme.kt @@ -40,7 +40,8 @@ fun AppTheme( if (useDarkTheme) dynamicDarkColorScheme() else dynamicLightColorScheme(), typography = AppTypography, - content = content + shapes = Shapes, + content = content, ) } } diff --git a/app/src/main/res/drawable/ic_broken_image_black_24dp.xml b/app/src/main/res/drawable/ic_broken_image_black_24dp.xml index 6872d2e..aa7e72d 100644 --- a/app/src/main/res/drawable/ic_broken_image_black_24dp.xml +++ b/app/src/main/res/drawable/ic_broken_image_black_24dp.xml @@ -5,5 +5,6 @@ android:viewportHeight="24"> + android:fillColor="#000000" + android:fillAlpha="0.3"/> diff --git a/app/src/main/res/drawable/ic_hourglass_empty_black_24dp.xml b/app/src/main/res/drawable/ic_hourglass_empty_black_24dp.xml index ca58686..5f4bd70 100644 --- a/app/src/main/res/drawable/ic_hourglass_empty_black_24dp.xml +++ b/app/src/main/res/drawable/ic_hourglass_empty_black_24dp.xml @@ -5,5 +5,6 @@ android:viewportHeight="24"> + android:fillColor="#000000" + android:fillAlpha="0.3"/> From 58671867515c10793d71f55f6ac5d45a9c6a9131 Mon Sep 17 00:00:00 2001 From: Ash Date: Wed, 25 May 2022 13:40:52 +0800 Subject: [PATCH 17/18] Optimize the content styles of articles --- .../ui/component/reader/HtmlToComposable.kt | 25 ++++++++++++------- .../ash/reader/ui/component/reader/Styles.kt | 8 ++++-- .../ui/component/reader/TextComposer.kt | 2 +- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/me/ash/reader/ui/component/reader/HtmlToComposable.kt b/app/src/main/java/me/ash/reader/ui/component/reader/HtmlToComposable.kt index e5262ed..1d98501 100644 --- a/app/src/main/java/me/ash/reader/ui/component/reader/HtmlToComposable.kt +++ b/app/src/main/java/me/ash/reader/ui/component/reader/HtmlToComposable.kt @@ -238,7 +238,7 @@ private fun TextComposer.appendTextChildren( withComposableStyle( style = { h5Style().toSpanStyle() } ) { - append(element.text()) + append("\n${element.text()}") } } } @@ -247,7 +247,7 @@ private fun TextComposer.appendTextChildren( withComposableStyle( style = { h5Style().toSpanStyle() } ) { - append(element.text()) + append("\n${element.text()}") } } } @@ -256,7 +256,7 @@ private fun TextComposer.appendTextChildren( withComposableStyle( style = { h5Style().toSpanStyle() } ) { - append(element.text()) + append("\n${element.text()}") } } } @@ -265,7 +265,7 @@ private fun TextComposer.appendTextChildren( withComposableStyle( style = { h5Style().toSpanStyle() } ) { - append(element.text()) + append("\n${element.text()}") } } } @@ -274,7 +274,7 @@ private fun TextComposer.appendTextChildren( withComposableStyle( style = { h5Style().toSpanStyle() } ) { - append(element.text()) + append("\n${element.text()}") } } } @@ -283,7 +283,7 @@ private fun TextComposer.appendTextChildren( withComposableStyle( style = { h5Style().toSpanStyle() } ) { - append(element.text()) + append("\n${element.text()}") } } } @@ -442,6 +442,7 @@ private fun TextComposer.appendTextChildren( // .padding(horizontal = PADDING_HORIZONTAL.dp) .width(MAX_CONTENT_WIDTH.dp) ) { + Spacer(modifier = Modifier.height(PADDING_HORIZONTAL.dp)) DisableSelection { BoxWithConstraints( modifier = Modifier @@ -466,7 +467,11 @@ private fun TextComposer.appendTextChildren( ) { val imageSize = maxImageSize() RYAsyncImage( - modifier = Modifier.fillMaxWidth(), + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = PADDING_HORIZONTAL.dp) + .clip(IMAGE_SHAPE) + .clickable { }, data = imageCandidates.getBestImageForMaxSize( pixelDensity = pixelDensity(), maxSize = imageSize, @@ -593,10 +598,12 @@ private fun TextComposer.appendTextChildren( ) { RYAsyncImage( modifier = Modifier + .fillMaxWidth() + .padding(horizontal = PADDING_HORIZONTAL.dp) + .clip(IMAGE_SHAPE) .clickable { onLinkClick(video.link) - } - .fillMaxWidth(), + }, data = video.imageUrl, size = maxImageSize(), contentDescription = stringResource(R.string.touch_to_play_video), diff --git a/app/src/main/java/me/ash/reader/ui/component/reader/Styles.kt b/app/src/main/java/me/ash/reader/ui/component/reader/Styles.kt index 7ed82e9..b1fa1b1 100644 --- a/app/src/main/java/me/ash/reader/ui/component/reader/Styles.kt +++ b/app/src/main/java/me/ash/reader/ui/component/reader/Styles.kt @@ -20,6 +20,7 @@ package me.ash.reader.ui.component.reader +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.Color @@ -27,12 +28,14 @@ import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.unit.dp import me.ash.reader.ui.ext.alphaLN const val PADDING_HORIZONTAL = 24.0 const val MAX_CONTENT_WIDTH = 840.0 +val IMAGE_SHAPE = RoundedCornerShape(32.dp) @Composable fun bodyForeground(): Color = @@ -71,7 +74,7 @@ fun h4Style(): TextStyle = @Composable fun h5Style(): TextStyle = MaterialTheme.typography.headlineSmall.copy( - color = bodyForeground() + color = bodyForeground(), ) @Composable @@ -83,7 +86,8 @@ fun h6Style(): TextStyle = @Composable fun captionStyle(): TextStyle = MaterialTheme.typography.bodySmall.copy( - color = bodyForeground().copy(alpha = 0.6f) + color = bodyForeground().copy(alpha = 0.6f), + textAlign = TextAlign.Center, ) @Composable diff --git a/app/src/main/java/me/ash/reader/ui/component/reader/TextComposer.kt b/app/src/main/java/me/ash/reader/ui/component/reader/TextComposer.kt index e38d0e6..f3db0b1 100644 --- a/app/src/main/java/me/ash/reader/ui/component/reader/TextComposer.kt +++ b/app/src/main/java/me/ash/reader/ui/component/reader/TextComposer.kt @@ -79,7 +79,7 @@ class TextComposer( ) -> R ): R { val url = link ?: findClosestLink() - builder.ensureDoubleNewline() + //builder.ensureDoubleNewline() terminateCurrentText() val onClick: (() -> Unit)? = if (url?.isNotBlank() == true) { { From 433ff6e6f284559f132b2c7ef34c26d09ca6a994 Mon Sep 17 00:00:00 2001 From: Ash Date: Tue, 31 May 2022 02:21:28 +0800 Subject: [PATCH 18/18] Optimize the feeds page --- .../data/repository/AbstractRssRepository.kt | 10 +- .../me/ash/reader/ui/component/FilterBar.kt | 5 +- .../reader/ui/component/base/RYScaffold.kt | 16 +- .../java/me/ash/reader/ui/ext/ContextExt.kt | 2 +- .../ash/reader/ui/page/home/feeds/FeedItem.kt | 110 +++++++------- .../reader/ui/page/home/feeds/FeedsPage.kt | 64 +++++--- .../ui/page/home/feeds/FeedsViewModel.kt | 63 ++++---- .../reader/ui/page/home/feeds/GroupItem.kt | 53 +++---- .../feeds/drawer/feed/FeedOptionDrawer.kt | 11 +- .../page/home/feeds/subscribe/ResultView.kt | 45 +++--- .../reader/ui/page/home/flow/ArticleItem.kt | 12 +- .../settings/color/feeds/FeedsPagePreview.kt | 143 ++++++++++++++++++ .../color/feeds/FeedsPageStylePage.kt | 98 ------------ .../settings/color/flow/FlowPagePreview.kt | 124 +++++++++++++++ .../settings/color/flow/FlowPageStylePage.kt | 102 ------------- .../java/me/ash/reader/ui/theme/Shapes.kt | 13 +- 16 files changed, 485 insertions(+), 386 deletions(-) create mode 100644 app/src/main/java/me/ash/reader/ui/page/settings/color/feeds/FeedsPagePreview.kt create mode 100644 app/src/main/java/me/ash/reader/ui/page/settings/color/flow/FlowPagePreview.kt diff --git a/app/src/main/java/me/ash/reader/data/repository/AbstractRssRepository.kt b/app/src/main/java/me/ash/reader/data/repository/AbstractRssRepository.kt index 75f42f8..bfa666d 100644 --- a/app/src/main/java/me/ash/reader/data/repository/AbstractRssRepository.kt +++ b/app/src/main/java/me/ash/reader/data/repository/AbstractRssRepository.kt @@ -10,12 +10,12 @@ import androidx.work.WorkManager import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.mapLatest import me.ash.reader.data.dao.AccountDao import me.ash.reader.data.dao.ArticleDao import me.ash.reader.data.dao.FeedDao import me.ash.reader.data.dao.GroupDao import me.ash.reader.data.entity.* -import me.ash.reader.data.model.ImportantCount import me.ash.reader.ui.ext.currentAccountId import java.util.* @@ -99,7 +99,7 @@ abstract class AbstractRssRepository constructor( fun pullImportant( isStarred: Boolean = false, isUnread: Boolean = false, - ): Flow> { + ): Flow> { val accountId = context.currentAccountId Log.i( "RLog", @@ -111,6 +111,12 @@ abstract class AbstractRssRepository constructor( isUnread -> articleDao .queryImportantCountWhenIsUnread(accountId, isUnread) else -> articleDao.queryImportantCountWhenIsAll(accountId) + }.mapLatest { + mapOf( + *(it.map { + it.feedId to it.important + }.toTypedArray()) + ) }.flowOn(dispatcherIO) } diff --git a/app/src/main/java/me/ash/reader/ui/component/FilterBar.kt b/app/src/main/java/me/ash/reader/ui/component/FilterBar.kt index 7027374..f0d29d9 100644 --- a/app/src/main/java/me/ash/reader/ui/component/FilterBar.kt +++ b/app/src/main/java/me/ash/reader/ui/component/FilterBar.kt @@ -4,6 +4,7 @@ import android.os.Build import android.view.SoundEffectConstants import androidx.compose.foundation.background import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.width import androidx.compose.material3.* import androidx.compose.runtime.Composable @@ -20,7 +21,6 @@ import me.ash.reader.ui.theme.palette.onDark @Composable fun FilterBar( - modifier: Modifier = Modifier, filter: Filter, filterBarStyle: Int, filterBarFilled: Boolean, @@ -33,7 +33,8 @@ fun FilterBar( NavigationBar( modifier = Modifier - .background(MaterialTheme.colorScheme.surfaceColorAtElevation(filterBarTonalElevation)), + .background(MaterialTheme.colorScheme.surfaceColorAtElevation(filterBarTonalElevation)) + .navigationBarsPadding(), tonalElevation = filterBarTonalElevation, ) { Spacer(modifier = Modifier.width(filterBarPadding)) diff --git a/app/src/main/java/me/ash/reader/ui/component/base/RYScaffold.kt b/app/src/main/java/me/ash/reader/ui/component/base/RYScaffold.kt index 773aed4..f707357 100644 --- a/app/src/main/java/me/ash/reader/ui/component/base/RYScaffold.kt +++ b/app/src/main/java/me/ash/reader/ui/component/base/RYScaffold.kt @@ -31,14 +31,14 @@ fun RYScaffold( color = containerColor ) ) - .statusBarsPadding() - .run { - if (bottomBar != null || floatingActionButton != null) { - navigationBarsPadding() - } else { - this - } - }, + .statusBarsPadding(), +// .run { +// if (bottomBar != null || floatingActionButton != null) { +// navigationBarsPadding() +// } else { +// this +// } +// }, containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation( containerTonalElevation, color = containerColor diff --git a/app/src/main/java/me/ash/reader/ui/ext/ContextExt.kt b/app/src/main/java/me/ash/reader/ui/ext/ContextExt.kt index 3c63125..8b084ba 100644 --- a/app/src/main/java/me/ash/reader/ui/ext/ContextExt.kt +++ b/app/src/main/java/me/ash/reader/ui/ext/ContextExt.kt @@ -67,7 +67,7 @@ fun Context.share(content: String) { }, getString(R.string.share))) } -fun Context.openURL(url: String? = null) { +fun Context.openURL(url: String?) { url?.takeIf { it.trim().isNotEmpty() } ?.let { startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(it))) } } \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedItem.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedItem.kt index 63ed5e2..b854541 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedItem.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedItem.kt @@ -1,95 +1,99 @@ package me.ash.reader.ui.page.home.feeds +import RYExtensibleVisibility import android.view.HapticFeedbackConstants +import androidx.compose.foundation.background import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Badge import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text -import androidx.compose.runtime.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.platform.LocalView import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.unit.Dp 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.FeedOptionViewModel -import kotlin.math.ln +import me.ash.reader.ui.theme.ShapeBottom32 @OptIn( androidx.compose.foundation.ExperimentalFoundationApi::class, - androidx.compose.material.ExperimentalMaterialApi::class, ) @Composable fun FeedItem( feed: Feed, + alpha: Float = 1f, + badgeAlpha: Float = 1f, + isEnded: Boolean = false, + isExpanded: () -> Boolean, feedOptionViewModel: FeedOptionViewModel = hiltViewModel(), - tonalElevation: Dp, onClick: () -> Unit = {}, ) { val view = LocalView.current val scope = rememberCoroutineScope() - val tonalElevationAlpha by remember { - derivedStateOf { - (ln(tonalElevation.value + 1.4f) + 2f) / 100f - } - } - Row( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 14.dp) - .clip(RoundedCornerShape(32.dp)) - .combinedClickable( - onClick = { - onClick() - }, - onLongClick = { - view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP) - feedOptionViewModel.showDrawer(scope, feed.id) - } - ) - .padding(vertical = 14.dp), - ) { + RYExtensibleVisibility(visible = isExpanded()) { Row( modifier = Modifier .fillMaxWidth() - .padding(start = 14.dp, end = 6.dp), - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically, - ) { - Row(modifier = Modifier.weight(1f)) { - FeedIcon(feed.name) - Text( - modifier = Modifier.padding(start = 12.dp, end = 6.dp), - text = feed.name, - style = MaterialTheme.typography.labelLarge, - color = MaterialTheme.colorScheme.onSurfaceVariant, - maxLines = 1, - overflow = TextOverflow.Ellipsis, + .padding(horizontal = 16.dp) + .background( + color = MaterialTheme.colorScheme.secondary.copy(alpha = alpha), + shape = if (isEnded) ShapeBottom32 else RectangleShape, ) - } - if ((feed.important ?: 0) != 0) { - Badge( - containerColor = MaterialTheme.colorScheme.surfaceTint.copy( - alpha = tonalElevationAlpha - ), - contentColor = MaterialTheme.colorScheme.outline, - content = { - Text( - text = feed.important.toString(), - style = MaterialTheme.typography.labelSmall - ) + .combinedClickable( + onClick = { + onClick() }, + onLongClick = { + view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP) + feedOptionViewModel.showDrawer(scope, feed.id) + } ) + .padding(horizontal = 14.dp) + .padding(top = 14.dp, bottom = if (isEnded) 22.dp else 14.dp), + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(start = 14.dp, end = 6.dp), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + ) { + Row(modifier = Modifier.weight(1f)) { + FeedIcon(feed.name) + Text( + modifier = Modifier.padding(start = 12.dp, end = 6.dp), + text = feed.name, + style = MaterialTheme.typography.labelLarge, + color = MaterialTheme.colorScheme.onSurfaceVariant, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) + } + if ((feed.important ?: 0) != 0) { + Badge( + containerColor = MaterialTheme.colorScheme.surfaceTint.copy( + alpha = badgeAlpha + ), + contentColor = MaterialTheme.colorScheme.outline, + content = { + Text( + text = feed.important.toString(), + style = MaterialTheme.typography.labelSmall + ) + }, + ) + } } } } diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedsPage.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedsPage.kt index 4ed4ee6..b144f93 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedsPage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedsPage.kt @@ -31,6 +31,7 @@ import me.ash.reader.data.preference.* import me.ash.reader.data.repository.SyncWorker.Companion.getIsSyncing import me.ash.reader.ui.component.FilterBar import me.ash.reader.ui.component.base.* +import me.ash.reader.ui.ext.alphaLN import me.ash.reader.ui.ext.collectAsStateValue import me.ash.reader.ui.ext.findActivity import me.ash.reader.ui.ext.getCurrentVersion @@ -41,9 +42,9 @@ 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.SubscribeViewModel +import kotlin.math.ln @OptIn( - com.google.accompanist.pager.ExperimentalPagerApi::class, androidx.compose.foundation.ExperimentalFoundationApi::class ) @Composable @@ -96,6 +97,24 @@ fun FeedsPage( } } + val feedBadgeAlpha by remember { derivedStateOf { (ln(groupListTonalElevation.value + 1.4f) + 2f) / 100f } } + val groupAlpha by remember { derivedStateOf { groupListTonalElevation.value.dp.alphaLN(weight = 1.2f) } } + val groupIndicatorAlpha by remember { + derivedStateOf { + groupListTonalElevation.value.dp.alphaLN( + weight = 1.4f + ) + } + } + + val groupsVisible = remember(feedsUiState.groupWithFeedList) { + mutableStateMapOf( + *(feedsUiState.groupWithFeedList.filterIsInstance().map { + it.group.id to groupListExpand.value + }.toTypedArray()) + ) + } + LaunchedEffect(Unit) { feedsViewModel.fetchAccount() } @@ -161,7 +180,7 @@ fun FeedsPage( item { Banner( title = filterUiState.filter.getName(), - desc = feedsUiState.importantCount.ifEmpty { stringResource(R.string.loading) }, + desc = feedsUiState.importantSum.ifEmpty { stringResource(R.string.loading) }, icon = filterUiState.filter.iconOutline, action = { Icon( @@ -189,14 +208,21 @@ fun FeedsPage( Spacer(modifier = Modifier.height(8.dp)) } itemsIndexed(feedsUiState.groupWithFeedList) { index, groupWithFeed -> -// Crossfade(targetState = groupWithFeed) { groupWithFeed -> - Column { - GroupItem( - isExpanded = groupListExpand.value, - tonalElevation = groupListTonalElevation.value.dp, - group = groupWithFeed.group, - feeds = groupWithFeed.feeds, - groupOnClick = { + when (groupWithFeed) { + is GroupFeedsView.Group -> { + if (index != 0) { + Spacer(modifier = Modifier.height(16.dp)) + } + GroupItem( + isExpanded = { groupsVisible[groupWithFeed.group.id] ?: false }, + group = groupWithFeed.group, + alpha = groupAlpha, + indicatorAlpha = groupIndicatorAlpha, + onExpanded = { + groupsVisible[groupWithFeed.group.id] = + !(groupsVisible[groupWithFeed.group.id] ?: false) + } + ) { filterChange( navController = navController, homeViewModel = homeViewModel, @@ -205,23 +231,27 @@ fun FeedsPage( feed = null, ) ) - }, - feedOnClick = { feed -> + } + } + is GroupFeedsView.Feed -> { + FeedItem( + feed = groupWithFeed.feed, + alpha = groupAlpha, + badgeAlpha = feedBadgeAlpha, + isEnded = index != feedsUiState.groupWithFeedList.lastIndex && feedsUiState.groupWithFeedList[index + 1] is GroupFeedsView.Group, + isExpanded = { groupsVisible[groupWithFeed.feed.groupId] ?: false }, + ) { filterChange( navController = navController, homeViewModel = homeViewModel, filterState = filterUiState.copy( group = null, - feed = feed, + feed = groupWithFeed.feed, ) ) } - ) - if (index != feedsUiState.groupWithFeedList.lastIndex) { - Spacer(modifier = Modifier.height(8.dp)) } } -// } } item { Spacer(modifier = Modifier.height(128.dp)) diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedsViewModel.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedsViewModel.kt index 6fd8243..08d4e5a 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedsViewModel.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/FeedsViewModel.kt @@ -2,6 +2,7 @@ package me.ash.reader.ui.page.home.feeds import android.util.Log import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.ui.util.fastForEach import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel @@ -10,7 +11,6 @@ import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import me.ash.reader.R import me.ash.reader.data.entity.Account -import me.ash.reader.data.entity.GroupWithFeed import me.ash.reader.data.module.DispatcherDefault import me.ash.reader.data.module.DispatcherIO import me.ash.reader.data.repository.AccountRepository @@ -67,42 +67,20 @@ class FeedsViewModel @Inject constructor( combine( rssRepository.get().pullFeeds(), rssRepository.get().pullImportant(isStarred, isUnread), - ) { groupWithFeedList, importantList -> - val groupImportantMap = mutableMapOf() - val feedImportantMap = mutableMapOf() - importantList.groupBy { it.groupId }.forEach { (i, list) -> - var groupImportantSum = 0 - list.forEach { - feedImportantMap[it.feedId] = it.important - groupImportantSum += it.important - } - groupImportantMap[i] = groupImportantSum - } - val groupsIt = groupWithFeedList.iterator() - while (groupsIt.hasNext()) { - val groupWithFeed = groupsIt.next() - val groupImportant = groupImportantMap[groupWithFeed.group.id] - if (groupImportant == null && (isStarred || isUnread)) { - groupsIt.remove() - } else { - groupWithFeed.group.important = groupImportant - val feedsIt = groupWithFeed.feeds.iterator() - while (feedsIt.hasNext()) { - val feed = feedsIt.next() - val feedImportant = feedImportantMap[feed.id] - if (feedImportant == null && (isStarred || isUnread)) { - feedsIt.remove() - } else { - feed.important = feedImportant - } - } + ) { groupWithFeedList, importantMap -> + groupWithFeedList.fastForEach { + var groupImportant = 0 + it.feeds.fastForEach { + it.important = importantMap[it.id] + groupImportant += it.important ?: 0 } + it.group.important = groupImportant } groupWithFeedList - }.onEach { groupWithFeedList -> + }.mapLatest { groupWithFeedList -> _feedsUiState.update { it.copy( - importantCount = groupWithFeedList.sumOf { it.group.important ?: 0 }.run { + importantSum = groupWithFeedList.sumOf { it.group.important ?: 0 }.run { when { isStarred -> stringsRepository.getQuantityString( R.plurals.starred_desc, @@ -121,8 +99,15 @@ class FeedsViewModel @Inject constructor( ) } }, - groupWithFeedList = groupWithFeedList, - feedsVisible = List(groupWithFeedList.size, init = { true }) + groupWithFeedList = groupWithFeedList.map { + mutableListOf(GroupFeedsView.Group(it.group)).apply { + addAll( + it.feeds.map { + GroupFeedsView.Feed(it) + } + ) + } + }.flatten(), ) } }.catch { @@ -133,9 +118,13 @@ class FeedsViewModel @Inject constructor( data class FeedsUiState( val account: Account? = null, - val importantCount: String = "", - val groupWithFeedList: List = emptyList(), - val feedsVisible: List = emptyList(), + val importantSum: String = "", + val groupWithFeedList: List = emptyList(), val listState: LazyListState = LazyListState(), val groupsVisible: Boolean = true, ) + +sealed class GroupFeedsView { + class Group(val group: me.ash.reader.data.entity.Group) : GroupFeedsView() + class Feed(val feed: me.ash.reader.data.entity.Feed) : GroupFeedsView() +} \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/GroupItem.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/GroupItem.kt index 61f79d5..e41ef52 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/GroupItem.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/GroupItem.kt @@ -1,58 +1,56 @@ package me.ash.reader.ui.page.home.feeds -import RYExtensibleVisibility import android.view.HapticFeedbackConstants +import androidx.compose.animation.animateContentSize import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.CircleShape -import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.ExpandLess import androidx.compose.material.icons.rounded.ExpandMore import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text -import androidx.compose.runtime.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.platform.LocalView import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel 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.GroupOptionViewModel +import me.ash.reader.ui.theme.Shape32 +import me.ash.reader.ui.theme.ShapeTop32 @OptIn(androidx.compose.foundation.ExperimentalFoundationApi::class) @Composable fun GroupItem( - modifier: Modifier = Modifier, - tonalElevation: Dp, group: Group, - feeds: List, - isExpanded: Boolean = true, + alpha: Float = 1f, + indicatorAlpha: Float = 1f, + isExpanded: () -> Boolean, groupOptionViewModel: GroupOptionViewModel = hiltViewModel(), + onExpanded: () -> Unit = {}, groupOnClick: () -> Unit = {}, - feedOnClick: (feed: Feed) -> Unit = {}, ) { val view = LocalView.current val scope = rememberCoroutineScope() - var expanded by remember { mutableStateOf(isExpanded) } Column( modifier = Modifier + .animateContentSize() .fillMaxWidth() .padding(horizontal = 16.dp) - .clip(RoundedCornerShape(32.dp)) + .clip(if (isExpanded()) ShapeTop32 else Shape32) .background( - MaterialTheme.colorScheme.secondary.copy(alpha = tonalElevation.alphaLN(weight = 1.2f)) + MaterialTheme.colorScheme.secondary.copy(alpha = alpha) ) .combinedClickable( onClick = { @@ -66,7 +64,7 @@ fun GroupItem( .padding(top = 22.dp) ) { Row( - modifier = modifier.fillMaxWidth(), + modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically, ) { @@ -86,38 +84,21 @@ fun GroupItem( .size(24.dp) .clip(CircleShape) .background( - MaterialTheme.colorScheme.surfaceTint.copy( - alpha = tonalElevation.alphaLN(weight = 1.4f) - ) + MaterialTheme.colorScheme.surfaceTint.copy(alpha = indicatorAlpha) ) .clickable { - expanded = !expanded + onExpanded() }, horizontalArrangement = Arrangement.Center, verticalAlignment = Alignment.CenterVertically, ) { Icon( - imageVector = if (expanded) Icons.Rounded.ExpandLess else Icons.Rounded.ExpandMore, - contentDescription = stringResource(if (expanded) R.string.expand_less else R.string.expand_more), + imageVector = if (isExpanded()) Icons.Rounded.ExpandLess else Icons.Rounded.ExpandMore, + contentDescription = stringResource(if (isExpanded()) R.string.expand_less else R.string.expand_more), tint = MaterialTheme.colorScheme.onSecondaryContainer, ) } } Spacer(modifier = Modifier.height(22.dp)) - RYExtensibleVisibility(visible = expanded) { - Column { - feeds.forEach { feed -> - FeedItem( - feed = feed, - tonalElevation = tonalElevation, - ) { - feedOnClick(feed) - } - } - if (feeds.isNotEmpty()) { - Spacer(modifier = Modifier.height(16.dp)) - } - } - } } } \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/feed/FeedOptionDrawer.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/feed/FeedOptionDrawer.kt index 5672846..f6f47b0 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/feed/FeedOptionDrawer.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/drawer/feed/FeedOptionDrawer.kt @@ -1,5 +1,6 @@ package me.ash.reader.ui.page.home.feeds.drawer.feed +import android.view.HapticFeedbackConstants import androidx.activity.compose.BackHandler import androidx.compose.foundation.layout.* import androidx.compose.material.ExperimentalMaterialApi @@ -13,6 +14,7 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalView import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp @@ -23,6 +25,7 @@ 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.openURL import me.ash.reader.ui.ext.roundClick import me.ash.reader.ui.ext.showToast import me.ash.reader.ui.page.home.feeds.subscribe.ResultView @@ -34,6 +37,7 @@ fun FeedOptionDrawer( content: @Composable () -> Unit = {}, ) { val context = LocalContext.current + val view = LocalView.current val scope = rememberCoroutineScope() val feedOptionUiState = feedOptionViewModel.feedOptionUiState.collectAsStateValue() val feed = feedOptionUiState.feed @@ -77,7 +81,8 @@ fun FeedOptionDrawer( ResultView( link = feed?.url ?: stringResource(R.string.unknown), groups = feedOptionUiState.groups, - selectedAllowNotificationPreset = feedOptionUiState.feed?.isNotification ?: false, + selectedAllowNotificationPreset = feedOptionUiState.feed?.isNotification + ?: false, selectedParseFullContentPreset = feedOptionUiState.feed?.isFullContent ?: false, isMoveToGroup = true, showUnsubscribe = true, @@ -101,6 +106,10 @@ fun FeedOptionDrawer( feedOptionViewModel.showNewGroupDialog() }, onFeedUrlClick = { + context.openURL(feed?.url) + }, + onFeedUrlLongClick = { + view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP) feedOptionViewModel.showFeedUrlDialog() } ) diff --git a/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/ResultView.kt b/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/ResultView.kt index 692101e..cadeb24 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/ResultView.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/feeds/subscribe/ResultView.kt @@ -1,15 +1,11 @@ package me.ash.reader.ui.page.home.feeds.subscribe import androidx.compose.animation.animateContentSize -import androidx.compose.foundation.background -import androidx.compose.foundation.clickable +import androidx.compose.foundation.* import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.items -import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape -import androidx.compose.foundation.text.selection.SelectionContainer -import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.Add import androidx.compose.material.icons.outlined.Article @@ -32,7 +28,6 @@ import me.ash.reader.R import me.ash.reader.data.entity.Group import me.ash.reader.ui.component.base.RYSelectionChip import me.ash.reader.ui.component.base.Subtitle -import me.ash.reader.ui.ext.roundClick import me.ash.reader.ui.theme.palette.alwaysLight @Composable @@ -51,7 +46,8 @@ fun ResultView( unsubscribeOnClick: () -> Unit = {}, onGroupClick: (groupId: String) -> Unit = {}, onAddNewGroup: () -> Unit = {}, - onFeedUrlClick: () -> Unit = {} + onFeedUrlClick: () -> Unit = {}, + onFeedUrlLongClick: () -> Unit = {}, ) { LaunchedEffect(Unit) { if (groups.isNotEmpty() && selectedGroupId.isEmpty()) onGroupClick(groups.first().id) @@ -60,7 +56,11 @@ fun ResultView( Column( modifier = modifier.verticalScroll(rememberScrollState()) ) { - EditableUrl(text = link, onFeedUrlClick) + EditableUrl( + text = link, + onClick = onFeedUrlClick, + onLongClick = onFeedUrlLongClick, + ) Spacer(modifier = Modifier.height(26.dp)) Preset( @@ -85,27 +85,30 @@ fun ResultView( } } +@OptIn(ExperimentalFoundationApi::class) @Composable private fun EditableUrl( text: String, - onClick: () -> Unit + onClick: () -> Unit, + onLongClick: () -> Unit, ) { Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center ) { - SelectionContainer { - Text( - modifier = Modifier.roundClick { - onClick() - }, - text = text, - color = MaterialTheme.colorScheme.outline.copy(alpha = 0.7f), - style = MaterialTheme.typography.bodyMedium, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - ) - } + Text( + modifier = Modifier + .clip(MaterialTheme.shapes.small) + .combinedClickable( + onClick = onClick, + onLongClick = onLongClick, + ), + text = text, + color = MaterialTheme.colorScheme.outline.copy(alpha = 0.7f), + style = MaterialTheme.typography.bodyMedium, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) } } diff --git a/app/src/main/java/me/ash/reader/ui/page/home/flow/ArticleItem.kt b/app/src/main/java/me/ash/reader/ui/page/home/flow/ArticleItem.kt index d741d1c..012a939 100644 --- a/app/src/main/java/me/ash/reader/ui/page/home/flow/ArticleItem.kt +++ b/app/src/main/java/me/ash/reader/ui/page/home/flow/ArticleItem.kt @@ -13,7 +13,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.clip import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp @@ -25,14 +24,13 @@ import me.ash.reader.data.preference.* import me.ash.reader.ui.component.FeedIcon import me.ash.reader.ui.component.base.RYAsyncImage import me.ash.reader.ui.component.base.SIZE_1000 -import me.ash.reader.ui.theme.SHAPE_20 +import me.ash.reader.ui.theme.Shape20 @Composable fun ArticleItem( articleWithFeed: ArticleWithFeed, onClick: (ArticleWithFeed) -> Unit = {}, ) { - val context = LocalContext.current val articleListFeedIcon = LocalFlowArticleListFeedIcon.current val articleListFeedName = LocalFlowArticleListFeedName.current val articleListImage = LocalFlowArticleListImage.current @@ -42,12 +40,12 @@ fun ArticleItem( Column( modifier = Modifier .padding(horizontal = 12.dp) - .clip(SHAPE_20) + .clip(Shape20) .clickable { onClick(articleWithFeed) } .padding(horizontal = 12.dp, vertical = 12.dp) .alpha(if (articleWithFeed.article.isStarred || articleWithFeed.article.isUnread) 1f else 0.5f), ) { - // Upper + // Top Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, @@ -99,7 +97,7 @@ fun ArticleItem( } } - // Lower + // Bottom Row( modifier = Modifier.fillMaxWidth(), ) { @@ -142,7 +140,7 @@ fun ArticleItem( modifier = Modifier .padding(start = 10.dp) .size(80.dp) - .clip(SHAPE_20), + .clip(Shape20), data = articleWithFeed.article.img, scale = Scale.FILL, precision = Precision.INEXACT, diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/color/feeds/FeedsPagePreview.kt b/app/src/main/java/me/ash/reader/ui/page/settings/color/feeds/FeedsPagePreview.kt new file mode 100644 index 0000000..1408f40 --- /dev/null +++ b/app/src/main/java/me/ash/reader/ui/page/settings/color/feeds/FeedsPagePreview.kt @@ -0,0 +1,143 @@ +package me.ash.reader.ui.page.settings.color.feeds + +import androidx.compose.animation.animateContentSize +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.rounded.Add +import androidx.compose.material.icons.rounded.ArrowBack +import androidx.compose.material.icons.rounded.Refresh +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.SmallTopAppBar +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import me.ash.reader.R +import me.ash.reader.data.entity.Feed +import me.ash.reader.data.entity.Group +import me.ash.reader.data.model.Filter +import me.ash.reader.data.preference.FeedsGroupListExpandPreference +import me.ash.reader.data.preference.FeedsGroupListTonalElevationPreference +import me.ash.reader.data.preference.FeedsTopBarTonalElevationPreference +import me.ash.reader.ui.component.FilterBar +import me.ash.reader.ui.component.base.FeedbackIconButton +import me.ash.reader.ui.ext.alphaLN +import me.ash.reader.ui.ext.surfaceColorAtElevation +import me.ash.reader.ui.page.home.feeds.FeedItem +import me.ash.reader.ui.page.home.feeds.GroupItem +import me.ash.reader.ui.theme.palette.onDark +import kotlin.math.ln + +@Composable +fun FeedsPagePreview( + topBarTonalElevation: FeedsTopBarTonalElevationPreference, + groupListExpand: FeedsGroupListExpandPreference, + groupListTonalElevation: FeedsGroupListTonalElevationPreference, + filterBarStyle: Int, + filterBarFilled: Boolean, + filterBarPadding: Dp, + filterBarTonalElevation: Dp, +) { + var filter by remember { mutableStateOf(Filter.Unread) } + val feedBadgeAlpha by remember { derivedStateOf { (ln(groupListTonalElevation.value + 1.4f) + 2f) / 100f } } + val groupAlpha by remember { derivedStateOf { groupListTonalElevation.value.dp.alphaLN(weight = 1.2f) } } + val groupIndicatorAlpha by remember { + derivedStateOf { + groupListTonalElevation.value.dp.alphaLN( + weight = 1.4f + ) + } + } + + Column( + modifier = Modifier + .animateContentSize() + .background( + color = MaterialTheme.colorScheme.surfaceColorAtElevation( + groupListTonalElevation.value.dp + ) onDark MaterialTheme.colorScheme.surface, + shape = RoundedCornerShape(24.dp) + ) + ) { + SmallTopAppBar( + title = {}, + colors = TopAppBarDefaults.smallTopAppBarColors( + containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation( + topBarTonalElevation.value.dp + ), + ), + navigationIcon = { + FeedbackIconButton( + imageVector = Icons.Rounded.ArrowBack, + contentDescription = stringResource(R.string.back), + tint = MaterialTheme.colorScheme.onSurface + ) + }, + actions = { + FeedbackIconButton( + imageVector = Icons.Rounded.Refresh, + contentDescription = stringResource(R.string.refresh), + tint = MaterialTheme.colorScheme.onSurface, + ) + FeedbackIconButton( + imageVector = Icons.Rounded.Add, + contentDescription = stringResource(R.string.subscribe), + tint = MaterialTheme.colorScheme.onSurface, + ) + } + ) + Spacer(modifier = Modifier.height(12.dp)) + GroupItem( + isExpanded = { groupListExpand.value }, + group = generateGroupPreview(), + alpha = groupAlpha, + indicatorAlpha = groupIndicatorAlpha, + ) + FeedItem( + feed = generateFeedPreview(), + alpha = groupAlpha, + badgeAlpha = feedBadgeAlpha, + isEnded = true, + isExpanded = { true }, + ) + Spacer(modifier = Modifier.height(12.dp)) + FilterBar( + filter = filter, + filterBarStyle = filterBarStyle, + filterBarFilled = filterBarFilled, + filterBarPadding = filterBarPadding, + filterBarTonalElevation = filterBarTonalElevation, + ) { + filter = it + } + } +} + +@Stable +@Composable +fun generateFeedPreview(): Feed = + Feed( + id = "", + name = stringResource(R.string.preview_feed_name), + icon = "", + accountId = 0, + groupId = "", + url = "", + ).apply { + important = 100 + } + +@Stable +@Composable +fun generateGroupPreview(): Group = + Group( + id = "", + name = stringResource(R.string.defaults), + accountId = 0, + ) \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/color/feeds/FeedsPageStylePage.kt b/app/src/main/java/me/ash/reader/ui/page/settings/color/feeds/FeedsPageStylePage.kt index 2c4095f..3a1a953 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/color/feeds/FeedsPageStylePage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/color/feeds/FeedsPageStylePage.kt @@ -1,38 +1,25 @@ package me.ash.reader.ui.page.settings.color.feeds -import androidx.compose.animation.animateContentSize import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.rounded.Add import androidx.compose.material.icons.rounded.ArrowBack -import androidx.compose.material.icons.rounded.Refresh import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.SmallTopAppBar -import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource -import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.navigation.NavHostController import me.ash.reader.R -import me.ash.reader.data.entity.Feed -import me.ash.reader.data.entity.Group -import me.ash.reader.data.model.Filter import me.ash.reader.data.preference.* -import me.ash.reader.ui.component.FilterBar import me.ash.reader.ui.component.base.* -import me.ash.reader.ui.ext.surfaceColorAtElevation -import me.ash.reader.ui.page.home.feeds.GroupItem import me.ash.reader.ui.page.settings.SettingItem -import me.ash.reader.ui.theme.palette.onDark import me.ash.reader.ui.theme.palette.onLight @Composable @@ -270,88 +257,3 @@ fun FeedsPageStylePage( groupListTonalElevationDialogVisible = false } } - -@Composable -fun FeedsPagePreview( - topBarTonalElevation: FeedsTopBarTonalElevationPreference, - groupListExpand: FeedsGroupListExpandPreference, - groupListTonalElevation: FeedsGroupListTonalElevationPreference, - filterBarStyle: Int, - filterBarFilled: Boolean, - filterBarPadding: Dp, - filterBarTonalElevation: Dp, -) { - var filter by remember { mutableStateOf(Filter.Unread) } - - Column( - modifier = Modifier - .animateContentSize() - .background( - color = MaterialTheme.colorScheme.surfaceColorAtElevation( - groupListTonalElevation.value.dp - ) onDark MaterialTheme.colorScheme.surface, - shape = RoundedCornerShape(24.dp) - ) - ) { - SmallTopAppBar( - title = {}, - colors = TopAppBarDefaults.smallTopAppBarColors( - containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation( - topBarTonalElevation.value.dp - ), - ), - navigationIcon = { - FeedbackIconButton( - imageVector = Icons.Rounded.ArrowBack, - contentDescription = stringResource(R.string.back), - tint = MaterialTheme.colorScheme.onSurface - ) {} - }, - actions = { - FeedbackIconButton( - imageVector = Icons.Rounded.Refresh, - contentDescription = stringResource(R.string.refresh), - tint = MaterialTheme.colorScheme.onSurface, - ) {} - FeedbackIconButton( - imageVector = Icons.Rounded.Add, - contentDescription = stringResource(R.string.subscribe), - tint = MaterialTheme.colorScheme.onSurface, - ) {} - } - ) - Spacer(modifier = Modifier.height(12.dp)) - GroupItem( - isExpanded = groupListExpand.value, - tonalElevation = groupListTonalElevation.value.dp, - group = Group( - id = "", - name = stringResource(R.string.defaults), - accountId = 0, - ), - feeds = listOf( - Feed( - id = "", - name = stringResource(R.string.preview_feed_name), - icon = "", - accountId = 0, - groupId = "", - url = "", - ).apply { - important = 100 - } - ), - ) - Spacer(modifier = Modifier.height(12.dp)) - FilterBar( - modifier = Modifier.padding(horizontal = 12.dp), - filter = filter, - filterBarStyle = filterBarStyle, - filterBarFilled = filterBarFilled, - filterBarPadding = filterBarPadding, - filterBarTonalElevation = filterBarTonalElevation, - ) { - filter = it - } - } -} \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/color/flow/FlowPagePreview.kt b/app/src/main/java/me/ash/reader/ui/page/settings/color/flow/FlowPagePreview.kt new file mode 100644 index 0000000..d3101fb --- /dev/null +++ b/app/src/main/java/me/ash/reader/ui/page/settings/color/flow/FlowPagePreview.kt @@ -0,0 +1,124 @@ +package me.ash.reader.ui.page.settings.color.flow + +import androidx.compose.animation.animateContentSize +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.rounded.ArrowBack +import androidx.compose.material.icons.rounded.DoneAll +import androidx.compose.material.icons.rounded.Search +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.SmallTopAppBar +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import me.ash.reader.R +import me.ash.reader.data.entity.Article +import me.ash.reader.data.entity.ArticleWithFeed +import me.ash.reader.data.entity.Feed +import me.ash.reader.data.model.Filter +import me.ash.reader.data.preference.FlowArticleListTonalElevationPreference +import me.ash.reader.data.preference.FlowTopBarTonalElevationPreference +import me.ash.reader.ui.component.FilterBar +import me.ash.reader.ui.component.base.FeedbackIconButton +import me.ash.reader.ui.ext.surfaceColorAtElevation +import me.ash.reader.ui.page.home.flow.ArticleItem +import me.ash.reader.ui.theme.palette.onDark +import java.util.* + +@Composable +fun FlowPagePreview( + topBarTonalElevation: FlowTopBarTonalElevationPreference, + articleListTonalElevation: FlowArticleListTonalElevationPreference, + filterBarStyle: Int, + filterBarFilled: Boolean, + filterBarPadding: Dp, + filterBarTonalElevation: Dp, +) { + var filter by remember { mutableStateOf(Filter.Unread) } + + Column( + modifier = Modifier + .animateContentSize() + .background( + color = MaterialTheme.colorScheme.surfaceColorAtElevation( + articleListTonalElevation.value.dp + ) onDark MaterialTheme.colorScheme.surface, + shape = RoundedCornerShape(24.dp) + ) + ) { + SmallTopAppBar( + title = {}, + colors = TopAppBarDefaults.smallTopAppBarColors( + containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation( + topBarTonalElevation.value.dp + ), + ), + navigationIcon = { + FeedbackIconButton( + imageVector = Icons.Rounded.ArrowBack, + contentDescription = stringResource(R.string.back), + tint = MaterialTheme.colorScheme.onSurface + ) {} + }, + actions = { + FeedbackIconButton( + imageVector = Icons.Rounded.DoneAll, + contentDescription = stringResource(R.string.mark_all_as_read), + tint = MaterialTheme.colorScheme.onSurface, + ) {} + FeedbackIconButton( + imageVector = Icons.Rounded.Search, + contentDescription = stringResource(R.string.search), + tint = MaterialTheme.colorScheme.onSurface, + ) {} + } + ) + Spacer(modifier = Modifier.height(12.dp)) + ArticleItem( + articleWithFeed = generateArticleWithFeedPreview(), + ) + Spacer(modifier = Modifier.height(12.dp)) + FilterBar( + filter = filter, + filterBarStyle = filterBarStyle, + filterBarFilled = filterBarFilled, + filterBarPadding = filterBarPadding, + filterBarTonalElevation = filterBarTonalElevation, + ) { + filter = it + } + } +} + +@Stable +@Composable +fun generateArticleWithFeedPreview(): ArticleWithFeed = + ArticleWithFeed( + Article( + id = "", + title = stringResource(R.string.preview_article_title), + shortDescription = stringResource(R.string.preview_article_desc), + rawDescription = stringResource(R.string.preview_article_desc), + link = "", + feedId = "", + accountId = 0, + date = Date(1654053729L), + isStarred = true, + img = "https://images.unsplash.com/photo-1544716278-ca5e3f4abd8c?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1yZWxhdGVkfDJ8fHxlbnwwfHx8fA%3D%3D&auto=format&fit=crop&w=800&q=60" + ), + feed = Feed( + id = "", + name = stringResource(R.string.preview_feed_name), + icon = "", + accountId = 0, + groupId = "", + url = "", + ), + ) diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/color/flow/FlowPageStylePage.kt b/app/src/main/java/me/ash/reader/ui/page/settings/color/flow/FlowPageStylePage.kt index d8c7116..add9a0d 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/color/flow/FlowPageStylePage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/color/flow/FlowPageStylePage.kt @@ -1,6 +1,5 @@ package me.ash.reader.ui.page.settings.color.flow -import androidx.compose.animation.animateContentSize import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* @@ -8,34 +7,20 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.ArrowBack -import androidx.compose.material.icons.rounded.DoneAll -import androidx.compose.material.icons.rounded.Search import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.SmallTopAppBar -import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource -import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.navigation.NavHostController import me.ash.reader.R -import me.ash.reader.data.entity.Article -import me.ash.reader.data.entity.ArticleWithFeed -import me.ash.reader.data.entity.Feed -import me.ash.reader.data.model.Filter import me.ash.reader.data.preference.* -import me.ash.reader.ui.component.FilterBar import me.ash.reader.ui.component.base.* -import me.ash.reader.ui.ext.surfaceColorAtElevation -import me.ash.reader.ui.page.home.flow.ArticleItem import me.ash.reader.ui.page.settings.SettingItem -import me.ash.reader.ui.theme.palette.onDark import me.ash.reader.ui.theme.palette.onLight -import java.util.* @Composable fun FlowPageStylePage( @@ -332,90 +317,3 @@ fun FlowPageStylePage( articleListTonalElevationDialogVisible = false } } - -@Composable -fun FlowPagePreview( - topBarTonalElevation: FlowTopBarTonalElevationPreference, - articleListTonalElevation: FlowArticleListTonalElevationPreference, - filterBarStyle: Int, - filterBarFilled: Boolean, - filterBarPadding: Dp, - filterBarTonalElevation: Dp, -) { - var filter by remember { mutableStateOf(Filter.Unread) } - - Column( - modifier = Modifier - .animateContentSize() - .background( - color = MaterialTheme.colorScheme.surfaceColorAtElevation( - articleListTonalElevation.value.dp - ) onDark MaterialTheme.colorScheme.surface, - shape = RoundedCornerShape(24.dp) - ) - ) { - SmallTopAppBar( - title = {}, - colors = TopAppBarDefaults.smallTopAppBarColors( - containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation( - topBarTonalElevation.value.dp - ), - ), - navigationIcon = { - FeedbackIconButton( - imageVector = Icons.Rounded.ArrowBack, - contentDescription = stringResource(R.string.back), - tint = MaterialTheme.colorScheme.onSurface - ) {} - }, - actions = { - FeedbackIconButton( - imageVector = Icons.Rounded.DoneAll, - contentDescription = stringResource(R.string.mark_all_as_read), - tint = MaterialTheme.colorScheme.onSurface, - ) {} - FeedbackIconButton( - imageVector = Icons.Rounded.Search, - contentDescription = stringResource(R.string.search), - tint = MaterialTheme.colorScheme.onSurface, - ) {} - } - ) - Spacer(modifier = Modifier.height(12.dp)) - ArticleItem( - articleWithFeed = ArticleWithFeed( - Article( - id = "", - title = stringResource(R.string.preview_article_title), - shortDescription = stringResource(R.string.preview_article_desc), - rawDescription = stringResource(R.string.preview_article_desc), - link = "", - feedId = "", - accountId = 0, - date = Date(), - isStarred = true, - img = "https://images.unsplash.com/photo-1544716278-ca5e3f4abd8c?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1yZWxhdGVkfDJ8fHxlbnwwfHx8fA%3D%3D&auto=format&fit=crop&w=800&q=60" - ), - feed = Feed( - id = "", - name = stringResource(R.string.preview_feed_name), - icon = "", - accountId = 0, - groupId = "", - url = "", - ), - ) - ) - Spacer(modifier = Modifier.height(12.dp)) - FilterBar( - modifier = Modifier.padding(horizontal = 12.dp), - filter = filter, - filterBarStyle = filterBarStyle, - filterBarFilled = filterBarFilled, - filterBarPadding = filterBarPadding, - filterBarTonalElevation = filterBarTonalElevation, - ) { - filter = it - } - } -} \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/ui/theme/Shapes.kt b/app/src/main/java/me/ash/reader/ui/theme/Shapes.kt index 670cab2..9905264 100644 --- a/app/src/main/java/me/ash/reader/ui/theme/Shapes.kt +++ b/app/src/main/java/me/ash/reader/ui/theme/Shapes.kt @@ -2,6 +2,7 @@ package me.ash.reader.ui.theme import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Shapes +import androidx.compose.runtime.Stable import androidx.compose.ui.unit.dp val Shapes = Shapes( @@ -12,4 +13,14 @@ val Shapes = Shapes( extraLarge = RoundedCornerShape(28.0.dp) ) -val SHAPE_20 = RoundedCornerShape(20.0.dp) \ No newline at end of file +@Stable +val Shape20 = RoundedCornerShape(20.0.dp) + +@Stable +val Shape32 = RoundedCornerShape(32.0.dp) + +@Stable +val ShapeTop32 = RoundedCornerShape(32.0.dp, 32.0.dp, 0.0.dp, 0.0.dp) + +@Stable +val ShapeBottom32 = RoundedCornerShape(0.0.dp, 0.0.dp, 32.0.dp, 32.0.dp)