Compare commits

..

No commits in common. "95e9d563ec38d7f067dec68e5a60c775f6214625" and "e0b9ab530d7bfe251a3197bfac4ce7f92b58b30e" have entirely different histories.

5 changed files with 75 additions and 115 deletions

View File

@ -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()

View File

@ -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,

View File

@ -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(),

View File

@ -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)

View File

@ -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()
} }
} }