Add dedicated "Support Us" screen (#3200)
This commit is contained in:
@@ -11,6 +11,9 @@ The format is a modified version of [Keep a Changelog](https://keepachangelog.co
|
|||||||
- `Other` - for technical stuff.
|
- `Other` - for technical stuff.
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
### Added
|
||||||
|
- Add dedicated "Support Us" screen ([@AntsyLich](https://github.com/AntsyLich)) ([#3200](https://github.com/mihonapp/mihon/pull/3200))
|
||||||
|
|
||||||
### Changed
|
### 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))
|
- 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))
|
||||||
- Make "Support Us" more prominent in More screen ([@AntsyLich](https://github.com/AntsyLich)) ([#3196](https://github.com/mihonapp/mihon/pull/3196))
|
- Make "Support Us" more prominent in More screen ([@AntsyLich](https://github.com/AntsyLich)) ([#3196](https://github.com/mihonapp/mihon/pull/3196))
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ fun MoreScreen(
|
|||||||
onClickStats: () -> Unit,
|
onClickStats: () -> Unit,
|
||||||
onClickDataAndStorage: () -> Unit,
|
onClickDataAndStorage: () -> Unit,
|
||||||
onClickSettings: () -> Unit,
|
onClickSettings: () -> Unit,
|
||||||
|
onClickSupport: () -> Unit,
|
||||||
onClickAbout: () -> Unit,
|
onClickAbout: () -> Unit,
|
||||||
) {
|
) {
|
||||||
val uriHandler = LocalUriHandler.current
|
val uriHandler = LocalUriHandler.current
|
||||||
@@ -136,7 +137,7 @@ fun MoreScreen(
|
|||||||
TextPreferenceWidget(
|
TextPreferenceWidget(
|
||||||
title = stringResource(MR.strings.label_support_us),
|
title = stringResource(MR.strings.label_support_us),
|
||||||
icon = Icons.Default.VolunteerActivism,
|
icon = Icons.Default.VolunteerActivism,
|
||||||
onPreferenceClick = { uriHandler.openUri(Constants.URL_DONATE) },
|
onPreferenceClick = onClickSupport,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
item {
|
item {
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ import eu.kanade.tachiyomi.util.system.toast
|
|||||||
import eu.kanade.tachiyomi.util.system.updaterEnabled
|
import eu.kanade.tachiyomi.util.system.updaterEnabled
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
|
import tachiyomi.core.common.Constants
|
||||||
import tachiyomi.core.common.util.lang.withIOContext
|
import tachiyomi.core.common.util.lang.withIOContext
|
||||||
import tachiyomi.core.common.util.lang.withUIContext
|
import tachiyomi.core.common.util.lang.withUIContext
|
||||||
import tachiyomi.core.common.util.system.logcat
|
import tachiyomi.core.common.util.system.logcat
|
||||||
@@ -179,7 +180,7 @@ object AboutScreen : Screen() {
|
|||||||
LinkIcon(
|
LinkIcon(
|
||||||
label = "Discord",
|
label = "Discord",
|
||||||
icon = CustomIcons.Discord,
|
icon = CustomIcons.Discord,
|
||||||
url = "https://discord.gg/mihon",
|
url = Constants.URL_DISCORD,
|
||||||
)
|
)
|
||||||
LinkIcon(
|
LinkIcon(
|
||||||
label = "X",
|
label = "X",
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import androidx.compose.runtime.Composable
|
|||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import cafe.adriel.voyager.core.model.ScreenModel
|
import cafe.adriel.voyager.core.model.ScreenModel
|
||||||
import cafe.adriel.voyager.core.model.rememberScreenModel
|
import cafe.adriel.voyager.core.model.rememberScreenModel
|
||||||
import cafe.adriel.voyager.core.model.screenModelScope
|
import cafe.adriel.voyager.core.model.screenModelScope
|
||||||
@@ -31,6 +30,7 @@ import kotlinx.coroutines.flow.StateFlow
|
|||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
|
import mihon.feature.support.SupportUsScreen
|
||||||
import tachiyomi.core.common.util.lang.launchIO
|
import tachiyomi.core.common.util.lang.launchIO
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import tachiyomi.presentation.core.i18n.stringResource
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
@@ -57,7 +57,6 @@ data object MoreTab : Tab {
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
override fun Content() {
|
override fun Content() {
|
||||||
val context = LocalContext.current
|
|
||||||
val navigator = LocalNavigator.currentOrThrow
|
val navigator = LocalNavigator.currentOrThrow
|
||||||
val screenModel = rememberScreenModel { MoreScreenModel() }
|
val screenModel = rememberScreenModel { MoreScreenModel() }
|
||||||
val downloadQueueState by screenModel.downloadQueueState.collectAsState()
|
val downloadQueueState by screenModel.downloadQueueState.collectAsState()
|
||||||
@@ -72,6 +71,7 @@ data object MoreTab : Tab {
|
|||||||
onClickStats = { navigator.push(StatsScreen()) },
|
onClickStats = { navigator.push(StatsScreen()) },
|
||||||
onClickDataAndStorage = { navigator.push(SettingsScreen(SettingsScreen.Destination.DataAndStorage)) },
|
onClickDataAndStorage = { navigator.push(SettingsScreen(SettingsScreen.Destination.DataAndStorage)) },
|
||||||
onClickSettings = { navigator.push(SettingsScreen()) },
|
onClickSettings = { navigator.push(SettingsScreen()) },
|
||||||
|
onClickSupport = { navigator.push(SupportUsScreen()) },
|
||||||
onClickAbout = { navigator.push(SettingsScreen(SettingsScreen.Destination.About)) },
|
onClickAbout = { navigator.push(SettingsScreen(SettingsScreen.Destination.About)) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,143 @@
|
|||||||
|
package mihon.feature.support
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
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.material3.Card
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
import androidx.compose.ui.platform.LocalUriHandler
|
||||||
|
import androidx.compose.ui.unit.Dp
|
||||||
|
import androidx.compose.ui.unit.LayoutDirection
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||||
|
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||||
|
import eu.kanade.presentation.components.AppBar
|
||||||
|
import eu.kanade.presentation.more.settings.widget.TextPreferenceWidget
|
||||||
|
import eu.kanade.presentation.util.Screen
|
||||||
|
import tachiyomi.core.common.Constants
|
||||||
|
import tachiyomi.i18n.MR
|
||||||
|
import tachiyomi.presentation.core.components.material.padding
|
||||||
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
|
import tachiyomi.presentation.core.icons.CustomIcons
|
||||||
|
import tachiyomi.presentation.core.icons.Discord
|
||||||
|
import tachiyomi.presentation.core.icons.OpenCollective
|
||||||
|
import tachiyomi.presentation.core.icons.Patreon
|
||||||
|
|
||||||
|
class SupportUsScreen : Screen() {
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
override fun Content() {
|
||||||
|
val navigator = LocalNavigator.currentOrThrow
|
||||||
|
val uriHandler = LocalUriHandler.current
|
||||||
|
|
||||||
|
Scaffold(
|
||||||
|
topBar = {
|
||||||
|
AppBar(
|
||||||
|
title = stringResource(MR.strings.label_support_us),
|
||||||
|
navigateUp = navigator::pop,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
) { paddingValues ->
|
||||||
|
Column(
|
||||||
|
verticalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small),
|
||||||
|
modifier = Modifier.verticalScroll(rememberScrollState()).padding(
|
||||||
|
remember(paddingValues) {
|
||||||
|
object : PaddingValues {
|
||||||
|
override fun calculateLeftPadding(layoutDirection: LayoutDirection): Dp {
|
||||||
|
return paddingValues.calculateLeftPadding(layoutDirection) +
|
||||||
|
MaterialTheme.padding.medium
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun calculateTopPadding(): Dp {
|
||||||
|
return 0.dp
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun calculateRightPadding(layoutDirection: LayoutDirection): Dp {
|
||||||
|
return paddingValues.calculateRightPadding(layoutDirection) +
|
||||||
|
MaterialTheme.padding.medium
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun calculateBottomPadding(): Dp {
|
||||||
|
return 0.dp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
) {
|
||||||
|
Spacer(modifier = Modifier.height(paddingValues.calculateTopPadding()))
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = stringResource(MR.strings.supportUsScreen_perks),
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
modifier = Modifier.padding(horizontal = MaterialTheme.padding.medium),
|
||||||
|
)
|
||||||
|
|
||||||
|
SupportItem(
|
||||||
|
icon = CustomIcons.Patreon,
|
||||||
|
title = stringResource(MR.strings.supportUsScreen_donationPlatform_patreon),
|
||||||
|
onClick = { uriHandler.openUri(Constants.URL_DONATE_PATREON) },
|
||||||
|
)
|
||||||
|
SupportItem(
|
||||||
|
icon = CustomIcons.OpenCollective,
|
||||||
|
title = stringResource(MR.strings.supportUsScreen_donationPlatform_opencollective),
|
||||||
|
onClick = { uriHandler.openUri(Constants.URL_DONATE_OPENCOLLECTIVE) },
|
||||||
|
)
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = stringResource(MR.strings.supportUsScreen_currentlySupportedBy, 200),
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
modifier = Modifier.padding(horizontal = MaterialTheme.padding.medium),
|
||||||
|
)
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = stringResource(MR.strings.supportUsScreen_contactForDetailsMessage),
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
modifier = Modifier.padding(horizontal = MaterialTheme.padding.medium),
|
||||||
|
)
|
||||||
|
|
||||||
|
SupportItem(
|
||||||
|
icon = CustomIcons.Discord,
|
||||||
|
title = stringResource(MR.strings.supportUsScreen_contactPlatform),
|
||||||
|
onClick = { uriHandler.openUri(Constants.URL_DISCORD) },
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(paddingValues.calculateBottomPadding()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun SupportItem(
|
||||||
|
icon: ImageVector,
|
||||||
|
title: String,
|
||||||
|
onClick: () -> Unit,
|
||||||
|
) {
|
||||||
|
Card {
|
||||||
|
TextPreferenceWidget(
|
||||||
|
title = title,
|
||||||
|
icon = icon,
|
||||||
|
widget = {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.AutoMirrored.Filled.OpenInNew,
|
||||||
|
contentDescription = null,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
onPreferenceClick = onClick,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,7 +3,9 @@ package tachiyomi.core.common
|
|||||||
object Constants {
|
object Constants {
|
||||||
const val URL_HELP = "https://mihon.app/docs/guides/troubleshooting/"
|
const val URL_HELP = "https://mihon.app/docs/guides/troubleshooting/"
|
||||||
const val URL_HELP_UPCOMING = "https://mihon.app/docs/faq/updates/upcoming"
|
const val URL_HELP_UPCOMING = "https://mihon.app/docs/faq/updates/upcoming"
|
||||||
const val URL_DONATE = "https://patreon.com/mihon/membership"
|
const val URL_DONATE_PATREON = "https://patreon.com/mihon/membership"
|
||||||
|
const val URL_DONATE_OPENCOLLECTIVE = "https://opencollective.com/mihon/contribute"
|
||||||
|
const val URL_DISCORD = "https://discord.gg/mihon"
|
||||||
|
|
||||||
const val MANGA_EXTRA = "manga"
|
const val MANGA_EXTRA = "manga"
|
||||||
|
|
||||||
|
|||||||
@@ -1047,4 +1047,11 @@
|
|||||||
<string name="migrationListScreen.migrateDialog.cancelLabel">Cancel</string>
|
<string name="migrationListScreen.migrateDialog.cancelLabel">Cancel</string>
|
||||||
<string name="migrationListScreen.progressDialog.cancelLabel">Cancel</string>
|
<string name="migrationListScreen.progressDialog.cancelLabel">Cancel</string>
|
||||||
<string name="migrationListScreen.matchWithoutChapterToast">No chapters found, this entry cannot be used for migration</string>
|
<string name="migrationListScreen.matchWithoutChapterToast">No chapters found, this entry cannot be used for migration</string>
|
||||||
|
|
||||||
|
<string name="supportUsScreen.perks">Patrons get access to exclusive channels, with more perks to come.</string>
|
||||||
|
<string name="supportUsScreen.donationPlatform.patreon">Patreon (preferred)</string>
|
||||||
|
<string name="supportUsScreen.donationPlatform.opencollective">OpenCollective</string>
|
||||||
|
<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>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package tachiyomi.presentation.core.icons
|
||||||
|
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.SolidColor
|
||||||
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
import androidx.compose.ui.graphics.vector.path
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
|
@Suppress("UnusedReceiverParameter")
|
||||||
|
val CustomIcons.OpenCollective: ImageVector
|
||||||
|
get() {
|
||||||
|
if (_OpenCollective != null) {
|
||||||
|
return _OpenCollective!!
|
||||||
|
}
|
||||||
|
_OpenCollective = ImageVector.Builder(
|
||||||
|
name = "OpenCollective",
|
||||||
|
defaultWidth = 24.dp,
|
||||||
|
defaultHeight = 24.dp,
|
||||||
|
viewportWidth = 24f,
|
||||||
|
viewportHeight = 24f,
|
||||||
|
).apply {
|
||||||
|
path(fill = SolidColor(Color.Black)) {
|
||||||
|
moveTo(12f, 0f)
|
||||||
|
curveTo(5.373f, 0f, 0f, 5.373f, 0f, 12f)
|
||||||
|
reflectiveCurveToRelative(5.373f, 12f, 12f, 12f)
|
||||||
|
curveToRelative(2.54f, 0f, 4.894f, -0.79f, 6.834f, -2.135f)
|
||||||
|
lineToRelative(-3.107f, -3.109f)
|
||||||
|
arcToRelative(7.715f, 7.715f, 0f, isMoreThanHalf = true, isPositiveArc = true, 0f, -13.512f)
|
||||||
|
lineToRelative(3.107f, -3.109f)
|
||||||
|
arcTo(11.943f, 11.943f, 0f, isMoreThanHalf = false, isPositiveArc = false, 12f, 0f)
|
||||||
|
close()
|
||||||
|
moveTo(21.865f, 5.166f)
|
||||||
|
lineToRelative(-3.109f, 3.107f)
|
||||||
|
arcTo(7.67f, 7.67f, 0f, isMoreThanHalf = false, isPositiveArc = true, 19.715f, 12f)
|
||||||
|
arcToRelative(7.682f, 7.682f, 0f, isMoreThanHalf = false, isPositiveArc = true, -0.959f, 3.727f)
|
||||||
|
lineToRelative(3.109f, 3.107f)
|
||||||
|
arcTo(11.943f, 11.943f, 0f, isMoreThanHalf = false, isPositiveArc = false, 24f, 12f)
|
||||||
|
curveToRelative(0f, -2.54f, -0.79f, -4.894f, -2.135f, -6.834f)
|
||||||
|
close()
|
||||||
|
}
|
||||||
|
}.build()
|
||||||
|
|
||||||
|
return _OpenCollective!!
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("ObjectPropertyName")
|
||||||
|
private var _OpenCollective: ImageVector? = null
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package tachiyomi.presentation.core.icons
|
||||||
|
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.SolidColor
|
||||||
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
import androidx.compose.ui.graphics.vector.path
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
|
@Suppress("UnusedReceiverParameter")
|
||||||
|
val CustomIcons.Patreon: ImageVector
|
||||||
|
get() {
|
||||||
|
if (_Patreon != null) {
|
||||||
|
return _Patreon!!
|
||||||
|
}
|
||||||
|
_Patreon = ImageVector.Builder(
|
||||||
|
name = "Patreon",
|
||||||
|
defaultWidth = 24.dp,
|
||||||
|
defaultHeight = 24.dp,
|
||||||
|
viewportWidth = 24f,
|
||||||
|
viewportHeight = 24f,
|
||||||
|
).apply {
|
||||||
|
path(fill = SolidColor(Color.Black)) {
|
||||||
|
moveTo(22.957f, 7.21f)
|
||||||
|
curveToRelative(-0.004f, -3.064f, -2.391f, -5.576f, -5.191f, -6.482f)
|
||||||
|
curveToRelative(-3.478f, -1.125f, -8.064f, -0.962f, -11.384f, 0.604f)
|
||||||
|
curveTo(2.357f, 3.231f, 1.093f, 7.391f, 1.046f, 11.54f)
|
||||||
|
curveToRelative(-0.039f, 3.411f, 0.302f, 12.396f, 5.369f, 12.46f)
|
||||||
|
curveToRelative(3.765f, 0.047f, 4.326f, -4.804f, 6.068f, -7.141f)
|
||||||
|
curveToRelative(1.24f, -1.662f, 2.836f, -2.132f, 4.801f, -2.618f)
|
||||||
|
curveToRelative(3.376f, -0.836f, 5.678f, -3.501f, 5.673f, -7.031f)
|
||||||
|
close()
|
||||||
|
}
|
||||||
|
}.build()
|
||||||
|
|
||||||
|
return _Patreon!!
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("ObjectPropertyName")
|
||||||
|
private var _Patreon: ImageVector? = null
|
||||||
Reference in New Issue
Block a user