Compare commits

...

2 Commits

Author SHA1 Message Date
Kingkor Roy Tirtho
fe0ebb47fe Refactor webview configuration and data clearing functions for iOS and JVM platforms
- Updated `platformWebviewConfig` function to accept an optional `pluginId` parameter for both iOS and JVM implementations.
- Enhanced data directory handling in JVM to create a separate directory for each plugin.
- Implemented `platformClearWebviewData` function for both platforms to clear webview data based on the provided `pluginId`.
- Added necessary imports for iOS and JVM specific functionalities.
2026-07-30 22:53:15 +06:00
Kingkor Roy Tirtho
14ee4f6a4c chore: remove dependabot configuration file 2026-07-30 22:51:07 +06:00
10 changed files with 2215 additions and 2189 deletions

View File

@ -1,16 +0,0 @@
version: 2
enable-beta-ecosystems: true
updates:
- package-ecosystem: "pub"
directory: "/"
schedule:
interval: "daily"
target-branch: "dev"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
target-branch: "dev"

View File

@ -17,8 +17,20 @@
package dev.krtirtho.spotube.core.webview package dev.krtirtho.spotube.core.webview
import android.webkit.CookieManager
import android.webkit.WebStorage
import android.webkit.WebView
import io.github.kdroidfilter.webview.web.WebViewState import io.github.kdroidfilter.webview.web.WebViewState
actual fun platformWebviewConfig(webView: WebViewState) { actual fun platformWebviewConfig(webView: WebViewState, pluginId: String?) {
webView.webView?.nativeWebView?.settings?.domStorageEnabled = true val nativeWebView = webView.webView?.nativeWebView as? WebView ?: return
nativeWebView.settings.domStorageEnabled = true
}
actual suspend fun platformClearWebviewData(pluginId: String?) {
if (pluginId == null) return
CookieManager.getInstance().removeAllCookies(null)
CookieManager.getInstance().flush()
WebStorage.getInstance().deleteAllData()
} }

View File

@ -38,6 +38,8 @@ class WebViewController(val navigationCommands: NavigationCommands): KoinCompone
private var cookieManager: CookieManager? = null private var cookieManager: CookieManager? = null
private val urlFlow = MutableStateFlow("") private val urlFlow = MutableStateFlow("")
private val webViewCreated = MutableSharedFlow<Unit>(replay = 1) private val webViewCreated = MutableSharedFlow<Unit>(replay = 1)
var currentPluginId: String? = null
private set
suspend fun getCookies(url: String): List<Cookie> { suspend fun getCookies(url: String): List<Cookie> {
if (cookieManager == null) { if (cookieManager == null) {
@ -104,19 +106,21 @@ class WebViewController(val navigationCommands: NavigationCommands): KoinCompone
webViewNavigator = null webViewNavigator = null
} }
fun navigateTo(url: String) { fun navigateTo(url: String, pluginId: String) {
if (this.content != null) { if (this.content != null) {
throw IllegalStateException("WebView is already open. Please close the current WebView before navigating to a new URL.") throw IllegalStateException("WebView is already open. Please close the current WebView before navigating to a new URL.")
} }
this.currentPluginId = pluginId
this.content = url this.content = url
this.isHtmlContent = false this.isHtmlContent = false
navigationCommands.navigateTo(Routes.WebView) navigationCommands.navigateTo(Routes.WebView)
} }
fun navigateToHTML(html: String) { fun navigateToHTML(html: String, pluginId: String) {
if (this.content != null) { if (this.content != null) {
throw IllegalStateException("WebView is already open. Please close the current WebView before navigating to a new URL.") throw IllegalStateException("WebView is already open. Please close the current WebView before navigating to a new URL.")
} }
this.currentPluginId = pluginId
this.content = html this.content = html
this.isHtmlContent = true this.isHtmlContent = true
navigationCommands.navigateTo(Routes.WebView) navigationCommands.navigateTo(Routes.WebView)
@ -138,12 +142,15 @@ class WebViewController(val navigationCommands: NavigationCommands): KoinCompone
return completer.await() return completer.await()
} }
suspend fun clearData() { suspend fun clearData(pluginId: String? = null) {
cookieManager?.removeAllCookies() cookieManager?.removeAllCookies()
val targetPluginId = pluginId ?: currentPluginId
platformClearWebviewData(targetPluginId)
cookieManager = null cookieManager = null
content = null content = null
isHtmlContent = false isHtmlContent = false
webViewNavigator = null webViewNavigator = null
currentPluginId = null
} }
val urlChangedFlow = urlFlow.asStateFlow() val urlChangedFlow = urlFlow.asStateFlow()

View File

@ -19,4 +19,6 @@ package dev.krtirtho.spotube.core.webview
import io.github.kdroidfilter.webview.web.WebViewState import io.github.kdroidfilter.webview.web.WebViewState
expect fun platformWebviewConfig(webView: WebViewState) expect fun platformWebviewConfig(webView: WebViewState, pluginId: String?)
expect suspend fun platformClearWebviewData(pluginId: String?)

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
@ -40,30 +39,25 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import io.github.kdroidfilter.webview.jsbridge.IJsMessageHandler
import io.github.kdroidfilter.webview.jsbridge.JsMessage
import io.github.kdroidfilter.webview.jsbridge.rememberWebViewJsBridge
import io.github.kdroidfilter.webview.web.WebView
import io.github.kdroidfilter.webview.web.rememberWebViewNavigator
import io.github.kdroidfilter.webview.web.WebViewState
import io.github.kdroidfilter.webview.web.WebViewNavigator
import compose.icons.FeatherIcons import compose.icons.FeatherIcons
import compose.icons.feathericons.ChevronLeft 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 kotlinx.coroutines.launch import io.github.kdroidfilter.webview.jsbridge.IJsMessageHandler
import io.github.kdroidfilter.webview.jsbridge.JsMessage
import io.github.kdroidfilter.webview.jsbridge.rememberWebViewJsBridge
import io.github.kdroidfilter.webview.web.WebView
import io.github.kdroidfilter.webview.web.WebViewNavigator
import io.github.kdroidfilter.webview.web.WebViewState
import io.github.kdroidfilter.webview.web.rememberWebViewNavigator
class PostMessageHandler( class PostMessageHandler(
private val onMessageReceived: (String) -> Unit = {} private val onMessageReceived: (String) -> Unit = {}
@ -98,7 +92,7 @@ fun PlatformWebViewScreen(webViewController: WebViewController) {
) )
}.apply { }.apply {
this.content = webViewController.getWebContent() this.content = webViewController.getWebContent()
platformWebviewConfig(this) platformWebviewConfig(this, webViewController.currentPluginId)
} }
val navigator = rememberWebViewNavigator() val navigator = rememberWebViewNavigator()

View File

@ -141,7 +141,7 @@ open class ZiplinePluginService(
} }
private val realHttpClientAPI = RealHttpClientAPI() private val realHttpClientAPI = RealHttpClientAPI()
private val realWebViewAPI = RealWebViewAPI(scope, webViewController) private val realWebViewAPI = RealWebViewAPI(scope, webViewController, pluginInfo.id)
private val persistedStorageAPI = RealPersistedStorageAPI(pluginInfo) private val persistedStorageAPI = RealPersistedStorageAPI(pluginInfo)
private val cryptoAPI = RealCryptoAPI(scope.coroutineContext) private val cryptoAPI = RealCryptoAPI(scope.coroutineContext)

View File

@ -22,26 +22,24 @@ import dev.krtirtho.plugin_interfaces.host_apis.WebViewAPI
import dev.krtirtho.spotube.core.webview.WebViewController import dev.krtirtho.spotube.core.webview.WebViewController
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
class RealWebViewAPI( class RealWebViewAPI(
private val scope: CoroutineScope, private val scope: CoroutineScope,
private val webViewController: WebViewController, private val webViewController: WebViewController,
private val pluginId: String,
) : WebViewAPI { ) : WebViewAPI {
override fun navigateTo(url: String) { override fun navigateTo(url: String) {
scope.launch(Dispatchers.Main) { scope.launch(Dispatchers.Main) {
webViewController.navigateTo(url) webViewController.navigateTo(url, pluginId)
} }
} }
override fun navigateToHTML(html: String) { override fun navigateToHTML(html: String) {
scope.launch { scope.launch {
webViewController.navigateToHTML(html) webViewController.navigateToHTML(html, pluginId)
} }
} }

View File

@ -735,7 +735,7 @@ fun PluginScreen(
selectedService.use { coreAPI.logout() } selectedService.use { coreAPI.logout() }
} }
// should clear webview data after logout // should clear webview data after logout
scope.launch { webviewController.clearData() } scope.launch { webviewController.clearData(plugin.id) }
} }
} else { } else {
null null

View File

@ -18,6 +18,24 @@
package dev.krtirtho.spotube.core.webview package dev.krtirtho.spotube.core.webview
import io.github.kdroidfilter.webview.web.WebViewState import io.github.kdroidfilter.webview.web.WebViewState
import kotlinx.cinterop.ExperimentalForeignApi
import platform.Foundation.NSDate
import platform.Foundation.NSHTTPCookieStorage
import platform.Foundation.distantPast
import platform.WebKit.WKWebsiteDataStore
import platform.WebKit.WKWebsiteDataTypeCookies
import platform.WebKit.WKWebsiteDataTypeLocalStorage
actual fun platformWebviewConfig(webView: WebViewState) { actual fun platformWebviewConfig(webView: WebViewState, pluginId: String?) {
}
@OptIn(ExperimentalForeignApi::class)
actual suspend fun platformClearWebviewData(pluginId: String?) {
if (pluginId == null) return
val dataStore = WKWebsiteDataStore.defaultDataStore()
val dataTypes = setOf(WKWebsiteDataTypeCookies, WKWebsiteDataTypeLocalStorage)
dataStore.removeDataOfTypes(dataTypes, NSDate.distantPast) {}
NSHTTPCookieStorage.sharedHTTPCookieStorage.removeCookiesSinceDate(NSDate.distantPast)
} }

View File

@ -19,13 +19,24 @@ package dev.krtirtho.spotube.core.webview
import dev.krtirtho.spotube.core.paths.Paths import dev.krtirtho.spotube.core.paths.Paths
import io.github.kdroidfilter.webview.web.WebViewState import io.github.kdroidfilter.webview.web.WebViewState
import io.github.vinceglb.filekit.utils.div import okio.FileSystem
import io.github.vinceglb.filekit.utils.toPath import okio.Path.Companion.toPath
import org.koin.core.context.GlobalContext import org.koin.core.context.GlobalContext
actual fun platformWebviewConfig(webView: WebViewState) { actual fun platformWebviewConfig(webView: WebViewState, pluginId: String?) {
val paths = GlobalContext.get().get<Paths>() val paths = GlobalContext.get().get<Paths>()
webView.webSettings.desktopWebSettings.dataDirectory = val baseDir = "${paths.getApplicationCacheDirPath()}/webview_data".toPath()
(paths.getApplicationCacheDirPath().toPath() / "webview_data").toString() val dataDir = if (pluginId != null) baseDir / pluginId else baseDir
webView.webSettings.desktopWebSettings.dataDirectory = dataDir.toString()
}
actual suspend fun platformClearWebviewData(pluginId: String?) {
if (pluginId == null) return
val paths = GlobalContext.get().get<Paths>()
val dataDirStr = "${paths.getApplicationCacheDirPath()}/webview_data/$pluginId"
val dataDir = dataDirStr.toPath()
if (FileSystem.SYSTEM.exists(dataDir)) {
FileSystem.SYSTEM.deleteRecursively(dataDir)
}
} }