mirror of
https://github.com/KRTirtho/spotube.git
synced 2026-09-20 22:54:00 +00:00
Compare commits
No commits in common. "95e9d563ec38d7f067dec68e5a60c775f6214625" and "e0b9ab530d7bfe251a3197bfac4ce7f92b58b30e" have entirely different histories.
95e9d563ec
...
e0b9ab530d
@ -19,7 +19,6 @@ package dev.krtirtho.spotube
|
|||||||
|
|
||||||
import androidx.compose.animation.ExperimentalSharedTransitionApi
|
import androidx.compose.animation.ExperimentalSharedTransitionApi
|
||||||
import androidx.compose.animation.SharedTransitionLayout
|
import androidx.compose.animation.SharedTransitionLayout
|
||||||
import androidx.compose.foundation.layout.Box
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
@ -42,7 +41,6 @@ import dev.krtirtho.spotube.core.ui.theming.SpotubeTheme
|
|||||||
import dev.krtirtho.spotube.modules.settings.SettingsRepository
|
import dev.krtirtho.spotube.modules.settings.SettingsRepository
|
||||||
import dev.krtirtho.spotube.modules.settings.UserSettings
|
import dev.krtirtho.spotube.modules.settings.UserSettings
|
||||||
import dev.krtirtho.spotube.modules.shell.AppShell
|
import dev.krtirtho.spotube.modules.shell.AppShell
|
||||||
import dev.krtirtho.spotube.modules.webview.WebViewScreen
|
|
||||||
import dev.krtirtho.spotube.resources.iconsax.Iconsax
|
import dev.krtirtho.spotube.resources.iconsax.Iconsax
|
||||||
import dev.krtirtho.spotube.resources.iconsax.IconsaxHome
|
import dev.krtirtho.spotube.resources.iconsax.IconsaxHome
|
||||||
import dev.krtirtho.spotube.resources.iconsax.IconsaxHomeBroken
|
import dev.krtirtho.spotube.resources.iconsax.IconsaxHomeBroken
|
||||||
@ -91,11 +89,7 @@ val tabs = listOf(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@OptIn(
|
@OptIn(KoinExperimentalAPI::class, ExperimentalCoroutinesApi::class, ExperimentalSharedTransitionApi::class)
|
||||||
KoinExperimentalAPI::class,
|
|
||||||
ExperimentalCoroutinesApi::class,
|
|
||||||
ExperimentalSharedTransitionApi::class
|
|
||||||
)
|
|
||||||
@Composable
|
@Composable
|
||||||
fun App(
|
fun App(
|
||||||
content: @Composable () -> Unit = {}
|
content: @Composable () -> Unit = {}
|
||||||
@ -114,29 +108,19 @@ fun App(
|
|||||||
SpotubeTheme(settings = userSettings) {
|
SpotubeTheme(settings = userSettings) {
|
||||||
val baseUITheme = rememberBaseUITheme()
|
val baseUITheme = rememberBaseUITheme()
|
||||||
CompositionLocalProvider(LocalBaseUITheme provides baseUITheme) {
|
CompositionLocalProvider(LocalBaseUITheme provides baseUITheme) {
|
||||||
Box(modifier = Modifier.fillMaxSize()) {
|
AppShell(navigator, navigationState) {
|
||||||
val currentRoute = navigationState.backStacks[navigationState.topLevelRoute]?.last()
|
SharedTransitionLayout {
|
||||||
|
CompositionLocalProvider(
|
||||||
Box(Modifier.fillMaxSize()) {
|
LocalSharedTransitionScope provides this@SharedTransitionLayout,
|
||||||
AppShell(navigator, navigationState) {
|
) {
|
||||||
SharedTransitionLayout {
|
Column {
|
||||||
CompositionLocalProvider(
|
NavDisplay(
|
||||||
LocalSharedTransitionScope provides this@SharedTransitionLayout,
|
modifier = Modifier.fillMaxSize(),
|
||||||
) {
|
onBack = navigator::pop,
|
||||||
Column {
|
entries = navigationState.toEntries(koinEntryProvider())
|
||||||
NavDisplay(
|
)
|
||||||
modifier = Modifier.fillMaxSize(),
|
|
||||||
onBack = navigator::pop,
|
|
||||||
entries = navigationState.toEntries(koinEntryProvider())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
when (currentRoute) {
|
|
||||||
Routes.WebView -> WebViewScreen(koinInject())
|
|
||||||
else -> {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
content()
|
content()
|
||||||
|
|||||||
@ -18,8 +18,9 @@
|
|||||||
package dev.krtirtho.spotube.core.navigation
|
package dev.krtirtho.spotube.core.navigation
|
||||||
|
|
||||||
import androidx.navigation3.runtime.NavKey
|
import androidx.navigation3.runtime.NavKey
|
||||||
import dev.krtirtho.spotube.modules.album.AlbumScreen
|
|
||||||
import dev.krtirtho.spotube.modules.artist.ArtistScreen
|
import dev.krtirtho.spotube.modules.artist.ArtistScreen
|
||||||
|
import dev.krtirtho.spotube.modules.album.AlbumScreen
|
||||||
|
import dev.krtirtho.spotube.modules.artist.ArtistViewModel
|
||||||
import dev.krtirtho.spotube.modules.blacklist.BlacklistScreen
|
import dev.krtirtho.spotube.modules.blacklist.BlacklistScreen
|
||||||
import dev.krtirtho.spotube.modules.home.HomeScreen
|
import dev.krtirtho.spotube.modules.home.HomeScreen
|
||||||
import dev.krtirtho.spotube.modules.library.LibraryScreen
|
import dev.krtirtho.spotube.modules.library.LibraryScreen
|
||||||
@ -30,6 +31,7 @@ import dev.krtirtho.spotube.modules.saved_tracks.SAVED_TRACKS_COLLECTION_ID
|
|||||||
import dev.krtirtho.spotube.modules.saved_tracks.SavedTracksScreen
|
import dev.krtirtho.spotube.modules.saved_tracks.SavedTracksScreen
|
||||||
import dev.krtirtho.spotube.modules.search.SearchScreen
|
import dev.krtirtho.spotube.modules.search.SearchScreen
|
||||||
import dev.krtirtho.spotube.modules.settings.SettingsScreen
|
import dev.krtirtho.spotube.modules.settings.SettingsScreen
|
||||||
|
import dev.krtirtho.spotube.modules.webview.WebViewScreen
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import org.koin.compose.viewmodel.koinViewModel
|
import org.koin.compose.viewmodel.koinViewModel
|
||||||
import org.koin.core.annotation.KoinExperimentalAPI
|
import org.koin.core.annotation.KoinExperimentalAPI
|
||||||
@ -95,7 +97,9 @@ val navigationModule = module {
|
|||||||
navigation<Routes.Plugins> {
|
navigation<Routes.Plugins> {
|
||||||
PluginScreen(pluginManager = get())
|
PluginScreen(pluginManager = get())
|
||||||
}
|
}
|
||||||
navigation<Routes.WebView> {}
|
navigation<Routes.WebView> {
|
||||||
|
WebViewScreen(get())
|
||||||
|
}
|
||||||
navigation<Routes.Playlist> {
|
navigation<Routes.Playlist> {
|
||||||
PlaylistScreen(
|
PlaylistScreen(
|
||||||
playlistId = it.playlistId,
|
playlistId = it.playlistId,
|
||||||
|
|||||||
@ -19,7 +19,6 @@ package dev.krtirtho.spotube.core.webview
|
|||||||
|
|
||||||
import androidx.compose.foundation.BorderStroke
|
import androidx.compose.foundation.BorderStroke
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.WindowInsets
|
import androidx.compose.foundation.layout.WindowInsets
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
@ -52,7 +51,6 @@ import compose.icons.feathericons.ChevronLeft
|
|||||||
import compose.icons.feathericons.ChevronRight
|
import compose.icons.feathericons.ChevronRight
|
||||||
import compose.icons.feathericons.X
|
import compose.icons.feathericons.X
|
||||||
import dev.krtirtho.spotube.core.tools.user_agents.UserAgents
|
import dev.krtirtho.spotube.core.tools.user_agents.UserAgents
|
||||||
import dev.krtirtho.spotube.core.ui.component.ApplicationMainBar
|
|
||||||
import dev.nucleusframework.webview.jsbridge.IJsMessageHandler
|
import dev.nucleusframework.webview.jsbridge.IJsMessageHandler
|
||||||
import dev.nucleusframework.webview.jsbridge.JsMessage
|
import dev.nucleusframework.webview.jsbridge.JsMessage
|
||||||
import dev.nucleusframework.webview.jsbridge.rememberWebViewJsBridge
|
import dev.nucleusframework.webview.jsbridge.rememberWebViewJsBridge
|
||||||
@ -151,63 +149,64 @@ fun PlatformWebViewScreen(webViewController: WebViewController) {
|
|||||||
Scaffold(
|
Scaffold(
|
||||||
contentWindowInsets = WindowInsets.statusBars,
|
contentWindowInsets = WindowInsets.statusBars,
|
||||||
topBar = {
|
topBar = {
|
||||||
ApplicationMainBar(
|
Row(
|
||||||
title = {
|
modifier = Modifier.fillMaxWidth().statusBarsPadding().height(56.dp),
|
||||||
Row(
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
modifier = Modifier.fillMaxWidth().statusBarsPadding().height(56.dp),
|
verticalAlignment = Alignment.CenterVertically
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
) {
|
||||||
verticalAlignment = Alignment.CenterVertically
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.Start,
|
||||||
|
modifier = Modifier.height(56.dp)
|
||||||
|
) {
|
||||||
|
IconButton(
|
||||||
|
onClick = {
|
||||||
|
navigator.navigateBack()
|
||||||
|
}, enabled = navigator.canGoBack
|
||||||
) {
|
) {
|
||||||
Row(
|
Icon(
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
FeatherIcons.ChevronLeft,
|
||||||
horizontalArrangement = Arrangement.Start,
|
contentDescription = "Go back to browser history"
|
||||||
modifier = Modifier.height(56.dp)
|
)
|
||||||
) {
|
}
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
navigator.navigateBack()
|
navigator.navigateForward()
|
||||||
}, enabled = navigator.canGoBack
|
}, enabled = navigator.canGoForward
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
FeatherIcons.ChevronLeft,
|
FeatherIcons.ChevronRight,
|
||||||
contentDescription = "Go back to browser history"
|
contentDescription = "Go forward to browser history"
|
||||||
)
|
)
|
||||||
}
|
|
||||||
IconButton(
|
|
||||||
onClick = {
|
|
||||||
navigator.navigateForward()
|
|
||||||
}, enabled = navigator.canGoForward
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
FeatherIcons.ChevronRight,
|
|
||||||
contentDescription = "Go forward to browser history"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Surface(
|
|
||||||
modifier = Modifier.weight(1f).height(36.dp).padding(horizontal = 4.dp),
|
|
||||||
shape = RoundedCornerShape(18.dp),
|
|
||||||
color = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.5f),
|
|
||||||
border = BorderStroke(1.dp, Color.Gray.copy(alpha = 0.5f))
|
|
||||||
) {
|
|
||||||
BasicTextField(
|
|
||||||
value = state.lastLoadedUrl ?: "",
|
|
||||||
onValueChange = {}, // Read-only
|
|
||||||
readOnly = true,
|
|
||||||
singleLine = true,
|
|
||||||
textStyle = MaterialTheme.typography.bodyMedium.copy(
|
|
||||||
color = MaterialTheme.colorScheme.onSurface,
|
|
||||||
textAlign = TextAlign.Start
|
|
||||||
),
|
|
||||||
modifier = Modifier.fillMaxWidth().padding(horizontal = 12.dp)
|
|
||||||
.wrapContentHeight(Alignment.CenterVertically)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
Surface(
|
||||||
})
|
modifier = Modifier.weight(1f).height(36.dp).padding(horizontal = 4.dp),
|
||||||
{ innerPadding ->
|
shape = RoundedCornerShape(18.dp),
|
||||||
|
color = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.5f),
|
||||||
|
border = BorderStroke(1.dp, Color.Gray.copy(alpha = 0.5f))
|
||||||
|
) {
|
||||||
|
BasicTextField(
|
||||||
|
value = state.lastLoadedUrl ?: "",
|
||||||
|
onValueChange = {}, // Read-only
|
||||||
|
readOnly = true,
|
||||||
|
singleLine = true,
|
||||||
|
textStyle = MaterialTheme.typography.bodyMedium.copy(
|
||||||
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
|
textAlign = TextAlign.Start
|
||||||
|
),
|
||||||
|
modifier = Modifier.fillMaxWidth().padding(horizontal = 12.dp)
|
||||||
|
.wrapContentHeight(Alignment.CenterVertically)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
IconButton(
|
||||||
|
onClick = {
|
||||||
|
webViewController.closeWebview()
|
||||||
|
}) {
|
||||||
|
Icon(FeatherIcons.X, contentDescription = "Close WebView")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}) { innerPadding ->
|
||||||
WebView(
|
WebView(
|
||||||
state = state,
|
state = state,
|
||||||
modifier = Modifier.padding(innerPadding).fillMaxSize(),
|
modifier = Modifier.padding(innerPadding).fillMaxSize(),
|
||||||
|
|||||||
@ -26,8 +26,6 @@ import androidx.compose.runtime.setValue
|
|||||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.window.rememberWindowState
|
import androidx.compose.ui.window.rememberWindowState
|
||||||
import com.sun.jna.Library
|
|
||||||
import com.sun.jna.Native
|
|
||||||
import dev.krtirtho.spotube.core.di.initKoin
|
import dev.krtirtho.spotube.core.di.initKoin
|
||||||
import dev.krtirtho.spotube.core.newpipe.NewPipeDownloader
|
import dev.krtirtho.spotube.core.newpipe.NewPipeDownloader
|
||||||
import dev.krtirtho.spotube.core.paths.Paths
|
import dev.krtirtho.spotube.core.paths.Paths
|
||||||
@ -57,33 +55,8 @@ private object KoinServicesProvider : KoinComponent {
|
|||||||
val settingsProvider: SettingsProvider get() = get()
|
val settingsProvider: SettingsProvider get() = get()
|
||||||
}
|
}
|
||||||
|
|
||||||
private interface LibC : Library {
|
|
||||||
fun setenv(name: String, value: String, overwrite: Int): Int
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val INSTANCE: LibC = Native.load("c", LibC::class.java)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* WebKitGTK is only used on Linux (Windows = WebView2, macOS = WKWebView).
|
|
||||||
* The webview is created *after* the window is already mapped and the Tao
|
|
||||||
* render/swap loop is running. WebKitGTK's accelerated-compositing path then
|
|
||||||
* initialises its own GL context in-process, racing Tao's swap thread on the
|
|
||||||
* same Mesa display, which deterministically segfaults `libgallium` on the
|
|
||||||
* next Compose flush. Disabling WebKit's hardware-accelerated compositing
|
|
||||||
* (and its DMABUF renderer) removes that GL context entirely — login pages
|
|
||||||
* render fine in software. Must run before libwebkit2gtk is loaded.
|
|
||||||
*/
|
|
||||||
private fun disableWebKitGpuCompositing() {
|
|
||||||
if (!System.getProperty("os.name").lowercase().contains("linux")) return
|
|
||||||
LibC.INSTANCE.setenv("WEBKIT_DISABLE_COMPOSITING_MODE", "1", 1)
|
|
||||||
LibC.INSTANCE.setenv("WEBKIT_DISABLE_DMABUF_RENDERER", "1", 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(ExperimentalComposeUiApi::class)
|
@OptIn(ExperimentalComposeUiApi::class)
|
||||||
fun main() {
|
fun main() {
|
||||||
disableWebKitGpuCompositing()
|
|
||||||
FileKit.init(appId = "dev.krtirtho.spotube")
|
FileKit.init(appId = "dev.krtirtho.spotube")
|
||||||
initKoin()
|
initKoin()
|
||||||
NewPipeDownloader.init(KoinPathsProvider.paths)
|
NewPipeDownloader.init(KoinPathsProvider.paths)
|
||||||
|
|||||||
@ -20,7 +20,6 @@ enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
|
|||||||
|
|
||||||
pluginManagement {
|
pluginManagement {
|
||||||
repositories {
|
repositories {
|
||||||
mavenLocal()
|
|
||||||
google {
|
google {
|
||||||
mavenContent {
|
mavenContent {
|
||||||
includeGroupAndSubgroups("androidx")
|
includeGroupAndSubgroups("androidx")
|
||||||
@ -30,13 +29,13 @@ pluginManagement {
|
|||||||
}
|
}
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
gradlePluginPortal()
|
gradlePluginPortal()
|
||||||
|
mavenLocal()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencyResolutionManagement {
|
dependencyResolutionManagement {
|
||||||
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||||
repositories {
|
repositories {
|
||||||
mavenLocal()
|
|
||||||
google {
|
google {
|
||||||
mavenContent {
|
mavenContent {
|
||||||
includeGroupAndSubgroups("androidx")
|
includeGroupAndSubgroups("androidx")
|
||||||
@ -46,6 +45,7 @@ dependencyResolutionManagement {
|
|||||||
}
|
}
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
maven("https://jitpack.io")
|
maven("https://jitpack.io")
|
||||||
|
mavenLocal()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user