Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ dependencies {
implementation(libs.kotlinx.coroutines.core)
implementation(libs.kotlinx.serialization.json)
implementation(libs.okhttp.client)
implementation(libs.vico.compose.m3)
implementation(libs.sqldelight.android.driver)
implementation(libs.sqldelight.androidx.paging.extensions)
implementation(libs.zoomable)
Expand Down
38 changes: 33 additions & 5 deletions app/src/main/java/com/capyreader/app/ui/settings/SettingsView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ import androidx.compose.material3.adaptive.navigation.rememberListDetailPaneScaf
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
Expand All @@ -28,6 +31,8 @@ import com.capyreader.app.ui.settings.panels.GeneralSettingsPanel
import com.capyreader.app.ui.settings.panels.GesturesSettingPanel
import com.capyreader.app.ui.settings.panels.NotificationsSettingsPanel
import com.capyreader.app.ui.settings.panels.SettingsPanel
import com.capyreader.app.ui.settings.panels.SubscriptionDetailView
import com.capyreader.app.ui.settings.panels.SubscriptionsSettingsPanel
import com.capyreader.app.ui.settings.panels.UnreadBadgesSettingsPanel
import com.capyreader.app.ui.settings.panels.SettingsViewModel
import com.jocmp.capy.common.launchUI
Expand All @@ -48,6 +53,7 @@ fun SettingsView(
val currentPanel = navigator.currentDestination?.contentKey
val feeds by viewModel.feeds.collectAsStateWithLifecycle(emptyList())
val savedSearches by viewModel.savedSearches.collectAsStateWithLifecycle(emptyList())
var subscriptionDetailFeedId by rememberSaveable { mutableStateOf<String?>(null) }

val navigateToPanel = { panel: SettingsPanel ->
coroutineScope.launchUI {
Expand Down Expand Up @@ -86,15 +92,37 @@ fun SettingsView(
SettingsPanelScaffold(
panel = currentPanel,
onBack = {
navigateBack()
if (currentPanel == SettingsPanel.Subscriptions && subscriptionDetailFeedId != null) {
subscriptionDetailFeedId = null
} else {
navigateBack()
}
},
) {
when (currentPanel) {
SettingsPanel.General -> GeneralSettingsPanel(
onNavigateToNotifications = {
navigateToPanel(SettingsPanel.Notifications)
SettingsPanel.General -> GeneralSettingsPanel()

SettingsPanel.Subscriptions -> {
val detailFeedId = subscriptionDetailFeedId
if (detailFeedId != null) {
SubscriptionDetailView(
feedStats = viewModel.feedStats,
onLoadStats = {
viewModel.loadFeedStats(detailFeedId)
},
)
} else {
SubscriptionsSettingsPanel(
feeds = feeds,
onSelectAll = viewModel::selectAllFeedNotifications,
onSelectNone = viewModel::deselectAllFeedNotifications,
onToggleNotifications = viewModel::toggleNotifications,
onNavigateToDetail = { feedID ->
subscriptionDetailFeedId = feedID
},
)
}
)
}

SettingsPanel.Notifications -> NotificationsSettingsPanel(
onSelectNone = viewModel::deselectAllFeedNotifications,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,6 @@
package com.capyreader.app.ui.settings.panels

import android.Manifest
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts.RequestPermission
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
Expand All @@ -18,20 +9,14 @@ import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.FilledTonalButton
import androidx.compose.material3.ListItem
import androidx.compose.material3.ListItemDefaults
import androidx.compose.material3.SnackbarDuration
import androidx.compose.material3.SnackbarResult
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
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.tooling.preview.Preview
Expand All @@ -40,7 +25,6 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.capyreader.app.BuildConfig
import com.capyreader.app.R
import com.capyreader.app.common.RowItem
import com.capyreader.app.notifications.Notifications
import com.capyreader.app.preferences.AfterReadAllBehavior
import com.capyreader.app.preferences.HomePage
import com.capyreader.app.refresher.RefreshInterval
Expand All @@ -49,7 +33,6 @@ import com.capyreader.app.ui.components.FormSection
import com.capyreader.app.ui.components.TextSwitch
import com.capyreader.app.ui.fixtures.PreviewKoinApplication
import com.capyreader.app.ui.settings.CrashReportingCheckbox
import com.capyreader.app.ui.components.LocalSnackbarHost
import com.capyreader.app.ui.settings.PreferenceSelect
import com.capyreader.app.ui.settings.filters.FilterKeywords
import com.capyreader.app.ui.settings.filters.FiltersItem
Expand All @@ -58,14 +41,12 @@ import com.capyreader.app.ui.theme.CapyTheme
import com.jocmp.capy.accounts.AutoDelete
import com.jocmp.capy.accounts.Source
import com.jocmp.capy.articles.SortOrder
import com.jocmp.capy.common.launchUI
import org.koin.androidx.compose.koinViewModel
import java.lang.String.CASE_INSENSITIVE_ORDER

@Composable
fun GeneralSettingsPanel(
viewModel: GeneralSettingsViewModel = koinViewModel(),
onNavigateToNotifications: () -> Unit,
) {
val hasReadLaterFeed by viewModel.hasReadLaterFeed.collectAsStateWithLifecycle(initialValue = false)
val keywords by viewModel.filterKeywords.collectAsStateWithLifecycle()
Expand All @@ -81,7 +62,6 @@ fun GeneralSettingsPanel(
) {
GeneralSettingsPanelView(
source = viewModel.source,
onNavigateToNotifications = onNavigateToNotifications,
refreshInterval = viewModel.refreshInterval,
updateRefreshInterval = viewModel::updateRefreshInterval,
canOpenLinksInternally = viewModel.canOpenLinksInternally,
Expand Down Expand Up @@ -109,7 +89,6 @@ fun GeneralSettingsPanel(
@Composable
fun GeneralSettingsPanelView(
source: Source,
onNavigateToNotifications: () -> Unit,
onClearArticles: () -> Unit,
refreshInterval: RefreshInterval,
updateRefreshInterval: (RefreshInterval) -> Unit,
Expand Down Expand Up @@ -163,10 +142,6 @@ fun GeneralSettingsPanelView(
refreshInterval = refreshInterval,
updateRefreshInterval = updateRefreshInterval,
)
NotificationsListItem(
onNavigate = onNavigateToNotifications,
refreshInterval = refreshInterval,
)
if (source == Source.LOCAL) {
FiltersItem()
}
Expand Down Expand Up @@ -276,75 +251,6 @@ fun GeneralSettingsPanelView(
}
}

@Composable
fun NotificationsListItem(
onNavigate: () -> Unit,
refreshInterval: RefreshInterval,
) {
val defaultColors = ListItemDefaults.colors()
val enabled = refreshInterval.isPeriodic
val snackbar = LocalSnackbarHost.current
val scope = rememberCoroutineScope()
val context = LocalContext.current

fun showPermissionFailureMessage() {
scope.launchUI {
snackbar.showSnackbar(
message = context.getString(R.string.notifications_permission_disabled_title),
actionLabel = context.getString(R.string.notifications_permissions_disabled_call_to_action),
duration = SnackbarDuration.Short
).let { result ->
if (result == SnackbarResult.ActionPerformed) {
context.openAppSettings()
}
}
}
}

val colors = ListItemDefaults.colors(
headlineColor = if (enabled) defaultColors.headlineColor else defaultColors.disabledHeadlineColor,
supportingColor = if (enabled) defaultColors.supportingTextColor else defaultColors.disabledHeadlineColor,
)

val permissions = rememberLauncherForActivityResult(RequestPermission()) { allowed ->
if (allowed) {
onNavigate()
} else {
showPermissionFailureMessage()
}
}

Box(
Modifier.clickable(
enabled = enabled
) {
if (Notifications.askForPermission) {
permissions.launch(Manifest.permission.POST_NOTIFICATIONS)
} else {
onNavigate()
}
}
) {
ListItem(
colors = colors,
headlineContent = {
Text(stringResource(R.string.settings_panel_notifications_title))
},
supportingContent = {
if (!enabled) {
Text(stringResource(R.string.settings_enable_refresh_call_to_action))
}
}
)
}
}

private fun Context.openAppSettings() {
startActivity(Intent().apply {
action = ACTION_APPLICATION_DETAILS_SETTINGS
data = Uri.fromParts("package", packageName, null)
})
}

@Preview
@Composable
Expand All @@ -362,7 +268,6 @@ private fun GeneralSettingsPanelPreview() {
autoDelete = AutoDelete.WEEKLY,
sortOrder = SortOrder.NEWEST_FIRST,
updateSortOrder = {},
onNavigateToNotifications = {},
markReadOnScroll = true,
updateConfirmMarkAllRead = {},
updateMarkReadOnScroll = {},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import androidx.compose.material.icons.rounded.Gesture
import androidx.compose.material.icons.rounded.Info
import androidx.compose.material.icons.rounded.Notifications
import androidx.compose.material.icons.rounded.Palette
import androidx.compose.material.icons.rounded.RssFeed
import androidx.compose.material.icons.rounded.Visibility
import androidx.compose.ui.graphics.vector.ImageVector
import com.capyreader.app.R
Expand All @@ -22,6 +23,11 @@ sealed class SettingsPanel(@StringRes val title: Int) {
override fun icon() = Icons.Rounded.Build
}

@Parcelize
data object Subscriptions : SettingsPanel(title = R.string.settings_panel_subscriptions_title), Parcelable {
override fun icon() = Icons.Rounded.RssFeed
}

@Parcelize
data object Notifications : SettingsPanel(title = R.string.settings_panel_notifications_title), Parcelable {
override fun icon() = Icons.Rounded.Notifications
Expand Down Expand Up @@ -66,6 +72,7 @@ sealed class SettingsPanel(@StringRes val title: Int) {
val items: List<SettingsPanel>
get() = listOf(
General,
Subscriptions,
Display,
Gestures,
Account,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import androidx.lifecycle.viewModelScope
import com.capyreader.app.preferences.AppPreferences
import com.capyreader.app.preferences.BadgeStyle
import com.jocmp.capy.Account
import com.jocmp.capy.FeedStats
import kotlinx.coroutines.launch

class SettingsViewModel(
Expand All @@ -18,6 +19,9 @@ class SettingsViewModel(
val feeds = account.allFeeds
val savedSearches = account.savedSearches

var feedStats by mutableStateOf<FeedStats?>(null)
private set

var badgeStyle by mutableStateOf(appPreferences.badgeStyle.get())
private set

Expand Down Expand Up @@ -73,4 +77,10 @@ class SettingsViewModel(
account.toggleAllUnreadBadges(enabled = false)
}
}

fun loadFeedStats(feedID: String) {
viewModelScope.launch {
feedStats = account.feedStats(feedID)
}
}
}
Loading
Loading