feat: Allow disabling Gravatar icons
This commit is contained in:
parent
b8cd84a7f4
commit
8f79db1fe7
|
@ -81,6 +81,8 @@ interface SettingsReadRepository {
|
|||
|
||||
fun getKeepScreenOn(): Flow<Boolean>
|
||||
|
||||
fun getGravatar(): Flow<Boolean>
|
||||
|
||||
fun getAllowTwoPanelLayoutInPortrait(): Flow<Boolean>
|
||||
|
||||
fun getAllowTwoPanelLayoutInLandscape(): Flow<Boolean>
|
||||
|
|
|
@ -149,6 +149,10 @@ interface SettingsReadWriteRepository : SettingsReadRepository {
|
|||
keepScreenOn: Boolean,
|
||||
): IO<Unit>
|
||||
|
||||
fun setGravatar(
|
||||
enabled: Boolean,
|
||||
): IO<Unit>
|
||||
|
||||
fun setAllowTwoPanelLayoutInPortrait(
|
||||
allow: Boolean,
|
||||
): IO<Unit>
|
||||
|
|
|
@ -74,6 +74,7 @@ class SettingsRepositoryImpl(
|
|||
private const val KEY_THEME_USE_AMOLED_DARK = "theme_use_amoled_dark"
|
||||
private const val KEY_ONBOARDING_LAST_VISIT = "onboarding_last_visit"
|
||||
private const val KEY_KEEP_SCREEN_ON = "keep_screen_on"
|
||||
private const val KEY_GRAVATAR = "gravatar"
|
||||
private const val KEY_COLORS = "colors"
|
||||
private const val KEY_LOCALE = "locale"
|
||||
|
||||
|
@ -158,6 +159,9 @@ class SettingsRepositoryImpl(
|
|||
private val keepScreenOnPref =
|
||||
store.getBoolean(KEY_KEEP_SCREEN_ON, true)
|
||||
|
||||
private val gravatarPref =
|
||||
store.getBoolean(KEY_GRAVATAR, true)
|
||||
|
||||
private val navLabelPref =
|
||||
store.getBoolean(KEY_NAV_LABEL, true)
|
||||
|
||||
|
@ -409,6 +413,11 @@ class SettingsRepositoryImpl(
|
|||
|
||||
override fun getKeepScreenOn() = keepScreenOnPref
|
||||
|
||||
override fun setGravatar(enabled: Boolean) = gravatarPref
|
||||
.setAndCommit(enabled)
|
||||
|
||||
override fun getGravatar() = gravatarPref
|
||||
|
||||
override fun setAllowTwoPanelLayoutInLandscape(allow: Boolean) =
|
||||
allowTwoPanelLayoutInLandscapePref
|
||||
.setAndCommit(allow)
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
package com.artemchep.keyguard.common.usecase
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface GetGravatar : () -> Flow<Boolean>
|
|
@ -0,0 +1,5 @@
|
|||
package com.artemchep.keyguard.common.usecase
|
||||
|
||||
import com.artemchep.keyguard.common.io.IO
|
||||
|
||||
interface PutGravatar : (Boolean) -> IO<Unit>
|
|
@ -0,0 +1,21 @@
|
|||
package com.artemchep.keyguard.common.usecase.impl
|
||||
|
||||
import com.artemchep.keyguard.common.service.settings.SettingsReadRepository
|
||||
import com.artemchep.keyguard.common.usecase.GetGravatar
|
||||
import com.artemchep.keyguard.common.usecase.GetKeepScreenOn
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import org.kodein.di.DirectDI
|
||||
import org.kodein.di.instance
|
||||
|
||||
class GetGravatarImpl(
|
||||
settingsReadRepository: SettingsReadRepository,
|
||||
) : GetGravatar {
|
||||
private val sharedFlow = settingsReadRepository.getGravatar()
|
||||
.distinctUntilChanged()
|
||||
|
||||
constructor(directDI: DirectDI) : this(
|
||||
settingsReadRepository = directDI.instance(),
|
||||
)
|
||||
|
||||
override fun invoke() = sharedFlow
|
||||
}
|
|
@ -3,26 +3,38 @@ package com.artemchep.keyguard.common.usecase.impl
|
|||
import com.artemchep.keyguard.common.io.IO
|
||||
import com.artemchep.keyguard.common.io.ioEffect
|
||||
import com.artemchep.keyguard.common.service.crypto.CryptoGenerator
|
||||
import com.artemchep.keyguard.common.usecase.GetGravatar
|
||||
import com.artemchep.keyguard.common.usecase.GetGravatarUrl
|
||||
import com.artemchep.keyguard.feature.favicon.GravatarUrl
|
||||
import io.ktor.util.hex
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.first
|
||||
import org.kodein.di.DirectDI
|
||||
import org.kodein.di.instance
|
||||
import java.util.Locale
|
||||
|
||||
class GetGravatarUrlImpl(
|
||||
private val cryptoGenerator: CryptoGenerator,
|
||||
private val getGravatar: GetGravatar,
|
||||
) : GetGravatarUrl {
|
||||
private val emailPlusAddressingRegex = "\\+.+(?=@)".toRegex()
|
||||
|
||||
class GravatarDisabledException : RuntimeException()
|
||||
|
||||
constructor(directDI: DirectDI) : this(
|
||||
cryptoGenerator = directDI.instance(),
|
||||
getGravatar = directDI.instance(),
|
||||
)
|
||||
|
||||
override fun invoke(
|
||||
email: String,
|
||||
): IO<GravatarUrl> = ioEffect(Dispatchers.Default) {
|
||||
val gravatarEnabled = getGravatar()
|
||||
.first()
|
||||
if (!gravatarEnabled) {
|
||||
throw GravatarDisabledException()
|
||||
}
|
||||
|
||||
val emailHash = run {
|
||||
// https://en.gravatar.com/site/implement/hash/
|
||||
val sanitizedEmail = transformEmail(email)
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
package com.artemchep.keyguard.common.usecase.impl
|
||||
|
||||
import com.artemchep.keyguard.common.io.IO
|
||||
import com.artemchep.keyguard.common.service.settings.SettingsReadWriteRepository
|
||||
import com.artemchep.keyguard.common.usecase.PutGravatar
|
||||
import org.kodein.di.DirectDI
|
||||
import org.kodein.di.instance
|
||||
|
||||
class PutGravatarImpl(
|
||||
private val settingsReadWriteRepository: SettingsReadWriteRepository,
|
||||
) : PutGravatar {
|
||||
constructor(directDI: DirectDI) : this(
|
||||
settingsReadWriteRepository = directDI.instance(),
|
||||
)
|
||||
|
||||
override fun invoke(enabled: Boolean): IO<Unit> = settingsReadWriteRepository
|
||||
.setGravatar(enabled)
|
||||
}
|
|
@ -57,6 +57,7 @@ import com.artemchep.keyguard.feature.home.settings.component.settingFeaturesOve
|
|||
import com.artemchep.keyguard.feature.home.settings.component.settingFeedbackAppProvider
|
||||
import com.artemchep.keyguard.feature.home.settings.component.settingFontProvider
|
||||
import com.artemchep.keyguard.feature.home.settings.component.settingGitHubProvider
|
||||
import com.artemchep.keyguard.feature.home.settings.component.settingGravatarProvider
|
||||
import com.artemchep.keyguard.feature.home.settings.component.settingKeepScreenOnProvider
|
||||
import com.artemchep.keyguard.feature.home.settings.component.settingLaunchAppPicker
|
||||
import com.artemchep.keyguard.feature.home.settings.component.settingLaunchYubiKey
|
||||
|
@ -150,6 +151,7 @@ object Setting {
|
|||
const val REDDIT = "reddit"
|
||||
const val CROWDIN = "crowdin"
|
||||
const val GITHUB = "github"
|
||||
const val GRAVATAR = "gravatar"
|
||||
const val PRIVACY_POLICY = "privacy_policy"
|
||||
const val OPEN_SOURCE_LICENSES = "open_source_licenses"
|
||||
const val ABOUT_APP = "about_app"
|
||||
|
@ -237,6 +239,7 @@ val hub = mapOf<String, (DirectDI) -> SettingComponent>(
|
|||
Setting.REDDIT to ::settingAboutTelegramProvider,
|
||||
Setting.CROWDIN to ::settingLocalizationProvider,
|
||||
Setting.GITHUB to ::settingGitHubProvider,
|
||||
Setting.GRAVATAR to ::settingGravatarProvider,
|
||||
Setting.PRIVACY_POLICY to ::settingPrivacyPolicyProvider,
|
||||
Setting.OPEN_SOURCE_LICENSES to ::settingOpenSourceLicensesProvider,
|
||||
Setting.EXPERIMENTAL to ::settingExperimentalProvider,
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
package com.artemchep.keyguard.feature.home.settings.component
|
||||
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.AccountCircle
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.LocalMinimumInteractiveComponentEnforcement
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import arrow.core.partially1
|
||||
import com.artemchep.keyguard.common.io.launchIn
|
||||
import com.artemchep.keyguard.common.usecase.GetGravatar
|
||||
import com.artemchep.keyguard.common.usecase.PutGravatar
|
||||
import com.artemchep.keyguard.common.usecase.WindowCoroutineScope
|
||||
import com.artemchep.keyguard.res.Res
|
||||
import com.artemchep.keyguard.ui.FlatItem
|
||||
import compose.icons.FeatherIcons
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
import kotlinx.coroutines.flow.map
|
||||
import org.kodein.di.DirectDI
|
||||
import org.kodein.di.instance
|
||||
|
||||
fun settingGravatarProvider(
|
||||
directDI: DirectDI,
|
||||
) = settingGravatarProvider(
|
||||
getGravatar = directDI.instance(),
|
||||
putGravatar = directDI.instance(),
|
||||
windowCoroutineScope = directDI.instance(),
|
||||
)
|
||||
|
||||
fun settingGravatarProvider(
|
||||
getGravatar: GetGravatar,
|
||||
putGravatar: PutGravatar,
|
||||
windowCoroutineScope: WindowCoroutineScope,
|
||||
): SettingComponent = getGravatar().map { gravatar ->
|
||||
val onCheckedChange = { shouldBeEnabled: Boolean ->
|
||||
putGravatar(shouldBeEnabled)
|
||||
.launchIn(windowCoroutineScope)
|
||||
Unit
|
||||
}
|
||||
|
||||
SettingIi(
|
||||
search = SettingIi.Search(
|
||||
group = "ui",
|
||||
tokens = listOf(
|
||||
"gravatar",
|
||||
),
|
||||
),
|
||||
) {
|
||||
SettingGravatar(
|
||||
checked = gravatar,
|
||||
onCheckedChange = onCheckedChange,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SettingGravatar(
|
||||
checked: Boolean,
|
||||
onCheckedChange: ((Boolean) -> Unit)?,
|
||||
) {
|
||||
FlatItem(
|
||||
leading = {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.AccountCircle,
|
||||
contentDescription = null,
|
||||
)
|
||||
},
|
||||
trailing = {
|
||||
CompositionLocalProvider(
|
||||
LocalMinimumInteractiveComponentEnforcement provides false,
|
||||
) {
|
||||
Switch(
|
||||
checked = checked,
|
||||
enabled = onCheckedChange != null,
|
||||
onCheckedChange = onCheckedChange,
|
||||
)
|
||||
}
|
||||
},
|
||||
title = {
|
||||
Text(
|
||||
text = stringResource(Res.strings.pref_item_load_gravatar_icons_title),
|
||||
)
|
||||
},
|
||||
onClick = onCheckedChange?.partially1(!checked),
|
||||
)
|
||||
}
|
|
@ -35,6 +35,7 @@ fun UiSettingsScreen() {
|
|||
list = listOf(
|
||||
SettingPaneItem.Item(Setting.APP_ICONS),
|
||||
SettingPaneItem.Item(Setting.WEBSITE_ICONS),
|
||||
SettingPaneItem.Item(Setting.GRAVATAR),
|
||||
),
|
||||
),
|
||||
SettingPaneItem.Group(
|
||||
|
|
|
@ -30,6 +30,7 @@ fun SecuritySettingsScreen() {
|
|||
SettingPaneItem.Item(Setting.CONCEAL),
|
||||
SettingPaneItem.Item(Setting.SCREENSHOTS),
|
||||
SettingPaneItem.Item(Setting.WEBSITE_ICONS),
|
||||
SettingPaneItem.Item(Setting.GRAVATAR),
|
||||
),
|
||||
),
|
||||
SettingPaneItem.Group(
|
||||
|
|
|
@ -966,6 +966,7 @@
|
|||
<string name="pref_item_load_app_icons_title">Load App icons</string>
|
||||
<string name="pref_item_load_website_icons_title">Load Website icons</string>
|
||||
<string name="pref_item_load_website_icons_text">Queuing website icon might leak the website address to internet provider</string>
|
||||
<string name="pref_item_load_gravatar_icons_title">Load Gravatar icons</string>
|
||||
<string name="pref_item_markdown_title">Rich text formatting</string>
|
||||
<string name="pref_item_markdown_text">Use Markdown to format notes</string>
|
||||
<string name="pref_item_locale_title">Language</string>
|
||||
|
|
|
@ -120,6 +120,7 @@ import com.artemchep.keyguard.common.usecase.GetDebugPremium
|
|||
import com.artemchep.keyguard.common.usecase.GetDebugScreenDelay
|
||||
import com.artemchep.keyguard.common.usecase.GetFont
|
||||
import com.artemchep.keyguard.common.usecase.GetFontVariants
|
||||
import com.artemchep.keyguard.common.usecase.GetGravatar
|
||||
import com.artemchep.keyguard.common.usecase.GetGravatarUrl
|
||||
import com.artemchep.keyguard.common.usecase.GetJustDeleteMeByUrl
|
||||
import com.artemchep.keyguard.common.usecase.GetJustGetMyDataByUrl
|
||||
|
@ -177,6 +178,7 @@ import com.artemchep.keyguard.common.usecase.PutConcealFields
|
|||
import com.artemchep.keyguard.common.usecase.PutDebugPremium
|
||||
import com.artemchep.keyguard.common.usecase.PutDebugScreenDelay
|
||||
import com.artemchep.keyguard.common.usecase.PutFont
|
||||
import com.artemchep.keyguard.common.usecase.PutGravatar
|
||||
import com.artemchep.keyguard.common.usecase.PutKeepScreenOn
|
||||
import com.artemchep.keyguard.common.usecase.PutMarkdown
|
||||
import com.artemchep.keyguard.common.usecase.PutNavAnimation
|
||||
|
@ -244,6 +246,7 @@ import com.artemchep.keyguard.common.usecase.impl.GetDebugPremiumImpl
|
|||
import com.artemchep.keyguard.common.usecase.impl.GetDebugScreenDelayImpl
|
||||
import com.artemchep.keyguard.common.usecase.impl.GetFontImpl
|
||||
import com.artemchep.keyguard.common.usecase.impl.GetFontVariantsImpl
|
||||
import com.artemchep.keyguard.common.usecase.impl.GetGravatarImpl
|
||||
import com.artemchep.keyguard.common.usecase.impl.GetGravatarUrlImpl
|
||||
import com.artemchep.keyguard.common.usecase.impl.GetJustDeleteMeByUrlImpl
|
||||
import com.artemchep.keyguard.common.usecase.impl.GetJustGetMyDataByUrlImpl
|
||||
|
@ -298,6 +301,7 @@ import com.artemchep.keyguard.common.usecase.impl.PutConcealFieldsImpl
|
|||
import com.artemchep.keyguard.common.usecase.impl.PutDebugPremiumImpl
|
||||
import com.artemchep.keyguard.common.usecase.impl.PutDebugScreenDelayImpl
|
||||
import com.artemchep.keyguard.common.usecase.impl.PutFontImpl
|
||||
import com.artemchep.keyguard.common.usecase.impl.PutGravatarImpl
|
||||
import com.artemchep.keyguard.common.usecase.impl.PutKeepScreenOnImpl
|
||||
import com.artemchep.keyguard.common.usecase.impl.PutMarkdownImpl
|
||||
import com.artemchep.keyguard.common.usecase.impl.PutNavAnimationImpl
|
||||
|
@ -865,6 +869,16 @@ fun globalModuleJvm() = DI.Module(
|
|||
directDI = this,
|
||||
)
|
||||
}
|
||||
bindSingleton<GetGravatar> {
|
||||
GetGravatarImpl(
|
||||
directDI = this,
|
||||
)
|
||||
}
|
||||
bindSingleton<PutGravatar> {
|
||||
PutGravatarImpl(
|
||||
directDI = this,
|
||||
)
|
||||
}
|
||||
bindSingleton<GetJustDeleteMeByUrl> {
|
||||
GetJustDeleteMeByUrlImpl(
|
||||
directDI = this,
|
||||
|
|
Loading…
Reference in New Issue