Add a one time popup asking for donation from long time users (#3203)
Also remove the empty "Updated to X.Y.Z" popup after app update
This commit is contained in:
@@ -13,6 +13,7 @@ The format is a modified version of [Keep a Changelog](https://keepachangelog.co
|
||||
## [Unreleased]
|
||||
### Added
|
||||
- Add dedicated "Support Us" screen ([@AntsyLich](https://github.com/AntsyLich)) ([#3200](https://github.com/mihonapp/mihon/pull/3200))
|
||||
- Add a one time popup asking for donation from long time users ([@AntsyLich](https://github.com/AntsyLich)) ([#3203](https://github.com/mihonapp/mihon/pull/3203))
|
||||
|
||||
### Changed
|
||||
- Don't prompt users to enable Google Play services if disabled or unavailable ([@leodyversemilla07](https://github.com/leodyversemilla07)) ([#3152](https://github.com/mihonapp/mihon/pull/3152))
|
||||
@@ -25,6 +26,9 @@ The format is a modified version of [Keep a Changelog](https://keepachangelog.co
|
||||
### Fixed
|
||||
- Fix app trying to split long strip when not needed ([@leodyversemilla07](https://github.com/leodyversemilla07)) ([#3121](https://github.com/mihonapp/mihon/pull/3121))
|
||||
|
||||
### Removed
|
||||
- Remove the empty "Updated to X.Y.Z" popup after app update ([@AntsyLich](https://github.com/AntsyLich)) ([#3203](https://github.com/mihonapp/mihon/pull/3203))
|
||||
|
||||
## [v0.19.7] - 2026-03-23
|
||||
Same as v0.19.6
|
||||
|
||||
|
||||
@@ -46,4 +46,9 @@ class BasePreferences(
|
||||
)
|
||||
|
||||
val installationId: Preference<String> = preferenceStore.getString(Preference.appStateKey("installation_id"), "")
|
||||
|
||||
val donationCampaignShown: Preference<Boolean> = preferenceStore.getBoolean(
|
||||
Preference.appStateKey("donation_campaign_shown"),
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ fun NavigatorAdaptiveSheet(
|
||||
content = { sheetNavigator ->
|
||||
AdaptiveSheet(
|
||||
onDismissRequest = onDismissRequest,
|
||||
enableSwipeDismiss = enableSwipeDismiss(sheetNavigator),
|
||||
enableImplicitDismiss = enableSwipeDismiss(sheetNavigator),
|
||||
) {
|
||||
ScreenTransition(
|
||||
navigator = sheetNavigator,
|
||||
@@ -63,7 +63,7 @@ fun NavigatorAdaptiveSheet(
|
||||
fun AdaptiveSheet(
|
||||
onDismissRequest: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
enableSwipeDismiss: Boolean = true,
|
||||
enableImplicitDismiss: Boolean = true,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
val isTabletUi = isTabletUi()
|
||||
@@ -74,7 +74,7 @@ fun AdaptiveSheet(
|
||||
) {
|
||||
AdaptiveSheetImpl(
|
||||
isTabletUi = isTabletUi,
|
||||
enableSwipeDismiss = enableSwipeDismiss,
|
||||
enableImplicitDismiss = enableImplicitDismiss,
|
||||
onDismissRequest = onDismissRequest,
|
||||
modifier = modifier,
|
||||
) {
|
||||
|
||||
@@ -5,6 +5,7 @@ import android.app.SearchManager
|
||||
import android.app.assist.AssistContent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.Color
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
@@ -14,21 +15,32 @@ import androidx.activity.SystemBarStyle
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.WindowInsetsSides
|
||||
import androidx.compose.foundation.layout.consumeWindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.navigationBars
|
||||
import androidx.compose.foundation.layout.only
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.windowInsetsBottomHeight
|
||||
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.OpenInNew
|
||||
import androidx.compose.material.icons.filled.VolunteerActivism
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
@@ -41,6 +53,8 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.graphics.luminance
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.animation.doOnEnd
|
||||
import androidx.core.net.toUri
|
||||
import androidx.core.splashscreen.SplashScreen
|
||||
@@ -55,6 +69,7 @@ import cafe.adriel.voyager.navigator.NavigatorDisposeBehavior
|
||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||
import eu.kanade.domain.base.BasePreferences
|
||||
import eu.kanade.domain.source.interactor.GetIncognitoState
|
||||
import eu.kanade.presentation.components.AdaptiveSheet
|
||||
import eu.kanade.presentation.components.AppStateBanners
|
||||
import eu.kanade.presentation.components.DownloadedOnlyBannerBackgroundColor
|
||||
import eu.kanade.presentation.components.IncognitoModeBannerBackgroundColor
|
||||
@@ -63,12 +78,10 @@ import eu.kanade.presentation.more.settings.screen.browse.ExtensionReposScreen
|
||||
import eu.kanade.presentation.more.settings.screen.data.RestoreBackupScreen
|
||||
import eu.kanade.presentation.util.AssistContentScreen
|
||||
import eu.kanade.presentation.util.DefaultNavigatorScreenTransition
|
||||
import eu.kanade.tachiyomi.BuildConfig
|
||||
import eu.kanade.tachiyomi.data.cache.ChapterCache
|
||||
import eu.kanade.tachiyomi.data.download.DownloadCache
|
||||
import eu.kanade.tachiyomi.data.notification.NotificationReceiver
|
||||
import eu.kanade.tachiyomi.data.updater.AppUpdateChecker
|
||||
import eu.kanade.tachiyomi.data.updater.RELEASE_URL
|
||||
import eu.kanade.tachiyomi.extension.api.ExtensionApi
|
||||
import eu.kanade.tachiyomi.ui.base.activity.BaseActivity
|
||||
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceScreen
|
||||
@@ -80,7 +93,6 @@ import eu.kanade.tachiyomi.ui.more.NewUpdateScreen
|
||||
import eu.kanade.tachiyomi.ui.more.OnboardingScreen
|
||||
import eu.kanade.tachiyomi.util.system.dpToPx
|
||||
import eu.kanade.tachiyomi.util.system.isNavigationBarNeedsScrim
|
||||
import eu.kanade.tachiyomi.util.system.openInBrowser
|
||||
import eu.kanade.tachiyomi.util.system.updaterEnabled
|
||||
import eu.kanade.tachiyomi.util.view.setComposeContent
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
@@ -93,6 +105,7 @@ import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import logcat.LogPriority
|
||||
import mihon.core.migration.Migrator
|
||||
import mihon.feature.support.SupportUsScreen
|
||||
import tachiyomi.core.common.Constants
|
||||
import tachiyomi.core.common.util.lang.launchIO
|
||||
import tachiyomi.core.common.util.system.logcat
|
||||
@@ -100,9 +113,14 @@ import tachiyomi.domain.library.service.LibraryPreferences
|
||||
import tachiyomi.domain.release.interactor.GetApplicationRelease
|
||||
import tachiyomi.i18n.MR
|
||||
import tachiyomi.presentation.core.components.material.Scaffold
|
||||
import tachiyomi.presentation.core.components.material.padding
|
||||
import tachiyomi.presentation.core.i18n.stringResource
|
||||
import tachiyomi.presentation.core.util.collectAsState
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import kotlin.time.Clock
|
||||
import kotlin.time.Duration.Companion.days
|
||||
import kotlin.time.Instant
|
||||
import kotlin.time.times
|
||||
|
||||
class MainActivity : BaseActivity() {
|
||||
|
||||
@@ -131,7 +149,7 @@ class MainActivity : BaseActivity() {
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
val didMigration = Migrator.awaitAndRelease()
|
||||
Migrator.awaitAndRelease()
|
||||
|
||||
// Do not let the launcher create a new activity http://stackoverflow.com/questions/16283079
|
||||
if (!isTaskRoot) {
|
||||
@@ -240,24 +258,7 @@ class MainActivity : BaseActivity() {
|
||||
|
||||
CheckForUpdates()
|
||||
ShowOnboarding()
|
||||
}
|
||||
|
||||
var showChangelog by remember { mutableStateOf(didMigration && !BuildConfig.DEBUG) }
|
||||
if (showChangelog) {
|
||||
AlertDialog(
|
||||
onDismissRequest = { showChangelog = false },
|
||||
title = { Text(text = stringResource(MR.strings.updated_version, BuildConfig.VERSION_NAME)) },
|
||||
dismissButton = {
|
||||
TextButton(onClick = { openInBrowser(RELEASE_URL) }) {
|
||||
Text(text = stringResource(MR.strings.whats_new))
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(onClick = { showChangelog = false }) {
|
||||
Text(text = stringResource(MR.strings.action_ok))
|
||||
}
|
||||
},
|
||||
)
|
||||
ShowDonationCampaign()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -343,6 +344,128 @@ class MainActivity : BaseActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ShowDonationCampaign() {
|
||||
val navigator = LocalNavigator.currentOrThrow
|
||||
|
||||
var showCampaign by remember { mutableStateOf(false) }
|
||||
if (showCampaign) {
|
||||
val uriHandler = LocalUriHandler.current
|
||||
val dismissSupportMessage = {
|
||||
preferences.donationCampaignShown.set(true)
|
||||
@Suppress("AssignedValueIsNeverRead")
|
||||
showCampaign = false
|
||||
}
|
||||
AdaptiveSheet(
|
||||
onDismissRequest = dismissSupportMessage,
|
||||
enableImplicitDismiss = false,
|
||||
) {
|
||||
Column {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(16.dp)
|
||||
.weight(1f, fill = false)
|
||||
.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(MR.strings.donationCampaign_title),
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
style = MaterialTheme.typography.headlineSmall,
|
||||
)
|
||||
Text(
|
||||
text = stringResource(MR.strings.donationCampaign_paragraph1),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
)
|
||||
Text(
|
||||
text = stringResource(MR.strings.donationCampaign_paragraph2),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
)
|
||||
Text(
|
||||
text = stringResource(MR.strings.donationCampaign_paragraph3),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
)
|
||||
}
|
||||
|
||||
HorizontalDivider()
|
||||
|
||||
Button(
|
||||
modifier = Modifier
|
||||
.padding(top = MaterialTheme.padding.small)
|
||||
.padding(horizontal = MaterialTheme.padding.medium)
|
||||
.fillMaxWidth(),
|
||||
onClick = {
|
||||
navigator.push(SupportUsScreen())
|
||||
dismissSupportMessage()
|
||||
},
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small),
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.VolunteerActivism,
|
||||
contentDescription = null,
|
||||
)
|
||||
Text(
|
||||
text = stringResource(MR.strings.label_support_us),
|
||||
color = MaterialTheme.colorScheme.onPrimary,
|
||||
)
|
||||
}
|
||||
}
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small),
|
||||
modifier = Modifier
|
||||
.padding(bottom = MaterialTheme.padding.small)
|
||||
.padding(horizontal = MaterialTheme.padding.medium),
|
||||
) {
|
||||
OutlinedButton(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(1f),
|
||||
onClick = { uriHandler.openUri(Constants.URL_DISCORD) },
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(MR.strings.donationCampaign_contactPlatform),
|
||||
)
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Default.OpenInNew,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
OutlinedButton(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(1f),
|
||||
onClick = dismissSupportMessage,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(MR.strings.donationCampaign_dismiss),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
try {
|
||||
val firstInstallTime = packageManager.getPackageInfo(packageName, 0).firstInstallTime
|
||||
val eligibleTime = Instant.fromEpochMilliseconds(firstInstallTime).plus(6 * 30.days)
|
||||
@Suppress("AssignedValueIsNeverRead")
|
||||
showCampaign = (Clock.System.now() >= eligibleTime && !preferences.donationCampaignShown.get())
|
||||
} catch (_: PackageManager.NameNotFoundException) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets custom splash screen exit animation on devices prior to Android 12.
|
||||
*
|
||||
|
||||
@@ -648,7 +648,6 @@
|
||||
<string name="licenses">Open source licenses</string>
|
||||
<string name="privacy_policy">Privacy policy</string>
|
||||
<string name="check_for_updates">Check for updates</string>
|
||||
<string name="updated_version">Updated to v%1$s</string>
|
||||
|
||||
<!-- ACRA -->
|
||||
<string name="pref_enable_acra">Send crash reports</string>
|
||||
@@ -1054,4 +1053,11 @@
|
||||
<string name="supportUsScreen.currentlySupportedBy">Mihon is currently backed by %d+ patrons. We want to thank each and everyone of you. Let's make Mihon even better together.</string>
|
||||
<string name="supportUsScreen.contactForDetailsMessage">For alternative options or any related questions, join our Discord server.</string>
|
||||
<string name="supportUsScreen.contactPlatform">Discord</string>
|
||||
|
||||
<string name="donationCampaign.title">Enjoying the app?</string>
|
||||
<string name="donationCampaign.paragraph1">Hi! I’m the main developer behind Mihon, and I’d like to ask you to consider supporting its development financially. With enough support, I’ll be able to work on Mihon full-time and focus on long-standing issues as well as highly requested features.</string>
|
||||
<string name="donationCampaign.paragraph2">You can donate through either Patreon (preferred) or OpenCollective. If you have questions or would like to support using alternative methods, feel free to reach out to me through Mihon's Discord server.</string>
|
||||
<string name="donationCampaign.paragraph3">Regardless, Mihon will continue to be maintained by me and the volunteers as well as stay open source.</string>
|
||||
<string name="donationCampaign.contactPlatform">Discord</string>
|
||||
<string name="donationCampaign.dismiss">Dismiss</string>
|
||||
</resources>
|
||||
|
||||
+6
-5
@@ -52,7 +52,7 @@ import kotlin.math.roundToInt
|
||||
@Composable
|
||||
fun AdaptiveSheet(
|
||||
isTabletUi: Boolean,
|
||||
enableSwipeDismiss: Boolean,
|
||||
enableImplicitDismiss: Boolean,
|
||||
onDismissRequest: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
content: @Composable () -> Unit,
|
||||
@@ -77,6 +77,7 @@ fun AdaptiveSheet(
|
||||
.clickable(
|
||||
interactionSource = null,
|
||||
indication = null,
|
||||
enabled = enableImplicitDismiss,
|
||||
onClick = internalOnDismissRequest,
|
||||
)
|
||||
.fillMaxSize()
|
||||
@@ -98,7 +99,7 @@ fun AdaptiveSheet(
|
||||
color = MaterialTheme.colorScheme.surfaceContainerHigh,
|
||||
content = {
|
||||
BackHandler(
|
||||
enabled = remember { derivedStateOf { alpha > 0f } }.value,
|
||||
enabled = enableImplicitDismiss && remember { derivedStateOf { alpha > 0f } }.value,
|
||||
onBack = internalOnDismissRequest,
|
||||
)
|
||||
content()
|
||||
@@ -149,7 +150,7 @@ fun AdaptiveSheet(
|
||||
onClick = {},
|
||||
)
|
||||
.then(
|
||||
if (enableSwipeDismiss) {
|
||||
if (enableImplicitDismiss) {
|
||||
Modifier.nestedScroll(
|
||||
remember(anchoredDraggableState) {
|
||||
anchoredDraggableState.preUpPostDownNestedScrollConnection {
|
||||
@@ -174,7 +175,7 @@ fun AdaptiveSheet(
|
||||
.anchoredDraggable(
|
||||
state = anchoredDraggableState,
|
||||
orientation = Orientation.Vertical,
|
||||
enabled = enableSwipeDismiss,
|
||||
enabled = enableImplicitDismiss,
|
||||
flingBehavior = flingBehavior,
|
||||
)
|
||||
.navigationBarsPadding()
|
||||
@@ -183,7 +184,7 @@ fun AdaptiveSheet(
|
||||
color = MaterialTheme.colorScheme.surfaceContainerHigh,
|
||||
content = {
|
||||
BackHandler(
|
||||
enabled = anchoredDraggableState.targetValue == 0,
|
||||
enabled = enableImplicitDismiss && anchoredDraggableState.targetValue == 0,
|
||||
onBack = internalOnDismissRequest,
|
||||
)
|
||||
content()
|
||||
|
||||
Reference in New Issue
Block a user