From 8f79db1fe72f2cfe257582720e45ae9fdd862771 Mon Sep 17 00:00:00 2001 From: Artem Chepurnoy Date: Wed, 10 Apr 2024 12:21:23 +0300 Subject: [PATCH] feat: Allow disabling Gravatar icons --- .../settings/SettingsReadRepository.kt | 2 + .../settings/SettingsReadWriteRepository.kt | 4 + .../settings/impl/SettingsRepositoryImpl.kt | 9 ++ .../keyguard/common/usecase/GetGravatar.kt | 5 ++ .../keyguard/common/usecase/PutGravatar.kt | 5 ++ .../common/usecase/impl/GetGravatarImpl.kt | 21 +++++ .../common/usecase/impl/GetGravatarUrlImpl.kt | 12 +++ .../common/usecase/impl/PutGravatarImpl.kt | 18 ++++ .../home/settings/SettingPaneContent.kt | 3 + .../settings/component/SettingGravatar.kt | 88 +++++++++++++++++++ .../home/settings/display/UiSettingsScreen.kt | 1 + .../security/SecuritySettingsScreen.kt | 1 + .../commonMain/resources/MR/base/strings.xml | 1 + .../artemchep/keyguard/di/GlobalModuleJvm.kt | 14 +++ 14 files changed, 184 insertions(+) create mode 100644 common/src/commonMain/kotlin/com/artemchep/keyguard/common/usecase/GetGravatar.kt create mode 100644 common/src/commonMain/kotlin/com/artemchep/keyguard/common/usecase/PutGravatar.kt create mode 100644 common/src/commonMain/kotlin/com/artemchep/keyguard/common/usecase/impl/GetGravatarImpl.kt create mode 100644 common/src/commonMain/kotlin/com/artemchep/keyguard/common/usecase/impl/PutGravatarImpl.kt create mode 100644 common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/settings/component/SettingGravatar.kt diff --git a/common/src/commonMain/kotlin/com/artemchep/keyguard/common/service/settings/SettingsReadRepository.kt b/common/src/commonMain/kotlin/com/artemchep/keyguard/common/service/settings/SettingsReadRepository.kt index 59d5c2d1..8f95f96a 100644 --- a/common/src/commonMain/kotlin/com/artemchep/keyguard/common/service/settings/SettingsReadRepository.kt +++ b/common/src/commonMain/kotlin/com/artemchep/keyguard/common/service/settings/SettingsReadRepository.kt @@ -81,6 +81,8 @@ interface SettingsReadRepository { fun getKeepScreenOn(): Flow + fun getGravatar(): Flow + fun getAllowTwoPanelLayoutInPortrait(): Flow fun getAllowTwoPanelLayoutInLandscape(): Flow diff --git a/common/src/commonMain/kotlin/com/artemchep/keyguard/common/service/settings/SettingsReadWriteRepository.kt b/common/src/commonMain/kotlin/com/artemchep/keyguard/common/service/settings/SettingsReadWriteRepository.kt index 4d2f7c97..ae720e10 100644 --- a/common/src/commonMain/kotlin/com/artemchep/keyguard/common/service/settings/SettingsReadWriteRepository.kt +++ b/common/src/commonMain/kotlin/com/artemchep/keyguard/common/service/settings/SettingsReadWriteRepository.kt @@ -149,6 +149,10 @@ interface SettingsReadWriteRepository : SettingsReadRepository { keepScreenOn: Boolean, ): IO + fun setGravatar( + enabled: Boolean, + ): IO + fun setAllowTwoPanelLayoutInPortrait( allow: Boolean, ): IO diff --git a/common/src/commonMain/kotlin/com/artemchep/keyguard/common/service/settings/impl/SettingsRepositoryImpl.kt b/common/src/commonMain/kotlin/com/artemchep/keyguard/common/service/settings/impl/SettingsRepositoryImpl.kt index 367ed75f..17bfd251 100644 --- a/common/src/commonMain/kotlin/com/artemchep/keyguard/common/service/settings/impl/SettingsRepositoryImpl.kt +++ b/common/src/commonMain/kotlin/com/artemchep/keyguard/common/service/settings/impl/SettingsRepositoryImpl.kt @@ -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) diff --git a/common/src/commonMain/kotlin/com/artemchep/keyguard/common/usecase/GetGravatar.kt b/common/src/commonMain/kotlin/com/artemchep/keyguard/common/usecase/GetGravatar.kt new file mode 100644 index 00000000..95177dc9 --- /dev/null +++ b/common/src/commonMain/kotlin/com/artemchep/keyguard/common/usecase/GetGravatar.kt @@ -0,0 +1,5 @@ +package com.artemchep.keyguard.common.usecase + +import kotlinx.coroutines.flow.Flow + +interface GetGravatar : () -> Flow diff --git a/common/src/commonMain/kotlin/com/artemchep/keyguard/common/usecase/PutGravatar.kt b/common/src/commonMain/kotlin/com/artemchep/keyguard/common/usecase/PutGravatar.kt new file mode 100644 index 00000000..1b5f623f --- /dev/null +++ b/common/src/commonMain/kotlin/com/artemchep/keyguard/common/usecase/PutGravatar.kt @@ -0,0 +1,5 @@ +package com.artemchep.keyguard.common.usecase + +import com.artemchep.keyguard.common.io.IO + +interface PutGravatar : (Boolean) -> IO diff --git a/common/src/commonMain/kotlin/com/artemchep/keyguard/common/usecase/impl/GetGravatarImpl.kt b/common/src/commonMain/kotlin/com/artemchep/keyguard/common/usecase/impl/GetGravatarImpl.kt new file mode 100644 index 00000000..1defd67e --- /dev/null +++ b/common/src/commonMain/kotlin/com/artemchep/keyguard/common/usecase/impl/GetGravatarImpl.kt @@ -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 +} diff --git a/common/src/commonMain/kotlin/com/artemchep/keyguard/common/usecase/impl/GetGravatarUrlImpl.kt b/common/src/commonMain/kotlin/com/artemchep/keyguard/common/usecase/impl/GetGravatarUrlImpl.kt index dda38af7..c9a6bd70 100644 --- a/common/src/commonMain/kotlin/com/artemchep/keyguard/common/usecase/impl/GetGravatarUrlImpl.kt +++ b/common/src/commonMain/kotlin/com/artemchep/keyguard/common/usecase/impl/GetGravatarUrlImpl.kt @@ -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 = 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) diff --git a/common/src/commonMain/kotlin/com/artemchep/keyguard/common/usecase/impl/PutGravatarImpl.kt b/common/src/commonMain/kotlin/com/artemchep/keyguard/common/usecase/impl/PutGravatarImpl.kt new file mode 100644 index 00000000..0900c4ce --- /dev/null +++ b/common/src/commonMain/kotlin/com/artemchep/keyguard/common/usecase/impl/PutGravatarImpl.kt @@ -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 = settingsReadWriteRepository + .setGravatar(enabled) +} diff --git a/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/settings/SettingPaneContent.kt b/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/settings/SettingPaneContent.kt index bc4b1742..144f443b 100644 --- a/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/settings/SettingPaneContent.kt +++ b/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/settings/SettingPaneContent.kt @@ -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 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, diff --git a/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/settings/component/SettingGravatar.kt b/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/settings/component/SettingGravatar.kt new file mode 100644 index 00000000..77ccd144 --- /dev/null +++ b/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/settings/component/SettingGravatar.kt @@ -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), + ) +} diff --git a/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/settings/display/UiSettingsScreen.kt b/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/settings/display/UiSettingsScreen.kt index 08f8b633..404e8fbb 100644 --- a/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/settings/display/UiSettingsScreen.kt +++ b/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/settings/display/UiSettingsScreen.kt @@ -35,6 +35,7 @@ fun UiSettingsScreen() { list = listOf( SettingPaneItem.Item(Setting.APP_ICONS), SettingPaneItem.Item(Setting.WEBSITE_ICONS), + SettingPaneItem.Item(Setting.GRAVATAR), ), ), SettingPaneItem.Group( diff --git a/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/settings/security/SecuritySettingsScreen.kt b/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/settings/security/SecuritySettingsScreen.kt index d146daeb..9728f666 100644 --- a/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/settings/security/SecuritySettingsScreen.kt +++ b/common/src/commonMain/kotlin/com/artemchep/keyguard/feature/home/settings/security/SecuritySettingsScreen.kt @@ -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( diff --git a/common/src/commonMain/resources/MR/base/strings.xml b/common/src/commonMain/resources/MR/base/strings.xml index a8516534..8e551b41 100644 --- a/common/src/commonMain/resources/MR/base/strings.xml +++ b/common/src/commonMain/resources/MR/base/strings.xml @@ -966,6 +966,7 @@ Load App icons Load Website icons Queuing website icon might leak the website address to internet provider + Load Gravatar icons Rich text formatting Use Markdown to format notes Language diff --git a/common/src/jvmMain/kotlin/com/artemchep/keyguard/di/GlobalModuleJvm.kt b/common/src/jvmMain/kotlin/com/artemchep/keyguard/di/GlobalModuleJvm.kt index 94d4341a..eda0120d 100644 --- a/common/src/jvmMain/kotlin/com/artemchep/keyguard/di/GlobalModuleJvm.kt +++ b/common/src/jvmMain/kotlin/com/artemchep/keyguard/di/GlobalModuleJvm.kt @@ -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 { + GetGravatarImpl( + directDI = this, + ) + } + bindSingleton { + PutGravatarImpl( + directDI = this, + ) + } bindSingleton { GetJustDeleteMeByUrlImpl( directDI = this,