Skip to content

Commit

Permalink
feat(ui): configuration for pull-to-load gesture
Browse files Browse the repository at this point in the history
  • Loading branch information
JunkFood02 committed Feb 20, 2024
1 parent 2d9e3de commit a187d38
Show file tree
Hide file tree
Showing 9 changed files with 122 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ fun Preferences.toSettings(): Settings {
initialFilter = InitialFilterPreference.fromPreferences(this),
swipeStartAction = SwipeStartActionPreference.fromPreferences(this),
swipeEndAction = SwipeEndActionPreference.fromPreferences(this),
pullToSwitchArticle = PullToSwitchArticlePreference.fromPreference(this),
openLink = OpenLinkPreference.fromPreferences(this),
openLinkSpecificBrowser = OpenLinkSpecificBrowserPreference.fromPreferences(this),

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package me.ash.reader.infrastructure.preference

import android.content.Context
import androidx.datastore.preferences.core.Preferences
import kotlinx.coroutines.CoroutineScope
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

class PullToSwitchArticlePreference(val value: Boolean) : Preference() {
override fun put(context: Context, scope: CoroutineScope) {
scope.launch {
context.dataStore.put(DataStoreKeys.PullToSwitchArticle, value)
}
}

fun toggle(context: Context, scope: CoroutineScope) =
PullToSwitchArticlePreference(!value).put(context, scope)

companion object {
val default = PullToSwitchArticlePreference(true)
fun fromPreference(preference: Preferences): PullToSwitchArticlePreference {
return PullToSwitchArticlePreference(
preference[DataStoreKeys.PullToSwitchArticle.key] ?: return default
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ data class Settings(
val initialFilter: InitialFilterPreference = InitialFilterPreference.default,
val swipeStartAction: SwipeStartActionPreference = SwipeStartActionPreference.default,
val swipeEndAction: SwipeEndActionPreference = SwipeEndActionPreference.default,
val pullToSwitchArticle: PullToSwitchArticlePreference = PullToSwitchArticlePreference.default,
val openLink: OpenLinkPreference = OpenLinkPreference.default,
val openLinkSpecificBrowser: OpenLinkSpecificBrowserPreference = OpenLinkSpecificBrowserPreference.default,

Expand Down Expand Up @@ -148,30 +149,40 @@ val LocalFlowArticleListReadIndicator =
compositionLocalOf<FlowArticleReadIndicatorPreference> { FlowArticleReadIndicatorPreference.default }

// Reading page
val LocalReadingTheme = compositionLocalOf<ReadingThemePreference> { ReadingThemePreference.default }
val LocalReadingDarkTheme = compositionLocalOf<ReadingDarkThemePreference> { ReadingDarkThemePreference.default }
val LocalReadingTheme =
compositionLocalOf<ReadingThemePreference> { ReadingThemePreference.default }
val LocalReadingDarkTheme =
compositionLocalOf<ReadingDarkThemePreference> { ReadingDarkThemePreference.default }
val LocalReadingPageTonalElevation =
compositionLocalOf<ReadingPageTonalElevationPreference> { ReadingPageTonalElevationPreference.default }
val LocalReadingAutoHideToolbar =
compositionLocalOf<ReadingAutoHideToolbarPreference> { ReadingAutoHideToolbarPreference.default }
val LocalReadingTextFontSize = compositionLocalOf { ReadingTextFontSizePreference.default }
val LocalReadingLetterSpacing = compositionLocalOf { ReadingLetterSpacingPreference.default }
val LocalReadingTextHorizontalPadding = compositionLocalOf { ReadingTextHorizontalPaddingPreference.default }
val LocalReadingTextAlign = compositionLocalOf<ReadingTextAlignPreference> { ReadingTextAlignPreference.default }
val LocalReadingTextBold = compositionLocalOf<ReadingTextBoldPreference> { ReadingTextBoldPreference.default }
val LocalReadingTitleAlign = compositionLocalOf<ReadingTitleAlignPreference> { ReadingTitleAlignPreference.default }
val LocalReadingTextHorizontalPadding =
compositionLocalOf { ReadingTextHorizontalPaddingPreference.default }
val LocalReadingTextAlign =
compositionLocalOf<ReadingTextAlignPreference> { ReadingTextAlignPreference.default }
val LocalReadingTextBold =
compositionLocalOf<ReadingTextBoldPreference> { ReadingTextBoldPreference.default }
val LocalReadingTitleAlign =
compositionLocalOf<ReadingTitleAlignPreference> { ReadingTitleAlignPreference.default }
val LocalReadingSubheadAlign =
compositionLocalOf<ReadingSubheadAlignPreference> { ReadingSubheadAlignPreference.default }
val LocalReadingFonts = compositionLocalOf<ReadingFontsPreference> { ReadingFontsPreference.default }
val LocalReadingTitleBold = compositionLocalOf<ReadingTitleBoldPreference> { ReadingTitleBoldPreference.default }
val LocalReadingFonts =
compositionLocalOf<ReadingFontsPreference> { ReadingFontsPreference.default }
val LocalReadingTitleBold =
compositionLocalOf<ReadingTitleBoldPreference> { ReadingTitleBoldPreference.default }
val LocalReadingSubheadBold =
compositionLocalOf<ReadingSubheadBoldPreference> { ReadingSubheadBoldPreference.default }
val LocalReadingTitleUpperCase =
compositionLocalOf<ReadingTitleUpperCasePreference> { ReadingTitleUpperCasePreference.default }
val LocalReadingSubheadUpperCase =
compositionLocalOf<ReadingSubheadUpperCasePreference> { ReadingSubheadUpperCasePreference.default }
val LocalReadingImageHorizontalPadding = compositionLocalOf { ReadingImageHorizontalPaddingPreference.default }
val LocalReadingImageRoundedCorners = compositionLocalOf { ReadingImageRoundedCornersPreference.default }
val LocalReadingImageHorizontalPadding =
compositionLocalOf { ReadingImageHorizontalPaddingPreference.default }
val LocalReadingImageRoundedCorners =
compositionLocalOf { ReadingImageRoundedCornersPreference.default }
val LocalReadingImageMaximize =
compositionLocalOf<ReadingImageMaximizePreference> { ReadingImageMaximizePreference.default }

Expand All @@ -181,6 +192,7 @@ val LocalInitialFilter =
compositionLocalOf<InitialFilterPreference> { InitialFilterPreference.default }
val LocalArticleListSwipeEndAction = compositionLocalOf { SwipeEndActionPreference.default }
val LocalArticleListSwipeStartAction = compositionLocalOf { SwipeStartActionPreference.default }
val LocalPullToSwitchArticle = compositionLocalOf { PullToSwitchArticlePreference.default }
val LocalOpenLink =
compositionLocalOf<OpenLinkPreference> { OpenLinkPreference.default }
val LocalOpenLinkSpecificBrowser =
Expand Down Expand Up @@ -269,6 +281,7 @@ fun SettingsProvider(
LocalInitialFilter provides settings.initialFilter,
LocalArticleListSwipeStartAction provides settings.swipeStartAction,
LocalArticleListSwipeEndAction provides settings.swipeEndAction,
LocalPullToSwitchArticle provides settings.pullToSwitchArticle,
LocalOpenLink provides settings.openLink,
LocalOpenLinkSpecificBrowser provides settings.openLinkSpecificBrowser,

Expand Down
7 changes: 6 additions & 1 deletion app/src/main/java/me/ash/reader/ui/ext/DataStoreExt.kt
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,11 @@ sealed class DataStoreKeys<T> {
get() = intPreferencesKey("swipeEndAction")
}

data object PullToSwitchArticle : DataStoreKeys<Boolean>() {
override val key: Preferences.Key<Boolean>
get() = booleanPreferencesKey("pullToSwitchArticle")
}

object OpenLink : DataStoreKeys<Int>() {

override val key: Preferences.Key<Int>
Expand All @@ -425,7 +430,7 @@ sealed class DataStoreKeys<T> {
object OpenLinkAppSpecificBrowser : DataStoreKeys<String>() {

override val key: Preferences.Key<String>
get() = stringPreferencesKey("openLppSpecificBrowser")
get() = stringPreferencesKey("openLppSpecificBrowser")
}

// Languages
Expand Down
32 changes: 27 additions & 5 deletions app/src/main/java/me/ash/reader/ui/page/home/reading/PullToLoad.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import androidx.compose.animation.core.FloatExponentialDecaySpec
import androidx.compose.animation.core.animate
import androidx.compose.animation.core.animateDecay
import androidx.compose.foundation.MutatorMutex
import androidx.compose.foundation.layout.offset
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
Expand All @@ -28,6 +29,7 @@ import androidx.compose.ui.unit.dp
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
import me.ash.reader.ui.page.home.reading.PullToLoadDefaults.ContentOffsetMultiple
import kotlin.math.abs
import kotlin.math.sqrt

Expand All @@ -45,18 +47,18 @@ private const val TAG = "PullRelease"
* @param enabled If not enabled, all scroll delta and fling velocity will be ignored.
* @param onScroll Used for detecting if the reader is scrolling down
*/
class ReaderNestedScrollConnection(
private class ReaderNestedScrollConnection(
private val enabled: Boolean,
private val onPreScroll: (Float) -> Float,
private val onPostScroll: (Float) -> Float,
private val onRelease: (Float) -> Unit,
private val onScroll: (Float) -> Unit
private val onScroll: ((Float) -> Unit)? = null
) : NestedScrollConnection {

override fun onPreScroll(
available: Offset, source: NestedScrollSource
): Offset {
onScroll(available.y)
onScroll?.invoke(available.y)
return when {
!enabled || available.y == 0f -> Offset.Zero

Expand Down Expand Up @@ -294,11 +296,31 @@ private fun Float.signOpposites(f: Float): Boolean =
/**
* Default parameter values for [rememberPullToLoadState].
*/
@ExperimentalMaterialApi
object PullToLoadDefaults {
/**
* If the indicator is below this threshold offset when it is released, the load action
* will be triggered.
*/
val LoadThreshold = 120.dp
}

const val ContentOffsetMultiple = 80
}

fun Modifier.pullToLoad(
state: PullToLoadState,
contentOffsetMultiple: Int = ContentOffsetMultiple,
onScroll: ((Float) -> Unit)? = null,
enabled: Boolean = true
): Modifier =
nestedScroll(
ReaderNestedScrollConnection(
enabled = enabled,
onPreScroll = state::onPullBack,
onPostScroll = state::onPull,
onRelease = state::onRelease,
onScroll = onScroll
)
).run {
if (enabled) offset(x = 0.dp, y = (state.offsetFraction * contentOffsetMultiple).dp)
else this
}
30 changes: 14 additions & 16 deletions app/src/main/java/me/ash/reader/ui/page/home/reading/ReadingPage.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import androidx.compose.foundation.LocalOverscrollConfiguration
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.material.ExperimentalMaterialApi
Expand All @@ -24,13 +23,12 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavHostController
import androidx.paging.compose.collectAsLazyPagingItems
import me.ash.reader.infrastructure.preference.LocalPullToSwitchArticle
import me.ash.reader.infrastructure.preference.LocalReadingAutoHideToolbar
import me.ash.reader.infrastructure.preference.LocalReadingPageTonalElevation
import me.ash.reader.ui.ext.collectAsStateValue
Expand All @@ -50,6 +48,7 @@ fun ReadingPage(
readingViewModel: ReadingViewModel = hiltViewModel(),
) {
val tonalElevation = LocalReadingPageTonalElevation.current
val isPullToSwitchArticleEnabled = LocalPullToSwitchArticle.current.value
val readingUiState = readingViewModel.readingUiState.collectAsStateValue()
val readerState = readingViewModel.readerStateStateFlow.collectAsStateValue()
val homeUiState = homeViewModel.homeUiState.collectAsStateValue()
Expand Down Expand Up @@ -165,26 +164,25 @@ fun ReadingPage(
saver = LazyListState.Saver
) { LazyListState() }

CompositionLocalProvider(LocalOverscrollConfiguration provides null) {
CompositionLocalProvider(
LocalOverscrollConfiguration provides
if (isPullToSwitchArticleEnabled) null else LocalOverscrollConfiguration.current
) {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Content(
modifier = Modifier
.nestedScroll(
ReaderNestedScrollConnection(
enabled = true,
onPreScroll = state::onPullBack,
onPostScroll = state::onPull,
onRelease = state::onRelease,
onScroll = { f ->
if (abs(f) > 2f)
isReaderScrollingDown = f < 0f
})
)
.padding(paddings)
.offset(x = 0.dp, y = (state.offsetFraction * 80).dp),
.pullToLoad(
state = state,
onScroll = { f ->
if (abs(f) > 2f)
isReaderScrollingDown = f < 0f
},
enabled = isPullToSwitchArticleEnabled
),
content = content.text ?: "",
feedName = feedName,
title = title.toString(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ fun ReadingStylePage(
val tonalElevation = LocalReadingPageTonalElevation.current
val fonts = LocalReadingFonts.current
val autoHideToolbar = LocalReadingAutoHideToolbar.current
val pullToSwitchArticle = LocalPullToSwitchArticle.current


var tonalElevationDialogVisible by remember { mutableStateOf(false) }
var fontsDialogVisible by remember { mutableStateOf(false) }
Expand Down Expand Up @@ -169,6 +171,11 @@ fun ReadingStylePage(
enabled = false,
onClick = {},
) {}
SettingItem(
title = stringResource(id = R.string.pull_to_switch_article),
onClick = { pullToSwitchArticle.toggle(context, scope) }) {
RYSwitch(activated = pullToSwitchArticle.value)
}
SettingItem(
title = stringResource(R.string.tonal_elevation),
desc = "${tonalElevation.value}dp",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ import androidx.compose.foundation.layout.windowInsetsBottomHeight
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.ArrowBack
import androidx.compose.material.icons.rounded.SwipeLeft
import androidx.compose.material.icons.rounded.SwipeRight
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
Expand All @@ -32,12 +30,14 @@ import me.ash.reader.infrastructure.preference.LocalInitialFilter
import me.ash.reader.infrastructure.preference.LocalInitialPage
import me.ash.reader.infrastructure.preference.LocalOpenLink
import me.ash.reader.infrastructure.preference.LocalOpenLinkSpecificBrowser
import me.ash.reader.infrastructure.preference.LocalPullToSwitchArticle
import me.ash.reader.infrastructure.preference.OpenLinkPreference
import me.ash.reader.infrastructure.preference.SwipeEndActionPreference
import me.ash.reader.infrastructure.preference.SwipeStartActionPreference
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.RYSwitch
import me.ash.reader.ui.component.base.RadioDialog
import me.ash.reader.ui.component.base.RadioDialogOption
import me.ash.reader.ui.component.base.Subtitle
Expand All @@ -54,6 +54,7 @@ fun InteractionPage(
val initialFilter = LocalInitialFilter.current
val swipeToStartAction = LocalArticleListSwipeStartAction.current
val swipeToEndAction = LocalArticleListSwipeEndAction.current
val pullToSwitchArticle = LocalPullToSwitchArticle.current
val openLink = LocalOpenLink.current
val openLinkSpecificBrowser = LocalOpenLinkSpecificBrowser.current
val scope = rememberCoroutineScope()
Expand Down Expand Up @@ -124,6 +125,17 @@ fun InteractionPage(
},
) {}

Subtitle(
modifier = Modifier.padding(horizontal = 24.dp),
text = stringResource(R.string.reading_page),
)

SettingItem(
title = stringResource(id = R.string.pull_to_switch_article),
onClick = { pullToSwitchArticle.toggle(context, scope) }) {
RYSwitch(activated = pullToSwitchArticle.value)
}

Subtitle(
modifier = Modifier.padding(horizontal = 24.dp),
text = stringResource(R.string.external_links),
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -420,4 +420,5 @@
<string name="toggle_read">Toggle read</string>
<string name="toggle_starred">Toggle starred</string>
<string name="export">Export</string>
<string name="pull_to_switch_article">Pull to switch article</string>
</resources>

0 comments on commit a187d38

Please sign in to comment.