From 71d7649db62431ea5ab9678f23873f99deb6b167 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Tue, 6 Oct 2026 15:29:27 +0600 Subject: [PATCH] feat(welcome): implement onboarding flow with multiple pages and donation options --- .../drawable/spotube_logo.png | Bin 0 -> 147637 bytes .../composeResources/values/strings.xml | 94 ++++- .../kotlin/dev/krtirtho/spotube/App.kt | 15 +- .../dev/krtirtho/spotube/core/DebugConfig.kt | 33 ++ .../dev/krtirtho/spotube/core/db/Database.kt | 28 ++ .../dev/krtirtho/spotube/core/di/Modules.kt | 6 + .../core/navigation/NavigationModule.kt | 7 + .../core/navigation/NavigationState.kt | 7 +- .../core/ui/component/ApplicationMainBar.kt | 3 +- .../spotube/modules/plugin/PluginScreen.kt | 362 +++++++++++------- .../spotube/modules/plugin/PluginViewModel.kt | 15 +- .../spotube/modules/shell/AppShell.kt | 7 + .../spotube/modules/welcome/WelcomeContent.kt | 66 ++++ .../spotube/modules/welcome/WelcomeModels.kt | 39 ++ .../modules/welcome/WelcomeRepository.kt | 112 ++++++ .../spotube/modules/welcome/WelcomeScreen.kt | 245 ++++++++++++ .../modules/welcome/WelcomeViewModel.kt | 87 +++++ .../spotube/modules/welcome/WelcomeVisuals.kt | 91 +++++ .../modules/welcome/pages/BrandingPage.kt | 46 +++ .../modules/welcome/pages/DonationPage.kt | 70 ++++ .../modules/welcome/pages/HowItWorksPage.kt | 181 +++++++++ .../modules/welcome/pages/PluginsPage.kt | 10 + .../resources/iconsax/BrandBuyMeACoffee.kt | 264 +++++++++++++ .../resources/iconsax/BrandOpencollective.kt | 58 +++ .../spotube/resources/iconsax/BrandPatreon.kt | 54 +++ .../ui/component/ApplicationMainBar.jvm.kt | 11 + .../ui/component/ApplicationMainBar.mobile.kt | 13 +- 27 files changed, 1784 insertions(+), 140 deletions(-) create mode 100644 composeApp/src/commonMain/composeResources/drawable/spotube_logo.png create mode 100644 composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/DebugConfig.kt create mode 100644 composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/welcome/WelcomeContent.kt create mode 100644 composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/welcome/WelcomeModels.kt create mode 100644 composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/welcome/WelcomeRepository.kt create mode 100644 composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/welcome/WelcomeScreen.kt create mode 100644 composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/welcome/WelcomeViewModel.kt create mode 100644 composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/welcome/WelcomeVisuals.kt create mode 100644 composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/welcome/pages/BrandingPage.kt create mode 100644 composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/welcome/pages/DonationPage.kt create mode 100644 composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/welcome/pages/HowItWorksPage.kt create mode 100644 composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/welcome/pages/PluginsPage.kt create mode 100644 composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/resources/iconsax/BrandBuyMeACoffee.kt create mode 100644 composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/resources/iconsax/BrandOpencollective.kt create mode 100644 composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/resources/iconsax/BrandPatreon.kt diff --git a/composeApp/src/commonMain/composeResources/drawable/spotube_logo.png b/composeApp/src/commonMain/composeResources/drawable/spotube_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..d46d9ce5cc29c5d4f69966c30cc9d26adcda3df3 GIT binary patch literal 147637 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�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%0Ax1Jqtd^98kT7;JTY#Gut(Knvd^JXpj=@cQx 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 literal 0 HcmV?d00001 diff --git a/composeApp/src/commonMain/composeResources/values/strings.xml b/composeApp/src/commonMain/composeResources/values/strings.xml index dcddd6af..b636db4f 100644 --- a/composeApp/src/commonMain/composeResources/values/strings.xml +++ b/composeApp/src/commonMain/composeResources/values/strings.xml @@ -202,8 +202,98 @@ Ignore Update Spotube %1$s is available - See what\'s new in this release on the Spotube downloads page. + See what’s new in this release on the Spotube downloads page. %1$s %2$s is available Currently installed: %1$s - See what\'s new in this update from the plugin\'s page. + See what’s new in this update from the plugin’s page. + + Skip + Next + Back + Get Started + Spotube + Music, without the strings. + An open-source player for a world of music.\nYours to explore. Yours to make your own. + Keep music free. + Spotube is built by people, not a subscription business. If it becomes part of your everyday life, a little support helps us give it the care it deserves.\n\nThank you for making room for independent software. + Start with a catalog plugin to find songs and playlists. Choose an audio plugin to play them. Lyrics and listening history are optional. Discover a plugin below, review its permissions, install it, then select it here. + Discover plugins + Your sources + Try again + More plugins + No new plugins found. You can also install one from a link or a .smplug file above. + Plugin discovery is unavailable right now. Try again, or install from a link or file. + A familiar player. A different kind of freedom. + Browse through your chosen catalog. Press play and your audio plugin finds the stream.\n\nYour device handles playback and caching. Add synced lyrics, save tracks for later, or listen together with Group Jam. + DEMONSTRATION + PAUSED + Pause demonstration + Play demonstration + + + Your music. Your sources. Your rules. + A free, open-source music player built by a community that simply loves music. + Open source + No ads + No telemetry + Plugin powered + Let’s Go + + + Supporters keep us going + Built with passion. Kept alive by you. + Spotube is 100% free, libre, and open source — no ads, no subscriptions, no tracking. Every feature exists because people like you chose to help keep the code flowing. + Patreon + Monthly Sponsor + Support recurring development and infrastructure + Buy Me a Coffee + One-Time Coffee + Fuel a late-night coding session with a quick tip + Open Collective + Community Fund + Transparent, community-governed financial backing + Thank you from the bottom of our hearts for believing in open music. + Supporting is completely optional — Spotube stays free either way. + Sponsor + Open %1$s + + + Modular Architecture + Choose where your music comes from. + Spotube separates catalog discovery, audio streaming, and synced lyrics. Configure your engines below or switch them anytime. + Metadata Engine + Powers music discovery, search, track listings, and playlists. + Audio Engine + Fetches and decodes the audio streams that play. + Lyrics Engine + Streams time-synced karaoke lyrics in real time. + Scrobble Engine + Records listening history to Last.fm or ListenBrainz. + Active + Built-in + Ready + Optional + Select an engine + Installed Engines + Set as default + NewPipe YouTube and LRCLib Lyrics are built-in and ready to stream out of the box. + Manage or add new community plugins anytime in Settings → Plugins. + + + Retro-Modern Architecture + Pure Hi-Fi, zero middlemen + Here’s how Spotube orchestrates decentralized playback directly on your machine. + DIRECT PEER-TO-PEER • 256 KBPS OPUS + Blue hour + Spotube · player demonstration + A little music for the moments in between. + 1. Decentralized Discovery + Queries go straight to your chosen catalog plugin. Spotube never profiles your taste. + 2. Direct Stream Resolution + Audio streams directly from content servers to your machine. No proxy hops or remote data harvesting. + 3. Local DSP & Playback + Decoded and cached locally with equalizer controls and optional offline download. + 4. Synced Lyrics & Jam + Karaoke lyrics track the playback timestamp, with LAN remote control and Group Jam. + No middleman servers. Playback happens right on your device — private by design. diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/App.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/App.kt index 6d9c9b22..6eb9bb8c 100644 --- a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/App.kt +++ b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/App.kt @@ -48,6 +48,7 @@ import dev.krtirtho.spotube.modules.update.AppUpdateService import dev.krtirtho.spotube.modules.update.PluginUpdateDialog import dev.krtirtho.spotube.modules.update.PluginUpdateService import dev.krtirtho.spotube.modules.webview.WebViewScreen +import dev.krtirtho.spotube.modules.welcome.WelcomeRepository import dev.krtirtho.spotube.resources.iconsax.Iconsax import dev.krtirtho.spotube.resources.iconsax.IconsaxCd import dev.krtirtho.spotube.resources.iconsax.IconsaxDirectboxReceive @@ -140,10 +141,17 @@ fun App( val availableAppUpdate by appUpdateService.availableUpdate.collectAsStateWithLifecycle() val availablePluginUpdate by pluginUpdateService.availableUpdate.collectAsStateWithLifecycle() + val welcomeRepository: WelcomeRepository = koinInject() + val showOnboarding by welcomeRepository.shouldShowOnboarding.collectAsStateWithLifecycle() + + // The app always renders the shell + NavDisplay; onboarding is just the start destination. + // When it is completed the destination flips to Home and the navigation state is rebuilt. + val startRoute = if (showOnboarding) Routes.Welcome else Routes.Home + val navigationState = rememberNavigationState( - startRoute = Routes.Home, topLevelRoutes = TOP_LEVEL_ROUTES + startRoute = startRoute, topLevelRoutes = TOP_LEVEL_ROUTES ) - val navigator = remember { + val navigator = remember(navigationState) { Navigator(navigationState) } @@ -151,7 +159,8 @@ fun App( val baseUITheme = rememberBaseUITheme() CompositionLocalProvider(LocalBaseUITheme provides baseUITheme) { Box(modifier = Modifier.fillMaxSize()) { - val currentRoute = navigationState.backStacks[navigationState.topLevelRoute]?.last() + val currentRoute = + navigationState.backStacks[navigationState.topLevelRoute]?.last() Box(Modifier.fillMaxSize()) { AppShell(navigator, navigationState) { diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/DebugConfig.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/DebugConfig.kt new file mode 100644 index 00000000..249315bf --- /dev/null +++ b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/DebugConfig.kt @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package dev.krtirtho.spotube.core + +/** + * Development-only switches. These are intentionally kept out of [dev.krtirtho.spotube.modules.settings.UserSettings] + * so they can never leak into user-facing configuration. + */ +object DebugConfig { + /** + * Flip to `true` while developing to always open the Welcome/Onboarding screen, even after + * it has been completed (so you don't have to wipe app data to see it again). It still + * dismisses for the session when you finish or skip it. + * + * Keep it `false` when committing. + */ + const val FORCE_SHOW_ONBOARDING: Boolean = true +} diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/db/Database.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/db/Database.kt index f268d57f..573efaa1 100644 --- a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/db/Database.kt +++ b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/db/Database.kt @@ -23,10 +23,25 @@ import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.longPreferencesKey import androidx.datastore.preferences.core.stringPreferencesKey import dev.krtirtho.spotube.core.paths.Paths +import okio.FileSystem import okio.Path.Companion.toPath +import okio.SYSTEM @Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING") class Database(val paths: Paths) { + /** + * True when Spotube had already written its own data before this process started. Captured + * synchronously at construction time — the earliest point, before any eager service can + * create a fresh store — so it reliably tells a brand-new install apart from an upgrade of + * an existing one (used to skip the first-run onboarding on upgrades). + */ + val isExistingInstall: Boolean = runCatching { + val dataDirPath = paths.getApplicationDataDirPath() + EXISTING_INSTALL_PROBE_FILES.any { fileName -> + FileSystem.SYSTEM.exists("$dataDirPath/$fileName".toPath()) + } + }.getOrDefault(false) + val settingsDataStore: DataStore by lazy { PreferenceDataStoreFactory.createWithPath( produceFile = { "${paths.getApplicationDataDirPath()}/spotube.preferences_pb".toPath() } @@ -61,6 +76,19 @@ class Database(val paths: Paths) { produceFile = { "${paths.getApplicationDataDirPath()}/spotube_blacklist.preferences_pb".toPath() } ) } + + private companion object { + /** Preference files written by previous launches; their presence means an upgrade. */ + val EXISTING_INSTALL_PROBE_FILES = listOf( + "spotube.preferences_pb", + "spotube_plugins.preferences_pb", + "spotube_matched_tracks.preferences_pb", + "spotube_audio_player_queue.preferences_pb", + "spotube_local_media.preferences_pb", + "spotube_blacklist.preferences_pb", + "spotube_cookies.preferences_pb", + ) + } } object DatabaseKeys { diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/di/Modules.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/di/Modules.kt index d9fd4290..bdf28574 100644 --- a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/di/Modules.kt +++ b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/di/Modules.kt @@ -86,6 +86,8 @@ import dev.krtirtho.spotube.modules.shell.AppShellViewModel import dev.krtirtho.spotube.modules.shell.PlayerOptionsViewModel import dev.krtirtho.spotube.modules.shell.alternative_track.AlternativeTrackContentViewModel import dev.krtirtho.spotube.modules.shell.player_queue.PlayerQueueContentViewModel +import dev.krtirtho.spotube.modules.welcome.WelcomeRepository +import dev.krtirtho.spotube.modules.welcome.WelcomeViewModel import org.koin.core.module.Module import org.koin.core.module.dsl.bind import org.koin.core.module.dsl.createdAtStart @@ -141,6 +143,10 @@ val sharedModules = module { viewModelOf(::SettingsViewModel) { bind() } viewModelOf(::JamSettingsViewModel) + // Welcome / Onboarding + singleOf(::WelcomeRepository) + viewModelOf(::WelcomeViewModel) + // Updates single { AppUpdateService(get()) } withOptions { createdAtStart() } single { PluginUpdateService(get(), get()) } withOptions { createdAtStart() } diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/navigation/NavigationModule.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/navigation/NavigationModule.kt index b3f7f48f..f1adc2f0 100644 --- a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/navigation/NavigationModule.kt +++ b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/navigation/NavigationModule.kt @@ -34,6 +34,7 @@ import dev.krtirtho.spotube.modules.saved_tracks.SavedTracksScreen import dev.krtirtho.spotube.modules.search.SearchScreen import dev.krtirtho.spotube.modules.settings.SettingsScreen import dev.krtirtho.spotube.modules.settings.JamSettingsScreen +import dev.krtirtho.spotube.modules.welcome.WelcomeScreen import kotlinx.serialization.Serializable import org.koin.compose.viewmodel.koinViewModel import org.koin.core.annotation.KoinExperimentalAPI @@ -91,6 +92,9 @@ sealed interface Routes : NavKey { @Serializable data object JamSettings : Routes + + @Serializable + data object Welcome : Routes } @OptIn(KoinExperimentalAPI::class) @@ -178,4 +182,7 @@ val navigationModule = module { navigation { JamSettingsScreen(viewModel = koinViewModel()) } + navigation { + WelcomeScreen(viewModel = koinViewModel()) + } } diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/navigation/NavigationState.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/navigation/NavigationState.kt index 3f0ced21..8845db6b 100644 --- a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/navigation/NavigationState.kt +++ b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/navigation/NavigationState.kt @@ -26,7 +26,6 @@ import androidx.compose.runtime.saveable.rememberSerializable import androidx.compose.runtime.setValue import androidx.compose.runtime.snapshots.SnapshotStateList import androidx.compose.runtime.toMutableStateList -import androidx.lifecycle.viewmodel.navigation3.rememberViewModelStoreNavEntryDecorator import androidx.navigation3.runtime.NavBackStack import androidx.navigation3.runtime.NavEntry import androidx.navigation3.runtime.NavKey @@ -45,7 +44,9 @@ val TOP_LEVEL_ROUTES = setOf( Routes.Home, Routes.Search, Routes.Library, - Routes.Settings + Routes.Settings, + // Bootstrap destination: needs its own back stack when the app starts on it. + Routes.Welcome, ) val serializersConfig = SavedStateConfiguration { @@ -63,6 +64,7 @@ val serializersConfig = SavedStateConfiguration { subclass(Routes.Blacklist::class, Routes.Blacklist.serializer()) subclass(Routes.Devices::class, Routes.Devices.serializer()) subclass(Routes.Jam::class, Routes.Jam.serializer()) + subclass(Routes.Welcome::class, Routes.Welcome.serializer()) } } } @@ -122,7 +124,6 @@ fun NavigationState.toEntries( val decoratedEntries = backStacks.mapValues { (_, stack) -> val decorators = listOf( rememberSaveableStateHolderNavEntryDecorator(), - rememberViewModelStoreNavEntryDecorator() ) rememberDecoratedNavEntries( backStack = stack, diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/ui/component/ApplicationMainBar.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/ui/component/ApplicationMainBar.kt index 359ee469..30cfa7b7 100644 --- a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/ui/component/ApplicationMainBar.kt +++ b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/core/ui/component/ApplicationMainBar.kt @@ -47,5 +47,6 @@ expect fun ApplicationMainBar( title: @Composable () -> Unit = {}, subtitle: @Composable () -> Unit = {}, actions: @Composable (RowScope.() -> Unit) = {}, - backButton: Boolean = true + backButton: Boolean = true, + transparent: Boolean = false, ) \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/plugin/PluginScreen.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/plugin/PluginScreen.kt index 031397ed..ede1fb60 100644 --- a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/plugin/PluginScreen.kt +++ b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/plugin/PluginScreen.kt @@ -102,6 +102,14 @@ import okio.Path import org.jetbrains.compose.resources.stringResource import org.koin.compose.viewmodel.koinViewModel import spotube.composeapp.generated.resources.Res +import spotube.composeapp.generated.resources.welcome_plugins_title +import spotube.composeapp.generated.resources.welcome_plugins_setup_guide +import spotube.composeapp.generated.resources.welcome_plugins_discover +import spotube.composeapp.generated.resources.welcome_plugins_retry +import spotube.composeapp.generated.resources.welcome_plugins_more +import spotube.composeapp.generated.resources.welcome_plugins_discovery_empty +import spotube.composeapp.generated.resources.welcome_plugins_discovery_error +import spotube.composeapp.generated.resources.welcome_plugins_sources import spotube.composeapp.generated.resources.plugin_action_download import spotube.composeapp.generated.resources.plugin_action_install_from_file import spotube.composeapp.generated.resources.plugin_configure_title @@ -138,6 +146,7 @@ private val VERIFIED_PLUGIN_OWNERS = setOf() @Composable fun PluginScreen( viewModel: PluginViewModel = koinViewModel(), + onboarding: Boolean = false, ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() val shellBottomInset = LocalAppShellBottomInset.current @@ -215,8 +224,11 @@ fun PluginScreen( Scaffold( topBar = { - ApplicationMainBar(title = { Text(stringResource(Res.string.plugin_screen_title)) }) - } + if (!onboarding) { + ApplicationMainBar(title = { Text(stringResource(Res.string.plugin_screen_title)) }) + } + }, + containerColor = if (onboarding) Color.Transparent else MaterialTheme.colorScheme.surface, ) { innerPadding -> when (val data = uiState) { is PluginUiState.Loading -> { @@ -244,114 +256,75 @@ fun PluginScreen( ), verticalArrangement = Arrangement.spacedBy(8.dp) ) { - item { - Row( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 4.dp, vertical = 4.dp), - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically - ) { - Text( - stringResource(Res.string.plugin_configure_title), - style = MaterialTheme.typography.titleLarge, - fontWeight = FontWeight.SemiBold - ) - PrimaryButton(onClick = { viewModel.showInstallSheet() }) { - Icon( - Iconsax.IconsaxAdd, - contentDescription = "Install a plugin", + if (onboarding) { + item { + Column( + modifier = Modifier.padding( + horizontal = 4.dp, + vertical = 16.dp + ), + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { + Text( + stringResource(Res.string.welcome_plugins_title), + style = MaterialTheme.typography.headlineMedium, + fontWeight = FontWeight.SemiBold, ) - Text(stringResource(Res.string.plugin_install_section_title)) + Text( + stringResource(Res.string.welcome_plugins_setup_guide), + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + } + item { + Row( + horizontalArrangement = Arrangement.spacedBy(8.dp), + modifier = Modifier.padding(bottom = 12.dp) + ) { + OutlineButton(onClick = { viewModel.showOnboardingDiscovery(true) }) { + Text( + stringResource(Res.string.welcome_plugins_discover), + fontWeight = if (data.onboardingDiscover) FontWeight.Bold else FontWeight.Normal + ) + } + OutlineButton(onClick = { + viewModel.showOnboardingDiscovery( + false + ) + }) { + Text( + stringResource(Res.string.welcome_plugins_sources), + fontWeight = if (!data.onboardingDiscover) FontWeight.Bold else FontWeight.Normal + ) + } } } } - - item { - Card( - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 4.dp), - ) { - Column( + if (!onboarding || !data.onboardingDiscover) { + item { + Row( modifier = Modifier .fillMaxWidth() - .padding(top = 4.dp, bottom = 4.dp) + .padding(horizontal = 4.dp, vertical = 4.dp), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically ) { - data.abilitySelections.forEachIndexed { index, selection -> - if (index > 0) { - HorizontalDivider( - color = MaterialTheme.colorScheme.outlineVariant.copy( - alpha = 0.5f - ), - ) - } - DefaultAbilityPluginSelector( - selection = selection, - onSelected = { plugin -> - viewModel.selectPlugin(selection.ability, plugin) - }, + Text( + stringResource(Res.string.plugin_configure_title), + style = MaterialTheme.typography.titleLarge, + fontWeight = FontWeight.SemiBold + ) + PrimaryButton(onClick = { viewModel.showInstallSheet() }) { + Icon( + Iconsax.IconsaxAdd, + contentDescription = stringResource(Res.string.plugin_install_section_title), ) + Text(stringResource(Res.string.plugin_install_section_title)) } } } - } - if (data.plugins.isEmpty()) { - item { - Box( - modifier = Modifier.fillMaxWidth().padding(vertical = 48.dp), - contentAlignment = Alignment.Center - ) { - Column( - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.spacedBy(12.dp) - ) { - Surface( - modifier = Modifier.size(72.dp) - .clip(RoundedCornerShape(18.dp)), - color = MaterialTheme.colorScheme.primary.copy(alpha = 0.1f) - ) { - Box(contentAlignment = Alignment.Center) { - Icon( - Iconsax.IconsaxBox, - contentDescription = null, - modifier = Modifier.size(32.dp), - tint = MaterialTheme.colorScheme.primary - ) - } - } - Text( - stringResource(Res.string.plugin_empty_title), - style = MaterialTheme.typography.titleSmall, - fontWeight = FontWeight.SemiBold - ) - Text( - stringResource(Res.string.plugin_empty_subtitle), - style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.onSurfaceVariant - ) - } - } - } - } else { - item { - val noun = if (data.plugins.size == 1) { - stringResource(Res.string.plugin_installed_singular) - } else { - stringResource(Res.string.plugin_installed_plural) - } - Text( - stringResource( - Res.string.plugin_installed_count, - data.plugins.size, - noun - ), - style = MaterialTheme.typography.labelMedium, - color = MaterialTheme.colorScheme.onSurfaceVariant, - modifier = Modifier.padding(horizontal = 4.dp, vertical = 4.dp) - ) - } item { Card( modifier = Modifier @@ -359,9 +332,11 @@ fun PluginScreen( .padding(vertical = 4.dp), ) { Column( - modifier = Modifier.fillMaxWidth() + modifier = Modifier + .fillMaxWidth() + .padding(top = 4.dp, bottom = 4.dp) ) { - data.plugins.forEachIndexed { index, item -> + data.abilitySelections.forEachIndexed { index, selection -> if (index > 0) { HorizontalDivider( color = MaterialTheme.colorScheme.outlineVariant.copy( @@ -369,30 +344,128 @@ fun PluginScreen( ), ) } - PluginCard( - plugin = item.plugin, - isSelected = item.isSelected, - onRemove = { viewModel.removePlugin(item.plugin) }, - isLoggedIn = item.authState.isLoggedIn, - logoPath = item.logoPath, - onInfo = { viewModel.showPluginInfo(item.plugin) }, - onSupport = if (item.isSelected) { - { viewModel.loadSupport(item.plugin) } - } else null, - onLogin = if (item.isSelected && item.authState.requiresAuth) { - { viewModel.login(item.plugin) } - } else null, - onLogout = if (item.isSelected && item.authState.requiresAuth) { - { viewModel.logout(item.plugin) } - } else null, + DefaultAbilityPluginSelector( + selection = selection, + onSelected = { plugin -> + viewModel.selectPlugin( + selection.ability, + plugin + ) + }, ) } } } } - } - if (data.discover.isLoading || data.discover.repos.isNotEmpty()) { + if (data.plugins.isEmpty()) { + item { + Box( + modifier = Modifier.fillMaxWidth() + .padding(vertical = 48.dp), + contentAlignment = Alignment.Center + ) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(12.dp) + ) { + Surface( + modifier = Modifier.size(72.dp) + .clip(RoundedCornerShape(18.dp)), + color = MaterialTheme.colorScheme.primary.copy(alpha = 0.1f) + ) { + Box(contentAlignment = Alignment.Center) { + Icon( + Iconsax.IconsaxBox, + contentDescription = null, + modifier = Modifier.size(32.dp), + tint = MaterialTheme.colorScheme.primary + ) + } + } + Text( + stringResource(Res.string.plugin_empty_title), + style = MaterialTheme.typography.titleSmall, + fontWeight = FontWeight.SemiBold + ) + Text( + stringResource(Res.string.plugin_empty_subtitle), + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + } + } + } + } else { + item { + val noun = if (data.plugins.size == 1) { + stringResource(Res.string.plugin_installed_singular) + } else { + stringResource(Res.string.plugin_installed_plural) + } + Text( + stringResource( + Res.string.plugin_installed_count, + data.plugins.size, + noun + ), + style = MaterialTheme.typography.labelMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = Modifier.padding( + horizontal = 4.dp, + vertical = 4.dp + ) + ) + } + item { + Card( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 4.dp), + ) { + Column( + modifier = Modifier.fillMaxWidth() + ) { + data.plugins.forEachIndexed { index, item -> + if (index > 0) { + HorizontalDivider( + color = MaterialTheme.colorScheme.outlineVariant.copy( + alpha = 0.5f + ), + ) + } + PluginCard( + plugin = item.plugin, + isSelected = item.isSelected, + onRemove = { viewModel.removePlugin(item.plugin) }, + isLoggedIn = item.authState.isLoggedIn, + logoPath = item.logoPath, + onInfo = { viewModel.showPluginInfo(item.plugin) }, + onSupport = if (item.isSelected) { + { viewModel.loadSupport(item.plugin) } + } else null, + onLogin = if (item.isSelected && item.authState.requiresAuth) { + { viewModel.login(item.plugin) } + } else null, + onLogout = if (item.isSelected && item.authState.requiresAuth) { + { viewModel.logout(item.plugin) } + } else null, + ) + } + } + } + } + } + + } + if ((!onboarding || data.onboardingDiscover) && (onboarding || data.discover.isLoading || data.discover.repos.isNotEmpty() || data.discover.error != null)) { + if (onboarding) { + item { + OutlineButton(onClick = viewModel::showInstallSheet) { + Text(stringResource(Res.string.plugin_install_section_title)) + } + } + } item { Row( modifier = Modifier @@ -408,12 +481,31 @@ fun PluginScreen( tint = MaterialTheme.colorScheme.primary ) Text( - "Discover Plugins", + stringResource(Res.string.welcome_plugins_discover), style = MaterialTheme.typography.titleLarge, fontWeight = FontWeight.SemiBold ) } } + if (data.discover.isLoading) { + item { + Box( + Modifier.fillMaxWidth().padding(16.dp), + contentAlignment = Alignment.Center + ) { + CircularProgressIndicator(Modifier.size(24.dp)) + } + } + } + if (onboarding && data.discover.isInitialLoaded && !data.discover.isLoading && data.discover.repos.isEmpty() && data.discover.error == null) { + item { + Text( + stringResource(Res.string.welcome_plugins_discovery_empty), + color = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = Modifier.padding(12.dp) + ) + } + } items( data.discover.repos, key = { it.id } @@ -440,15 +532,27 @@ fun PluginScreen( data.discover.error?.let { error -> item { - Text( - error, - style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.error, - modifier = Modifier.padding( - horizontal = 4.dp, - vertical = 8.dp + Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { + Text( + stringResource(Res.string.welcome_plugins_discovery_error), + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.error, + modifier = Modifier.padding( + horizontal = 4.dp, + vertical = 8.dp + ) ) - ) + OutlineButton(onClick = viewModel::retryDiscovery) { + Text(stringResource(Res.string.welcome_plugins_retry)) + } + } + } + } + if (onboarding && data.discover.hasMore && !data.discover.isLoading && !data.discover.isLoadingMore && data.discover.error == null) { + item { + OutlineButton(onClick = viewModel::loadNextDiscoverPage) { + Text(stringResource(Res.string.welcome_plugins_more)) + } } } } @@ -468,8 +572,8 @@ fun PluginScreen( // Pagination trigger stays in the composable because it is driven by // LazyList layout information, which only exists in UI scope. // The actual loading call is forwarded to the ViewModel. - LaunchedEffect(shouldLoadMore.value) { - if (shouldLoadMore.value) { + LaunchedEffect(shouldLoadMore.value, onboarding) { + if (!onboarding && shouldLoadMore.value) { viewModel.loadNextDiscoverPage() } } diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/plugin/PluginViewModel.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/plugin/PluginViewModel.kt index 004580f9..2f8917bb 100644 --- a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/plugin/PluginViewModel.kt +++ b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/plugin/PluginViewModel.kt @@ -87,6 +87,7 @@ sealed interface PluginUiState { data object Loading : PluginUiState data class Data( + val onboardingDiscover: Boolean = true, val plugins: List, val abilitySelections: List, val pendingPlugin: PluginManager.PendingPlugin? = null, @@ -118,6 +119,7 @@ private data class PluginManagerSnapshot( ) private data class PluginScreenState( + val onboardingDiscover: Boolean = true, val urlInput: String = "", val urlError: UrlError? = null, val isLoadingUrl: Boolean = false, @@ -239,6 +241,7 @@ class PluginViewModel( when (val state = snapshot.state) { is PluginManagerStates.Loading -> PluginUiState.Loading is PluginManagerStates.Data -> PluginUiState.Data( + onboardingDiscover = screen.onboardingDiscover, plugins = state.plugins.map { plugin -> PluginListItem( plugin = plugin, @@ -468,6 +471,10 @@ class PluginViewModel( pluginManager.setSelectedPlugin(ability, plugin) } + fun showOnboardingDiscovery(show: Boolean) { + _screenState.update { it.copy(onboardingDiscover = show) } + } + fun removePlugin(plugin: PluginEntry) { viewModelScope.launch { runCatching { @@ -512,6 +519,11 @@ class PluginViewModel( return repos.filter { it.htmlUrl !in installedUrls } } + fun retryDiscovery() { + if (_screenState.value.discover.isLoading) return + loadFirstDiscoverPage() + } + private fun loadFirstDiscoverPage() { viewModelScope.launch { _screenState.update { @@ -590,7 +602,8 @@ class PluginViewModel( } fun installPlugin(release: GitHubRelease, repoId: Long) { - val smplugUrl = release.assets.firstOrNull { it.name.endsWith(".smplug") }?.browserDownloadUrl + val smplugUrl = + release.assets.firstOrNull { it.name.endsWith(".smplug") }?.browserDownloadUrl if (smplugUrl != null) { installPluginFromUrl(smplugUrl, repoId) } diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/shell/AppShell.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/shell/AppShell.kt index b044e3a4..4d64ed63 100644 --- a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/shell/AppShell.kt +++ b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/shell/AppShell.kt @@ -129,6 +129,13 @@ fun AppShell( } } + // Onboarding is a bootstrap, full-screen destination. It draws its own app bar (and thus + // the desktop window controls), so render it without the sidebar, players or bottom bar. + if (navigationState.topLevelRoute == Routes.Welcome) { + content() + return + } + ConnectionRequestDialogHost() PlayDestinationPickerHost() diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/welcome/WelcomeContent.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/welcome/WelcomeContent.kt new file mode 100644 index 00000000..b5ea2ede --- /dev/null +++ b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/welcome/WelcomeContent.kt @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package dev.krtirtho.spotube.modules.welcome + +import dev.krtirtho.spotube.resources.iconsax.BrandBuyMeACoffee +import dev.krtirtho.spotube.resources.iconsax.BrandOpencollective +import dev.krtirtho.spotube.resources.iconsax.BrandPatreon +import dev.krtirtho.spotube.resources.iconsax.Iconsax +import org.jetbrains.compose.resources.StringResource +import spotube.composeapp.generated.resources.Res +import spotube.composeapp.generated.resources.welcome_donate_bmac_subtitle +import spotube.composeapp.generated.resources.welcome_donate_bmac_title +import spotube.composeapp.generated.resources.welcome_donate_opencollective_subtitle +import spotube.composeapp.generated.resources.welcome_donate_opencollective_title +import spotube.composeapp.generated.resources.welcome_donate_patreon_subtitle +import spotube.composeapp.generated.resources.welcome_donate_patreon_title +import spotube.composeapp.generated.resources.welcome_get_started +import spotube.composeapp.generated.resources.welcome_lets_go +import spotube.composeapp.generated.resources.welcome_next + +/** Order matches the 4 onboarding pages. */ +internal val welcomePrimaryLabels: List = listOf( + Res.string.welcome_lets_go, + Res.string.welcome_next, + Res.string.welcome_next, + Res.string.welcome_get_started, +) + +internal val welcomeDonations: List = listOf( + DonationOption( + id = "patreon", + title = Res.string.welcome_donate_patreon_title, + subtitle = Res.string.welcome_donate_patreon_subtitle, + url = "https://patreon.com/krtirtho", + icon = Iconsax.BrandPatreon, + ), + DonationOption( + id = "buymeacoffee", + title = Res.string.welcome_donate_bmac_title, + subtitle = Res.string.welcome_donate_bmac_subtitle, + url = "https://www.buymeacoffee.com/krtirtho", + icon = Iconsax.BrandBuyMeACoffee, + ), + DonationOption( + id = "opencollective", + title = Res.string.welcome_donate_opencollective_title, + subtitle = Res.string.welcome_donate_opencollective_subtitle, + url = "https://opencollective.com/spotube", + icon = Iconsax.BrandOpencollective, + ), +) diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/welcome/WelcomeModels.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/welcome/WelcomeModels.kt new file mode 100644 index 00000000..b8199ef9 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/welcome/WelcomeModels.kt @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package dev.krtirtho.spotube.modules.welcome + +import androidx.compose.ui.graphics.vector.ImageVector +import org.jetbrains.compose.resources.StringResource + +/** A single donation destination shown on the support page. */ +data class DonationOption( + val id: String, + val title: StringResource, + val subtitle: StringResource, + val url: String, + val icon: ImageVector, +) + +data class WelcomeUiState( + val currentPageIndex: Int, + val pageCount: Int, + val isLastPage: Boolean, + val primaryLabel: StringResource, + val donations: List, + val demoPlaying: Boolean = true, +) diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/welcome/WelcomeRepository.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/welcome/WelcomeRepository.kt new file mode 100644 index 00000000..6240e22d --- /dev/null +++ b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/welcome/WelcomeRepository.kt @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package dev.krtirtho.spotube.modules.welcome + +import androidx.datastore.preferences.core.booleanPreferencesKey +import androidx.datastore.preferences.core.edit +import dev.krtirtho.spotube.core.DebugConfig +import dev.krtirtho.spotube.core.db.Database +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.IO +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch + +/** + * Owns whether the Welcome/Onboarding flow should be the app's start destination. + * + * This lives in its own preference rather than [dev.krtirtho.spotube.modules.settings.UserSettings] + * so the very first launch can be distinguished from "settings not read yet". It is a Koin + * singleton so the app-level start route and the routed [WelcomeScreen] observe the same state. + */ +class WelcomeRepository(private val database: Database) { + companion object { + private val ONBOARDING_COMPLETED_KEY = booleanPreferencesKey("onboarding_completed") + } + + private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO) + + /** Set when onboarding is finished for this process only; does not persist (debug forcing). */ + private val sessionDismissed = MutableStateFlow(false) + + /** Synchronous signal, available before the persisted flag loads; true on upgrades. */ + val isExistingInstall: Boolean get() = database.isExistingInstall + + private val onboardingCompleted: StateFlow = database.settingsDataStore.data + .map { prefs -> prefs[ONBOARDING_COMPLETED_KEY] ?: database.isExistingInstall } + .stateIn(scope, SharingStarted.Eagerly, null) + + /** + * Whether the app should start on the Welcome/Onboarding route instead of the main shell. + * + * - `true` on a fresh install, or whenever [DebugConfig.FORCE_SHOW_ONBOARDING] is enabled. + * - `false` on an existing install (upgrade) or once onboarding is completed or skipped, + * including within a forced session. + * + * The initial value is derived from [isExistingInstall] so the start route can be chosen on + * the very first frame without flashing the wrong screen. + */ + val shouldShowOnboarding: StateFlow = combine( + onboardingCompleted, + sessionDismissed, + ) { completed, dismissed -> + when { + dismissed -> false + DebugConfig.FORCE_SHOW_ONBOARDING -> true + else -> !(completed ?: database.isExistingInstall) + } + }.stateIn( + scope = scope, + started = SharingStarted.Eagerly, + initialValue = if (DebugConfig.FORCE_SHOW_ONBOARDING) true else !database.isExistingInstall, + ) + + init { + // One-time migration. When the flag was never written this is either a brand-new install + // (show onboarding) or an upgrade of an existing install (skip it). Persisting the + // decision also stops the fresh install's own data writes from being mistaken for an + // upgrade on the next launch. + scope.launch { + database.settingsDataStore.edit { prefs -> + if (ONBOARDING_COMPLETED_KEY !in prefs) { + prefs[ONBOARDING_COMPLETED_KEY] = database.isExistingInstall + } + } + } + } + + suspend fun completeOnboarding() { + sessionDismissed.value = true + database.settingsDataStore.edit { prefs -> + prefs[ONBOARDING_COMPLETED_KEY] = true + } + } + + /** Development helper to surface the welcome screen again on the next launch. */ + suspend fun resetOnboarding() { + database.settingsDataStore.edit { prefs -> + prefs[ONBOARDING_COMPLETED_KEY] = false + } + } +} diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/welcome/WelcomeScreen.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/welcome/WelcomeScreen.kt new file mode 100644 index 00000000..94c2055e --- /dev/null +++ b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/welcome/WelcomeScreen.kt @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package dev.krtirtho.spotube.modules.welcome + +import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.core.animateDpAsState +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.slideInHorizontally +import androidx.compose.animation.slideOutHorizontally +import androidx.compose.animation.togetherWith +import androidx.compose.foundation.background +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.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.widthIn +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.ExperimentalMaterial3Api +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.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import dev.krtirtho.spotube.core.ui.base.GhostButton +import dev.krtirtho.spotube.core.ui.base.OutlineButton +import dev.krtirtho.spotube.core.ui.component.ApplicationMainBar +import dev.krtirtho.spotube.modules.welcome.pages.BrandingPage +import dev.krtirtho.spotube.modules.welcome.pages.DonationPage +import dev.krtirtho.spotube.modules.welcome.pages.HowItWorksPage +import dev.krtirtho.spotube.modules.welcome.pages.PluginsPage +import dev.krtirtho.spotube.resources.iconsax.Iconsax +import dev.krtirtho.spotube.resources.iconsax.IconsaxCheckCircle +import dev.krtirtho.spotube.resources.iconsax.IconsaxNext +import org.jetbrains.compose.resources.stringResource +import spotube.composeapp.generated.resources.Res +import spotube.composeapp.generated.resources.welcome_back +import spotube.composeapp.generated.resources.welcome_skip + +/** + * First-run Welcome/Onboarding screen. It is a route ([dev.krtirtho.spotube.core.navigation.Routes.Welcome]) + * rendered inside the app shell. Uses the application's base controls and keeps plugin setup + * in-place, with a neutral palette independent of the user's chosen accent. + */ +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun WelcomeScreen(viewModel: WelcomeViewModel) { + val state by viewModel.uiState.collectAsStateWithLifecycle() + WelcomeTheme { + Box(Modifier.fillMaxSize()) { + WelcomeBackground() + Scaffold( + containerColor = Color.Transparent, + topBar = { + ApplicationMainBar( + title = { + WelcomeWordmark( + fontSize = MaterialTheme.typography.titleLarge.fontSize, + ) + }, + backButton = false, + transparent = true, + actions = { + if (!state.isLastPage) { + GhostButton(onClick = viewModel::completeOnboarding) { + Text( + text = stringResource(Res.string.welcome_skip), + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + } + }, + ) + }, + ) { innerPadding -> + Box(modifier = Modifier.fillMaxSize()) { + + Column( + modifier = Modifier + .fillMaxSize() + .padding(innerPadding) + .padding(horizontal = 16.dp, vertical = 8.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + // Central stage (animated page content) + Box( + modifier = Modifier + .weight(1f) + .widthIn(max = 1280.dp) + .fillMaxWidth(), + contentAlignment = Alignment.Center, + ) { + AnimatedContent( + modifier = Modifier.fillMaxSize(), + targetState = state.currentPageIndex, + transitionSpec = { + if (targetState > initialState) { + (slideInHorizontally { it / 4 } + fadeIn()) togetherWith + (slideOutHorizontally { -it / 4 } + fadeOut()) + } else { + (slideInHorizontally { -it / 4 } + fadeIn()) togetherWith + (slideOutHorizontally { it / 4 } + fadeOut()) + } + }, + label = "welcome_page", + ) { index -> + when (index) { + 0 -> BrandingPage() + + 1 -> DonationPage( + donations = state.donations, + onDonate = viewModel::openDonation, + ) + + 2 -> PluginsPage() + + else -> HowItWorksPage( + isPlaying = state.demoPlaying, + onTogglePlayback = viewModel::toggleDemo + ) + } + } + } + + Spacer(Modifier.height(10.dp)) + + // Bottom precision navigation bar: Back | Page Dots | Action Button + Row( + modifier = Modifier + .widthIn(max = 680.dp) + .fillMaxWidth() + .padding(horizontal = 12.dp, vertical = 6.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + // Left: Back button + if (state.currentPageIndex > 0) { + OutlineButton( + onClick = viewModel::previousPage, + ) { + Text( + text = stringResource(Res.string.welcome_back), + color = MaterialTheme.colorScheme.onSurface, + ) + } + } else { + Spacer(Modifier.width(76.dp)) + } + + // Center: Precision page dots + Box(Modifier.weight(1f), contentAlignment = Alignment.Center) { + PageIndicator( + pageCount = state.pageCount, + currentPageIndex = state.currentPageIndex, + ) + } + + // Right: Primary Next / Let's Go action + OutlineButton( + onClick = viewModel::onPrimary, + ) { + Text( + text = stringResource(state.primaryLabel), + fontWeight = FontWeight.SemiBold, + ) + Icon( + imageVector = if (state.isLastPage) { + Iconsax.IconsaxCheckCircle + } else { + Iconsax.IconsaxNext + }, + contentDescription = null, + modifier = Modifier.size(16.dp), + ) + } + } + } + } + } + } + } +} + +@Composable +private fun PageIndicator( + pageCount: Int, + currentPageIndex: Int, + modifier: Modifier = Modifier, +) { + Row( + modifier = modifier + .padding(horizontal = 12.dp, vertical = 8.dp), + horizontalArrangement = Arrangement.spacedBy(7.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + repeat(pageCount) { index -> + val selected = index == currentPageIndex + val width by animateDpAsState( + targetValue = if (selected) 20.dp else 6.dp, + label = "welcome_page_indicator", + ) + Box( + modifier = Modifier + .size(width = width, height = 6.dp) + .clip(CircleShape) + .background( + if (selected) { + MaterialTheme.colorScheme.onSurface + } else { + MaterialTheme.colorScheme.outlineVariant + } + ), + ) + } + } +} diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/welcome/WelcomeViewModel.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/welcome/WelcomeViewModel.kt new file mode 100644 index 00000000..b2ee940a --- /dev/null +++ b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/welcome/WelcomeViewModel.kt @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package dev.krtirtho.spotube.modules.welcome + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch + +class WelcomeViewModel( + private val repository: WelcomeRepository, +) : ViewModel() { + private val currentPageIndex = MutableStateFlow(0) + private val demoPlaying = MutableStateFlow(true) + + val uiState: StateFlow = + combine(currentPageIndex, demoPlaying) { index, playing -> + val pageCount = welcomePrimaryLabels.size + WelcomeUiState( + currentPageIndex = index, + pageCount = pageCount, + isLastPage = index == pageCount - 1, + primaryLabel = welcomePrimaryLabels[index.coerceIn(0, pageCount - 1)], + donations = welcomeDonations, + demoPlaying = playing, + ) + } + .stateIn( + scope = viewModelScope, + started = SharingStarted.Eagerly, + initialValue = WelcomeUiState( + currentPageIndex = 0, + pageCount = welcomePrimaryLabels.size, + isLastPage = welcomePrimaryLabels.size == 1, + primaryLabel = welcomePrimaryLabels.first(), + donations = welcomeDonations, + ), + ) + + fun nextPage() { + currentPageIndex.update { (it + 1).coerceAtMost(welcomePrimaryLabels.lastIndex) } + } + + fun toggleDemo() { + demoPlaying.update { !it } + } + + fun onPrimary() { + if (currentPageIndex.value == welcomePrimaryLabels.lastIndex) completeOnboarding() else nextPage() + } + + fun openDonation(url: String) { + // Only destinations owned by our static funding list can be opened here. + if (welcomeDonations.none { it.url == url }) return + runCatching { dev.krtirtho.spotube.openUrlInBrowser(url) } + } + + fun previousPage() { + currentPageIndex.update { (it - 1).coerceAtLeast(0) } + } + + fun completeOnboarding() { + viewModelScope.launch { + repository.completeOnboarding() + } + } +} diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/welcome/WelcomeVisuals.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/welcome/WelcomeVisuals.kt new file mode 100644 index 00000000..d5e50838 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/welcome/WelcomeVisuals.kt @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2026 Kingkor Roy Tirtho and Spotube Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package dev.krtirtho.spotube.modules.welcome + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.luminance +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.unit.TextUnit +import dev.krtirtho.spotube.core.ui.base.LocalBaseUITheme +import dev.krtirtho.spotube.core.ui.base.rememberBaseUITheme +import org.jetbrains.compose.resources.Font +import org.jetbrains.compose.resources.stringResource +import spotube.composeapp.generated.resources.* + +/** App-native controls and surfaces, in neutral charcoal/ivory instead of the user's accent. */ +@Composable +fun WelcomeTheme(content: @Composable () -> Unit) { + val dark = MaterialTheme.colorScheme.surface.luminance() < 0.5f + val scheme = if (dark) darkColorScheme( + primary = Color(0xFF8BA8DD), onPrimary = Color(0xFF10213E), + primaryContainer = Color(0xFF263754), onPrimaryContainer = Color(0xFFD9E4F7), + secondary = Color(0xFFBDBDBD), secondaryContainer = Color(0xFF343434), + onSecondaryContainer = Color(0xFFF0F0F0), + surface = Color(0xFF181818), background = Color(0xFF181818), + surfaceContainer = Color(0xFF222222), surfaceContainerHigh = Color(0xFF292929), + surfaceContainerHighest = Color(0xFF323232), + onSurface = Color(0xFFEDEBE7), onSurfaceVariant = Color(0xFFABA9A5), + outlineVariant = Color(0xFF414141), + ) else lightColorScheme( + primary = Color(0xFF244879), onPrimary = Color.White, + primaryContainer = Color(0xFFDDE5F1), onPrimaryContainer = Color(0xFF173252), + secondaryContainer = Color(0xFFE5E3DF), onSecondaryContainer = Color(0xFF292929), + surface = Color(0xFFF4F3F0), background = Color(0xFFF4F3F0), + surfaceContainer = Color(0xFFEDEBE7), surfaceContainerHigh = Color(0xFFFDFCFA), + surfaceContainerHighest = Color(0xFFE1DFDB), + onSurface = Color(0xFF252525), onSurfaceVariant = Color(0xFF696762), + outlineVariant = Color(0xFFD2D0CC), + ) + MaterialTheme(colorScheme = scheme) { + CompositionLocalProvider(LocalBaseUITheme provides rememberBaseUITheme()) { + content() + } + } +} + +@Composable +fun WelcomeBackground(modifier: Modifier = Modifier) { + val scheme = MaterialTheme.colorScheme + Box( + modifier.fillMaxSize().background( + Brush.verticalGradient(listOf(scheme.surfaceContainer, scheme.surface)), + ) + ) +} + +@Composable +fun WelcomeWordmark( + modifier: Modifier = Modifier, + fontSize: TextUnit = MaterialTheme.typography.displaySmall.fontSize +) { + Text( + stringResource(Res.string.welcome_app_name), + modifier = modifier, + fontFamily = FontFamily(Font(Res.font.cookie_regular)), + fontSize = fontSize, + color = MaterialTheme.colorScheme.onSurface, + ) +} diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/welcome/pages/BrandingPage.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/welcome/pages/BrandingPage.kt new file mode 100644 index 00000000..c0f432f6 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/welcome/pages/BrandingPage.kt @@ -0,0 +1,46 @@ +package dev.krtirtho.spotube.modules.welcome.pages + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import dev.krtirtho.spotube.modules.welcome.WelcomeWordmark +import org.jetbrains.compose.resources.painterResource +import org.jetbrains.compose.resources.stringResource +import spotube.composeapp.generated.resources.* + +@Composable +fun BrandingPage(modifier: Modifier = Modifier) { + Column( + modifier = modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(32.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center, + ) { + Image(painterResource(Res.drawable.spotube_logo), null, Modifier.size(88.dp)) + Spacer(Modifier.height(24.dp)) + WelcomeWordmark(fontSize = 48.sp) + Spacer(Modifier.height(24.dp)) + Text( + stringResource(Res.string.welcome_brand_freedom), + style = MaterialTheme.typography.displaySmall.copy(fontFamily = FontFamily.Serif), + textAlign = TextAlign.Center, + ) + Spacer(Modifier.height(16.dp)) + Text( + stringResource(Res.string.welcome_brand_quiet_description), + modifier = Modifier.widthIn(max = 440.dp), + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.colorScheme.onSurfaceVariant, + textAlign = TextAlign.Center, + ) + } +} diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/welcome/pages/DonationPage.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/welcome/pages/DonationPage.kt new file mode 100644 index 00000000..39dcbaf5 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/welcome/pages/DonationPage.kt @@ -0,0 +1,70 @@ +package dev.krtirtho.spotube.modules.welcome.pages + +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import dev.krtirtho.spotube.core.ui.base.OutlineButton +import dev.krtirtho.spotube.modules.welcome.DonationOption +import org.jetbrains.compose.resources.stringResource +import spotube.composeapp.generated.resources.* + +@OptIn(ExperimentalLayoutApi::class) +@Composable +fun DonationPage( + donations: List, + onDonate: (String) -> Unit, + modifier: Modifier = Modifier, +) { + Column( + modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(32.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center, + ) { + Text( + stringResource(Res.string.welcome_donate_quiet_title), + style = MaterialTheme.typography.displaySmall.copy(fontFamily = FontFamily.Serif), + textAlign = TextAlign.Center, + ) + Spacer(Modifier.height(20.dp)) + Text( + stringResource(Res.string.welcome_donate_letter), + modifier = Modifier.widthIn(max = 540.dp), + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.colorScheme.onSurfaceVariant, + textAlign = TextAlign.Center, + ) + Spacer(Modifier.height(32.dp)) + FlowRow( + horizontalArrangement = Arrangement.spacedBy(12.dp, Alignment.CenterHorizontally), + verticalArrangement = Arrangement.spacedBy(12.dp), + ) { + donations.forEach { option -> + OutlineButton(onClick = { onDonate(option.url) }) { + Icon( + option.icon, null, Modifier.size(24.dp), + tint = if (option.id == "patreon") MaterialTheme.colorScheme.onSurface else Color.Unspecified, + ) + Text(stringResource(option.title)) + } + } + } + Spacer(Modifier.height(24.dp)) + Text( + stringResource(Res.string.welcome_donate_optional), + modifier = Modifier.widthIn(max = 500.dp), + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + textAlign = TextAlign.Center, + ) + } +} diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/welcome/pages/HowItWorksPage.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/welcome/pages/HowItWorksPage.kt new file mode 100644 index 00000000..bfd8a2b1 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/welcome/pages/HowItWorksPage.kt @@ -0,0 +1,181 @@ +package dev.krtirtho.spotube.modules.welcome.pages + +import androidx.compose.animation.core.* +import androidx.compose.foundation.Canvas +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.rotate +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.drawscope.Stroke +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.unit.dp +import dev.krtirtho.spotube.core.ui.base.Card +import dev.krtirtho.spotube.core.ui.base.GhostIconButton +import dev.krtirtho.spotube.resources.iconsax.* +import org.jetbrains.compose.resources.stringResource +import spotube.composeapp.generated.resources.* +import kotlin.math.PI +import kotlin.math.sin + +@Composable +fun HowItWorksPage( + isPlaying: Boolean, + onTogglePlayback: () -> Unit, + modifier: Modifier = Modifier +) { + BoxWithConstraints(modifier.fillMaxSize()) { + val wide = maxWidth >= 760.dp + if (wide) { + Row( + Modifier.fillMaxSize().padding(40.dp), + horizontalArrangement = Arrangement.spacedBy(48.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + PlayerDemonstration(isPlaying, onTogglePlayback, Modifier.weight(1f)) + ListeningIntroduction(Modifier.weight(1f)) + } + } else { + Column( + Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(24.dp), + verticalArrangement = Arrangement.spacedBy(32.dp, Alignment.CenterVertically), + ) { + ListeningIntroduction() + PlayerDemonstration(isPlaying, onTogglePlayback) + } + } + } +} + +@Composable +private fun ListeningIntroduction(modifier: Modifier = Modifier) { + Column(modifier, verticalArrangement = Arrangement.spacedBy(20.dp)) { + Text( + stringResource(Res.string.welcome_how_quiet_title), + style = MaterialTheme.typography.headlineLarge.copy(fontFamily = FontFamily.Serif), + ) + Text( + stringResource(Res.string.welcome_how_quiet_description), + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } +} + +/** Silent, illustrative player. The ViewModel owns the play/pause interaction. */ +@Composable +private fun PlayerDemonstration( + isPlaying: Boolean, + onTogglePlayback: () -> Unit, + modifier: Modifier = Modifier +) { + val motion = rememberInfiniteTransition(label = "welcome_record") + val rotation by motion.animateFloat( + 0f, 360f, + infiniteRepeatable(tween(12000, easing = LinearEasing)), label = "record_rotation" + ) + val phase by motion.animateFloat( + 0f, (2 * PI).toFloat(), + infiniteRepeatable(tween(2400, easing = LinearEasing)), label = "audio_meter" + ) + val scheme = MaterialTheme.colorScheme + + Card(modifier.fillMaxWidth()) { + Column(Modifier.padding(24.dp), verticalArrangement = Arrangement.spacedBy(20.dp)) { + Text( + stringResource(if (isPlaying) Res.string.welcome_demo_streaming else Res.string.welcome_demo_paused), + style = MaterialTheme.typography.labelSmall, + color = scheme.onSurfaceVariant, + ) + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(20.dp) + ) { + Canvas(Modifier.size(104.dp).rotate(if (isPlaying) rotation else 0f)) { + val radius = size.minDimension / 2f + drawCircle(Color(0xFF171717)) + for (groove in 1..8) { + drawCircle( + Color(0xFF343434), + radius * (0.4f + groove * 0.065f), + style = Stroke(1f) + ) + } + drawCircle(Color(0xFF334A6B), radius * 0.32f) + drawCircle(Color(0xFFE1DDD5), radius * 0.045f) + // An off-centre label mark makes the turntable's motion visible. + drawCircle( + Color(0xFFD6CEC0), + radius * 0.025f, + Offset(center.x, center.y - radius * 0.2f) + ) + } + Column(Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(6.dp)) { + Text( + stringResource(Res.string.welcome_how_mockup_title), + style = MaterialTheme.typography.titleLarge + ) + Text( + stringResource(Res.string.welcome_how_mockup_artist), + style = MaterialTheme.typography.bodySmall, color = scheme.onSurfaceVariant + ) + Canvas(Modifier.fillMaxWidth().height(32.dp)) { + val count = 18 + val barWidth = size.width / (count * 1.7f) + repeat(count) { index -> + val wave = if (isPlaying) (sin(phase + index * 0.6f) + 1f) / 2f else 0f + val height = size.height * (0.12f + wave * 0.88f) + drawRect( + scheme.primary, + Offset(index * size.width / count, size.height - height), + androidx.compose.ui.geometry.Size(barWidth, height) + ) + } + } + } + } + Text( + stringResource(Res.string.welcome_how_mockup_lyric), + style = MaterialTheme.typography.bodyMedium.copy(fontFamily = FontFamily.Serif), + color = scheme.onSurfaceVariant + ) + Row( + Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + Iconsax.IconsaxPrevious, + null, + Modifier.size(18.dp), + tint = scheme.onSurfaceVariant + ) + Spacer(Modifier.width(24.dp)) + GhostIconButton(onClick = onTogglePlayback, modifier = Modifier.size(48.dp)) { + Icon( + if (isPlaying) Iconsax.IconsaxPause else Iconsax.IconsaxPlay, + stringResource(if (isPlaying) Res.string.welcome_demo_pause_action else Res.string.welcome_demo_play_action), + Modifier.size(24.dp) + ) + } + Spacer(Modifier.width(24.dp)) + Icon( + Iconsax.IconsaxNext, + null, + Modifier.size(18.dp), + tint = scheme.onSurfaceVariant + ) + } + } + } +} diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/welcome/pages/PluginsPage.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/welcome/pages/PluginsPage.kt new file mode 100644 index 00000000..ab3e7477 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/modules/welcome/pages/PluginsPage.kt @@ -0,0 +1,10 @@ +package dev.krtirtho.spotube.modules.welcome.pages + +import androidx.compose.runtime.Composable +import dev.krtirtho.spotube.modules.plugin.PluginScreen + +/** The same installation, discovery, permissions and sign-in flow as Settings, in-place. */ +@Composable +fun PluginsPage() { + PluginScreen(onboarding = true) +} diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/resources/iconsax/BrandBuyMeACoffee.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/resources/iconsax/BrandBuyMeACoffee.kt new file mode 100644 index 00000000..67779257 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/resources/iconsax/BrandBuyMeACoffee.kt @@ -0,0 +1,264 @@ +package dev.krtirtho.spotube.resources.iconsax + +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.PathData +import androidx.compose.ui.graphics.vector.group +import androidx.compose.ui.graphics.vector.path +import androidx.compose.ui.unit.dp + +val Iconsax.BrandBuyMeACoffee: ImageVector + get() { + if (_BrandBuyMeACoffee != null) { + return _BrandBuyMeACoffee!! + } + _BrandBuyMeACoffee = ImageVector.Builder( + name = "BrandBuyMeACoffee", + defaultWidth = 512.dp, + defaultHeight = 512.dp, + viewportWidth = 512f, + viewportHeight = 512f + ).apply { + group( + clipPathData = PathData { + moveTo(0f, 0f) + horizontalLineToRelative(512f) + verticalLineToRelative(512f) + horizontalLineTo(0f) + close() + } + ) { + path(fill = SolidColor(Color(0xFF0D0C22))) { + moveTo(256.17f, 256.45f) + lineToRelative(-0.35f, -0.21f) + lineTo(255f, 256f) + arcToRelative(1.93f, 1.93f, 0f, isMoreThanHalf = false, isPositiveArc = false, 1.16f, 0.45f) + close() + moveTo(256.15f, 256.04f) + arcToRelative(0.75f, 0.75f, 0f, isMoreThanHalf = false, isPositiveArc = true, -0.15f, -0.04f) + arcToRelative(0.54f, 0.54f, 0f, isMoreThanHalf = false, isPositiveArc = false, 0f, 0.1f) + arcToRelative(0.3f, 0.3f, 0f, isMoreThanHalf = false, isPositiveArc = false, 0.15f, -0.06f) + close() + } + path(fill = SolidColor(Color(0xFF0D0C22))) { + moveTo(256f, 256.03f) + horizontalLineToRelative(0.05f) + verticalLineTo(256f) + lineToRelative(-0.05f, 0.03f) + close() + moveTo(255f, 256.67f) + lineToRelative(0.59f, -0.34f) + lineToRelative(0.22f, -0.12f) + lineToRelative(0.2f, -0.21f) + arcToRelative(3.39f, 3.39f, 0f, isMoreThanHalf = false, isPositiveArc = false, -1.01f, 0.68f) + close() + } + path(fill = SolidColor(Color(0xFF0D0C22))) { + moveTo(255.97f, 256.77f) + lineToRelative(-0.58f, -0.55f) + lineTo(255f, 256f) + curveToRelative(0.21f, 0.37f, 0.56f, 0.65f, 0.97f, 0.76f) + close() + } + path(fill = SolidColor(Color(0xFF0D0C22))) { + moveTo(256.18f, 256f) + curveToRelative(-0.46f, 0.2f, -0.87f, 0.51f, -1.18f, 0.91f) + lineToRelative(0.37f, -0.23f) + curveToRelative(0.25f, -0.23f, 0.6f, -0.5f, 0.81f, -0.68f) + close() + } + path(fill = SolidColor(Color(0xFF0D0C22))) { + moveTo(256.2f, 255.27f) + curveToRelative(0f, -0.52f, -0.25f, -0.43f, -0.19f, 1.43f) + curveToRelative(0f, -0.15f, 0.06f, -0.3f, 0.09f, -0.44f) + curveToRelative(0.04f, -0.33f, 0.06f, -0.66f, 0.1f, -0.99f) + close() + } + path(fill = SolidColor(Color(0xFF0D0C22))) { + moveTo(256.18f, 256f) + arcToRelative(3.02f, 3.02f, 0f, isMoreThanHalf = false, isPositiveArc = false, -1.18f, 0.91f) + lineToRelative(0.37f, -0.23f) + curveToRelative(0.25f, -0.23f, 0.6f, -0.5f, 0.81f, -0.68f) + close() + } + path(fill = SolidColor(Color(0xFF0D0C22))) { + moveTo(256.24f, 256.58f) + arcTo(2.52f, 2.52f, 0f, isMoreThanHalf = false, isPositiveArc = false, 255f, 256f) + curveToRelative(0.37f, 0.18f, 0.74f, 0.36f, 0.99f, 0.5f) + lineToRelative(0.25f, 0.08f) + close() + moveTo(256.49f, 256.54f) + arcTo(3.96f, 3.96f, 0f, isMoreThanHalf = false, isPositiveArc = false, 256f, 255f) + curveToRelative(0.19f, 0.49f, 0.35f, 1f, 0.47f, 1.51f) + lineToRelative(0.01f, 0.03f) + close() + } + path(fill = SolidColor(Color(0xFFFFDD00))) { + moveTo(267.82f, 145.08f) + curveToRelative(-18.4f, 7.87f, -39.28f, 16.8f, -66.33f, 16.8f) + arcTo(125.64f, 125.64f, 0f, isMoreThanHalf = false, isPositiveArc = true, 168f, 157.26f) + lineToRelative(18.71f, 192.07f) + arcToRelative(32.09f, 32.09f, 0f, isMoreThanHalf = false, isPositiveArc = false, 32f, 29.45f) + reflectiveCurveToRelative(26.53f, 1.38f, 35.39f, 1.38f) + curveToRelative(9.53f, 0f, 38.1f, -1.38f, 38.1f, -1.38f) + arcToRelative(32.1f, 32.1f, 0f, isMoreThanHalf = false, isPositiveArc = false, 31.99f, -29.45f) + lineToRelative(20.04f, -212.25f) + curveToRelative(-8.96f, -3.06f, -18f, -5.09f, -28.19f, -5.09f) + curveToRelative(-17.63f, -0.01f, -31.83f, 6.06f, -48.23f, 13.08f) + close() + } + path(fill = SolidColor(Color(0xFF0D0C22))) { + moveTo(256f, 256f) + lineToRelative(0.32f, 0.3f) + lineToRelative(0.21f, 0.12f) + arcTo(3.21f, 3.21f, 0f, isMoreThanHalf = false, isPositiveArc = false, 256f, 256f) + close() + } + path(fill = SolidColor(Color(0xFF0D0C22))) { + moveTo(431.23f, 136.85f) + lineToRelative(-2.82f, -14.21f) + curveToRelative(-2.53f, -12.75f, -8.27f, -24.8f, -21.36f, -29.41f) + curveToRelative(-4.2f, -1.47f, -8.96f, -2.11f, -12.18f, -5.16f) + curveToRelative(-3.22f, -3.05f, -4.17f, -7.79f, -4.91f, -12.18f) + curveToRelative(-1.38f, -8.07f, -2.67f, -16.14f, -4.09f, -24.19f) + curveToRelative(-1.22f, -6.92f, -2.18f, -14.7f, -5.36f, -21.05f) + curveToRelative(-4.13f, -8.53f, -12.71f, -13.51f, -21.24f, -16.81f) + arcToRelative(122.38f, 122.38f, 0f, isMoreThanHalf = false, isPositiveArc = false, -13.36f, -4.13f) + curveToRelative(-21.31f, -5.62f, -43.72f, -7.69f, -65.64f, -8.86f) + arcToRelative(551.32f, 551.32f, 0f, isMoreThanHalf = false, isPositiveArc = false, -78.96f, 1.31f) + curveToRelative(-19.54f, 1.78f, -40.12f, 3.93f, -58.69f, 10.68f) + curveToRelative(-6.79f, 2.47f, -13.78f, 5.44f, -18.94f, 10.68f) + curveToRelative(-6.33f, 6.44f, -8.4f, 16.4f, -3.78f, 24.43f) + curveToRelative(3.29f, 5.7f, 8.85f, 9.73f, 14.76f, 12.4f) + arcToRelative(119.72f, 119.72f, 0f, isMoreThanHalf = false, isPositiveArc = false, 23.97f, 7.8f) + curveToRelative(22.94f, 5.07f, 46.71f, 7.06f, 70.15f, 7.91f) + arcToRelative(534.38f, 534.38f, 0f, isMoreThanHalf = false, isPositiveArc = false, 77.86f, -2.54f) + arcToRelative(443.55f, 443.55f, 0f, isMoreThanHalf = false, isPositiveArc = false, 19.15f, -2.53f) + curveToRelative(7.5f, -1.15f, 12.32f, -10.96f, 10.11f, -17.79f) + curveToRelative(-2.65f, -8.17f, -9.76f, -11.34f, -17.8f, -10.1f) + curveToRelative(-1.18f, 0.19f, -2.36f, 0.36f, -3.55f, 0.53f) + lineToRelative(-0.85f, 0.12f) + curveToRelative(-2.72f, 0.34f, -5.45f, 0.67f, -8.17f, 0.96f) + arcToRelative(433.76f, 433.76f, 0f, isMoreThanHalf = false, isPositiveArc = true, -16.92f, 1.49f) + curveToRelative(-12.66f, 0.88f, -25.36f, 1.29f, -38.06f, 1.31f) + curveToRelative(-12.47f, 0f, -24.95f, -0.35f, -37.39f, -1.17f) + arcToRelative(475.74f, 475.74f, 0f, isMoreThanHalf = false, isPositiveArc = true, -16.99f, -1.42f) + curveToRelative(-2.57f, -0.27f, -5.13f, -0.55f, -7.7f, -0.87f) + lineToRelative(-2.44f, -0.31f) + lineToRelative(-0.53f, -0.08f) + lineToRelative(-2.53f, -0.37f) + curveToRelative(-5.17f, -0.78f, -10.34f, -1.67f, -15.45f, -2.76f) + arcToRelative(2.32f, 2.32f, 0f, isMoreThanHalf = false, isPositiveArc = true, 0f, -4.53f) + horizontalLineToRelative(0.1f) + arcToRelative(250.82f, 250.82f, 0f, isMoreThanHalf = false, isPositiveArc = true, 13.37f, -2.45f) + curveToRelative(1.49f, -0.23f, 2.99f, -0.46f, 4.49f, -0.69f) + horizontalLineToRelative(0.04f) + curveToRelative(2.81f, -0.19f, 5.62f, -0.69f, 8.41f, -1.02f) + arcToRelative(535.29f, 535.29f, 0f, isMoreThanHalf = false, isPositiveArc = true, 73.1f, -2.58f) + curveToRelative(11.84f, 0.34f, 23.68f, 1.04f, 35.47f, 2.24f) + arcToRelative(391.9f, 391.9f, 0f, isMoreThanHalf = false, isPositiveArc = true, 7.58f, 0.85f) + curveToRelative(0.96f, 0.12f, 1.94f, 0.25f, 2.91f, 0.37f) + lineToRelative(1.96f, 0.28f) + curveToRelative(5.7f, 0.85f, 11.38f, 1.88f, 17.03f, 3.09f) + curveToRelative(8.36f, 1.82f, 19.11f, 2.41f, 22.83f, 11.57f) + curveToRelative(1.18f, 2.91f, 1.72f, 6.14f, 2.38f, 9.19f) + lineToRelative(0.83f, 3.89f) + curveToRelative(0.02f, 0.07f, 0.04f, 0.14f, 0.05f, 0.21f) + curveToRelative(1.97f, 9.18f, 3.94f, 18.37f, 5.92f, 27.55f) + arcToRelative(5.05f, 5.05f, 0f, isMoreThanHalf = false, isPositiveArc = true, -4.24f, 6.05f) + horizontalLineToRelative(-0.05f) + lineToRelative(-1.21f, 0.17f) + lineToRelative(-1.19f, 0.16f) + curveToRelative(-3.78f, 0.49f, -7.56f, 0.95f, -11.34f, 1.38f) + arcToRelative(732.52f, 732.52f, 0f, isMoreThanHalf = false, isPositiveArc = true, -22.4f, 2.2f) + arcToRelative(784.15f, 784.15f, 0f, isMoreThanHalf = false, isPositiveArc = true, -44.67f, 2.43f) + arcToRelative(802.63f, 802.63f, 0f, isMoreThanHalf = false, isPositiveArc = true, -22.8f, 0.28f) + arcToRelative(791.3f, 791.3f, 0f, isMoreThanHalf = false, isPositiveArc = true, -90.5f, -5.26f) + arcToRelative(944.16f, 944.16f, 0f, isMoreThanHalf = false, isPositiveArc = true, -9.76f, -1.22f) + curveToRelative(2.52f, 0.32f, -1.83f, -0.25f, -2.71f, -0.37f) + curveToRelative(-2.07f, -0.29f, -4.13f, -0.59f, -6.2f, -0.9f) + curveToRelative(-6.94f, -1.04f, -13.84f, -2.32f, -20.76f, -3.44f) + curveToRelative(-8.37f, -1.38f, -16.38f, -0.69f, -23.95f, 3.44f) + arcToRelative(34.83f, 34.83f, 0f, isMoreThanHalf = false, isPositiveArc = false, -14.42f, 14.95f) + curveToRelative(-3.27f, 6.75f, -4.24f, 14.1f, -5.7f, 21.35f) + curveToRelative(-1.46f, 7.25f, -3.73f, 15.06f, -2.87f, 22.5f) + curveToRelative(1.85f, 16.07f, 13.09f, 29.13f, 29.26f, 32.05f) + curveToRelative(15.21f, 2.76f, 30.5f, 4.99f, 45.83f, 6.89f) + arcToRelative(847.2f, 847.2f, 0f, isMoreThanHalf = false, isPositiveArc = false, 196.22f, 1.13f) + arcToRelative(10.34f, 10.34f, 0f, isMoreThanHalf = false, isPositiveArc = true, 11.41f, 11.32f) + lineToRelative(-1.53f, 14.86f) + lineToRelative(-9.25f, 90.1f) + arcToRelative(70942.69f, 70942.69f, 0f, isMoreThanHalf = false, isPositiveArc = true, -9.71f, 94.6f) + lineToRelative(-2.76f, 26.64f) + curveToRelative(-0.88f, 8.74f, -1.01f, 17.76f, -2.67f, 26.4f) + curveToRelative(-2.62f, 13.58f, -11.82f, 21.93f, -25.24f, 24.98f) + arcToRelative(175.93f, 175.93f, 0f, isMoreThanHalf = false, isPositiveArc = true, -37.47f, 4.38f) + curveToRelative(-13.98f, 0.08f, -27.95f, -0.54f, -41.94f, -0.47f) + curveToRelative(-14.93f, 0.08f, -33.2f, -1.29f, -44.72f, -12.4f) + curveToRelative(-10.12f, -9.75f, -11.52f, -25.02f, -12.9f, -38.23f) + arcToRelative(29492.9f, 29492.9f, 0f, isMoreThanHalf = false, isPositiveArc = true, -5.46f, -52.44f) + lineToRelative(-10.13f, -97.18f) + lineToRelative(-6.55f, -62.88f) + curveToRelative(-0.11f, -1.04f, -0.22f, -2.07f, -0.32f, -3.11f) + curveToRelative(-0.79f, -7.5f, -6.1f, -14.84f, -14.47f, -14.46f) + curveToRelative(-7.17f, 0.32f, -15.31f, 6.41f, -14.47f, 14.46f) + lineToRelative(4.86f, 46.62f) + lineToRelative(10.05f, 96.43f) + curveToRelative(2.86f, 27.39f, 5.72f, 54.79f, 8.56f, 82.19f) + curveToRelative(0.55f, 5.25f, 1.07f, 10.51f, 1.65f, 15.76f) + curveToRelative(3.15f, 28.68f, 25.06f, 44.14f, 52.19f, 48.49f) + curveToRelative(15.85f, 2.55f, 32.08f, 3.08f, 48.16f, 3.34f) + curveToRelative(20.62f, 0.33f, 41.44f, 1.13f, 61.72f, -2.61f) + curveToRelative(30.05f, -5.51f, 52.6f, -25.57f, 55.81f, -56.68f) + lineToRelative(2.76f, -26.95f) + curveToRelative(3.05f, -29.72f, 6.11f, -59.44f, 9.15f, -89.17f) + lineToRelative(9.96f, -97.12f) + lineToRelative(4.57f, -44.51f) + arcToRelative(10.34f, 10.34f, 0f, isMoreThanHalf = false, isPositiveArc = true, 8.32f, -9.09f) + curveToRelative(8.59f, -1.67f, 16.81f, -4.53f, 22.92f, -11.07f) + curveToRelative(9.73f, -10.41f, 11.66f, -23.98f, 8.23f, -37.66f) + close() + moveTo(108.01f, 146.45f) + curveToRelative(0.13f, -0.06f, -0.11f, 1.06f, -0.21f, 1.58f) + curveToRelative(-0.02f, -0.79f, 0.02f, -1.49f, 0.21f, -1.58f) + close() + moveTo(108.84f, 152.9f) + curveToRelative(0.07f, -0.05f, 0.28f, 0.23f, 0.49f, 0.56f) + curveToRelative(-0.32f, -0.3f, -0.53f, -0.53f, -0.5f, -0.56f) + horizontalLineToRelative(0.01f) + close() + moveTo(109.66f, 153.98f) + curveToRelative(0.45f, 0.82f, 0.3f, 0.5f, 0f, 0f) + close() + moveTo(111.31f, 155.31f) + horizontalLineToRelative(0.04f) + curveToRelative(0f, 0.05f, 0.08f, 0.1f, 0.1f, 0.14f) + arcToRelative(1.04f, 1.04f, 0f, isMoreThanHalf = false, isPositiveArc = false, -0.15f, -0.14f) + horizontalLineToRelative(0.01f) + close() + moveTo(399.68f, 153.32f) + curveToRelative(-3.09f, 2.93f, -7.74f, 4.3f, -12.33f, 4.98f) + curveToRelative(-51.54f, 7.64f, -103.83f, 11.52f, -155.94f, 9.81f) + curveToRelative(-37.29f, -1.27f, -74.19f, -5.41f, -111.11f, -10.63f) + curveToRelative(-3.62f, -0.51f, -7.54f, -1.17f, -10.02f, -3.84f) + curveToRelative(-4.69f, -5.03f, -2.38f, -15.15f, -1.16f, -21.23f) + curveToRelative(1.12f, -5.57f, 3.25f, -12.98f, 9.87f, -13.78f) + curveToRelative(10.34f, -1.21f, 22.34f, 3.15f, 32.56f, 4.7f) + arcToRelative(616.95f, 616.95f, 0f, isMoreThanHalf = false, isPositiveArc = false, 37.07f, 4.51f) + curveToRelative(52.93f, 4.82f, 106.75f, 4.07f, 159.45f, -2.98f) + arcToRelative(666.2f, 666.2f, 0f, isMoreThanHalf = false, isPositiveArc = false, 28.71f, -4.5f) + curveToRelative(8.5f, -1.52f, 17.92f, -4.38f, 23.05f, 4.41f) + curveToRelative(3.52f, 5.99f, 3.99f, 14.01f, 3.44f, 20.78f) + arcToRelative(11.59f, 11.59f, 0f, isMoreThanHalf = false, isPositiveArc = true, -3.6f, 7.76f) + horizontalLineToRelative(0.01f) + close() + } + } + }.build() + + return _BrandBuyMeACoffee!! + } + +@Suppress("ObjectPropertyName") +private var _BrandBuyMeACoffee: ImageVector? = null diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/resources/iconsax/BrandOpencollective.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/resources/iconsax/BrandOpencollective.kt new file mode 100644 index 00000000..cd5ba60f --- /dev/null +++ b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/resources/iconsax/BrandOpencollective.kt @@ -0,0 +1,58 @@ +package dev.krtirtho.spotube.resources.iconsax + +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.PathFillType +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 + +val Iconsax.BrandOpencollective: ImageVector + get() { + if (_BrandOpencollective != null) { + return _BrandOpencollective!! + } + _BrandOpencollective = ImageVector.Builder( + name = "BrandOpencollective", + defaultWidth = 64.dp, + defaultHeight = 64.dp, + viewportWidth = 64f, + viewportHeight = 64f + ).apply { + path( + fill = SolidColor(Color(0xFFB8D3F4)), + pathFillType = PathFillType.EvenOdd + ) { + moveTo(52.402f, 31.916f) + curveToRelative(0f, 4.03f, -1.17f, 7.895f, -3.178f, 11.087f) + lineToRelative(8.196f, 8.23f) + curveToRelative(4.014f, -5.375f, 6.523f, -12.094f, 6.523f, -19.318f) + reflectiveCurveToRelative(-2.51f, -13.942f, -6.523f, -19.318f) + lineToRelative(-8.196f, 8.23f) + curveToRelative(2.007f, 3.192f, 3.178f, 6.887f, 3.178f, 11.087f) + close() + } + path( + fill = SolidColor(Color(0xFF3385FF)), + pathFillType = PathFillType.EvenOdd + ) { + moveTo(32.004f, 52.41f) + curveToRelative(-11.207f, 0f, -20.406f, -9.24f, -20.406f, -20.493f) + reflectiveCurveToRelative(9.2f, -20.493f, 20.406f, -20.493f) + curveToRelative(4.182f, 0f, 7.86f, 1.176f, 11.04f, 3.36f) + lineToRelative(8.196f, -8.23f) + curveTo(45.887f, 2.52f, 39.197f, 0f, 32.004f, 0f) + curveTo(14.44f, 0f, 0.057f, 14.278f, 0.057f, 32.084f) + reflectiveCurveTo(14.44f, 64f, 32.004f, 64f) + curveToRelative(7.36f, 0f, 14.05f, -2.52f, 19.403f, -6.55f) + lineToRelative(-8.196f, -8.23f) + curveToRelative(-3.178f, 2.016f, -7.025f, 3.192f, -11.207f, 3.192f) + close() + } + }.build() + + return _BrandOpencollective!! + } + +@Suppress("ObjectPropertyName") +private var _BrandOpencollective: ImageVector? = null diff --git a/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/resources/iconsax/BrandPatreon.kt b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/resources/iconsax/BrandPatreon.kt new file mode 100644 index 00000000..7029e29b --- /dev/null +++ b/composeApp/src/commonMain/kotlin/dev/krtirtho/spotube/resources/iconsax/BrandPatreon.kt @@ -0,0 +1,54 @@ +package dev.krtirtho.spotube.resources.iconsax + +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.graphics.StrokeJoin +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.graphics.vector.PathData +import androidx.compose.ui.graphics.vector.group +import androidx.compose.ui.graphics.vector.path +import androidx.compose.ui.unit.dp + +val Iconsax.BrandPatreon: ImageVector + get() { + if (_BrandPatreon != null) { + return _BrandPatreon!! + } + _BrandPatreon = ImageVector.Builder( + name = "BrandPatreon", + defaultWidth = 512.dp, + defaultHeight = 512.dp, + viewportWidth = 512f, + viewportHeight = 512f + ).apply { + group( + clipPathData = PathData { + moveTo(0.38f, 0.42f) + lineToRelative(512f, 0f) + lineToRelative(0f, 512f) + lineTo(0.38f, 512.42f) + close() + } + ) { + path( + fill = SolidColor(Color.Black), + strokeLineJoin = StrokeJoin.Round + ) { + moveTo(490.12f, 154.23f) + curveToRelative(-0.09f, -65.37f, -51.01f, -118.95f, -110.74f, -138.29f) + curveToRelative(-74.18f, -24.01f, -172.02f, -20.53f, -242.86f, 12.89f) + curveToRelative(-85.85f, 40.51f, -112.82f, 129.26f, -113.83f, 217.77f) + curveToRelative(-0.82f, 72.77f, 6.44f, 264.43f, 114.54f, 265.8f) + curveToRelative(80.33f, 1.02f, 92.29f, -102.48f, 129.45f, -152.33f) + curveToRelative(26.44f, -35.47f, 60.49f, -45.48f, 102.4f, -55.85f) + curveToRelative(72.03f, -17.83f, 121.13f, -74.68f, 121.03f, -149.99f) + close() + } + } + }.build() + + return _BrandPatreon!! + } + +@Suppress("ObjectPropertyName") +private var _BrandPatreon: ImageVector? = null diff --git a/composeApp/src/jvmMain/kotlin/dev/krtirtho/spotube/core/ui/component/ApplicationMainBar.jvm.kt b/composeApp/src/jvmMain/kotlin/dev/krtirtho/spotube/core/ui/component/ApplicationMainBar.jvm.kt index cd346728..fb1c63fe 100644 --- a/composeApp/src/jvmMain/kotlin/dev/krtirtho/spotube/core/ui/component/ApplicationMainBar.jvm.kt +++ b/composeApp/src/jvmMain/kotlin/dev/krtirtho/spotube/core/ui/component/ApplicationMainBar.jvm.kt @@ -31,6 +31,8 @@ import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.ui.graphics.Color import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue @@ -59,6 +61,7 @@ actual fun ApplicationMainBar( subtitle: @Composable () -> Unit, actions: @Composable (RowScope.() -> Unit), backButton: Boolean, + transparent: Boolean, ) { Box( modifier = Modifier.fillMaxWidth() @@ -75,6 +78,14 @@ actual fun ApplicationMainBar( } }, subtitle = subtitle, + colors = if (transparent) { + TopAppBarDefaults.topAppBarColors( + containerColor = Color.Transparent, + scrolledContainerColor = Color.Transparent, + ) + } else { + TopAppBarDefaults.topAppBarColors() + }, modifier = Modifier.padding(end = 125.dp) // To avoid overlap with window buttons ) LocalWindowScope.current.WindowControls( diff --git a/composeApp/src/mobileMain/kotlin/dev/krtirtho/spotube/core/ui/component/ApplicationMainBar.mobile.kt b/composeApp/src/mobileMain/kotlin/dev/krtirtho/spotube/core/ui/component/ApplicationMainBar.mobile.kt index 47639afc..37f6b279 100644 --- a/composeApp/src/mobileMain/kotlin/dev/krtirtho/spotube/core/ui/component/ApplicationMainBar.mobile.kt +++ b/composeApp/src/mobileMain/kotlin/dev/krtirtho/spotube/core/ui/component/ApplicationMainBar.mobile.kt @@ -21,6 +21,8 @@ import androidx.compose.foundation.layout.RowScope import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.ui.graphics.Color import androidx.compose.runtime.Composable @OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class) @@ -29,12 +31,21 @@ actual fun ApplicationMainBar( title: @Composable (() -> Unit), subtitle: @Composable () -> Unit, actions: @Composable (RowScope.() -> Unit), - backButton: Boolean + backButton: Boolean, + transparent: Boolean, ) { return TopAppBar( title = title, subtitle = subtitle, actions = actions, + colors = if (transparent) { + TopAppBarDefaults.topAppBarColors( + containerColor = Color.Transparent, + scrolledContainerColor = Color.Transparent, + ) + } else { + TopAppBarDefaults.topAppBarColors() + }, navigationIcon = { if (backButton) { ApplicationBackButton()