init projects
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
package ru.shadowsparky.vbox
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.asPaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.safeDrawing
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.window.ComposeViewport
|
||||
import com.arkivanov.decompose.DefaultComponentContext
|
||||
import com.arkivanov.decompose.ExperimentalDecomposeApi
|
||||
import com.arkivanov.decompose.router.webhistory.withWebHistory
|
||||
import com.arkivanov.essenty.lifecycle.LifecycleRegistry
|
||||
import com.arkivanov.essenty.lifecycle.doOnResume
|
||||
import com.arkivanov.essenty.statekeeper.StateKeeper
|
||||
import kotlinx.browser.document
|
||||
import org.koin.core.annotation.KoinApplication
|
||||
import org.koin.core.annotation.Module
|
||||
import org.koin.core.annotation.Single
|
||||
import org.koin.plugin.module.dsl.startKoin
|
||||
import org.w3c.dom.HTMLElement
|
||||
import ru.shadowsparky.domain.AppConfig
|
||||
import ru.shadowsparky.koin
|
||||
import ru.shadowsparky.ui.hideElementById
|
||||
import ru.shadowsparky.ui.hookPageVisibility
|
||||
import ru.shadowsparky.vbox.shared.presentation.Nav
|
||||
import ru.shadowsparky.vbox.shared.presentation.RootComponent
|
||||
import ru.shadowsparky.vbox.shared.presentation.RootFactory
|
||||
import ru.shadowsparky.vbox.shared.presentation.UrlRoutes
|
||||
import ru.shadowsparky.vbox.shared.presentation.nav.Route
|
||||
|
||||
@KoinApplication
|
||||
object VBox
|
||||
|
||||
@Module
|
||||
class AppModule {
|
||||
|
||||
@Single
|
||||
fun provideAppConfig(): AppConfig {
|
||||
return object : AppConfig {
|
||||
override fun isDebug(): Boolean = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun main() {
|
||||
try {
|
||||
mainInternal()
|
||||
} catch (e: Throwable) {
|
||||
println(e.stackTraceToString())
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalDecomposeApi::class, ExperimentalComposeUiApi::class)
|
||||
fun mainInternal() {
|
||||
val lifecycle = LifecycleRegistry()
|
||||
|
||||
startKoin<VBox>()
|
||||
|
||||
val composeRoot = document.getElementById("compose-root") as HTMLElement
|
||||
|
||||
var rootRef: RootComponent? = null
|
||||
|
||||
val root: RootComponent = withWebHistory { stateKeeper: StateKeeper, deepLinkUrl: String? ->
|
||||
if (rootRef == null) {
|
||||
rootRef = createRoot(lifecycle, stateKeeper, deepLinkUrl)
|
||||
}
|
||||
rootRef
|
||||
}
|
||||
|
||||
hookPageVisibility(lifecycle)
|
||||
lifecycle.doOnResume { hideElementById("loader") }
|
||||
|
||||
ComposeViewport(viewportContainer = composeRoot, content = {
|
||||
val r = remember { root }
|
||||
|
||||
LaunchedEffect(null) {
|
||||
r.onFinish.collect { r.navReplace(Route.Loading()) }
|
||||
}
|
||||
|
||||
val isLoggedIn by r.isLoggedIn.collectAsState(null)
|
||||
LaunchedEffect(isLoggedIn) {
|
||||
if (isLoggedIn == false) {
|
||||
r.navReplace(Route.Loading())
|
||||
}
|
||||
}
|
||||
|
||||
Box(Modifier.fillMaxSize().padding(WindowInsets.safeDrawing.asPaddingValues())) {
|
||||
Nav(r)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun createRoot(
|
||||
lifecycle: LifecycleRegistry,
|
||||
stateKeeper: StateKeeper,
|
||||
deepLinkUrl: String?,
|
||||
): RootComponent {
|
||||
val ctx = DefaultComponentContext(
|
||||
lifecycle = lifecycle, stateKeeper = stateKeeper
|
||||
)
|
||||
val deep = deepLinkUrl?.let { runCatching { UrlRoutes.parseUrl(it) }.getOrNull() }
|
||||
|
||||
val initRoute: Route = deep ?: Route.default
|
||||
|
||||
return koin.get<RootFactory>().create(context = ctx, initStack = initRoute)
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 663 B |
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
@@ -0,0 +1,83 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<title>VBox</title>
|
||||
<base href="/"/>
|
||||
<link href="/manifest.json" rel="manifest">
|
||||
|
||||
<meta content="width=device-width, initial-scale=1, viewport-fit=cover" name="viewport"/>
|
||||
|
||||
<style>
|
||||
|
||||
#loader {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: #111;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border: 6px solid #ccc;
|
||||
border-top-color: #09f;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
html, body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #000;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#vboxCanvas {
|
||||
display: block;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
height: 100dvh;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: #000;
|
||||
touch-action: manipulation;
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- грузим скрипты после DOM -->
|
||||
<script defer src="/composeApp.js"></script>
|
||||
|
||||
<meta content="yes" name="apple-mobile-web-app-capable">
|
||||
<meta content="black" name="apple-mobile-web-app-status-bar-style">
|
||||
<meta content="VBox" name="apple-mobile-web-app-title">
|
||||
|
||||
<link href="icon.png" rel="apple-touch-icon">
|
||||
|
||||
<link href="icon.png" rel="icon" sizes="192x192" type="image/png">
|
||||
<link href="icon-32.png" rel="icon" sizes="32x32" type="image/png">
|
||||
<link href="icon-16.png" rel="icon" sizes="16x16" type="image/png">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app-container" style="position:fixed; inset:0; overflow:hidden;">
|
||||
<div id="loader">
|
||||
<div class="spinner"></div>
|
||||
</div>
|
||||
<div id="video-layer"
|
||||
style="position:absolute; inset:0; background:#000; visibility: hidden;"></div>
|
||||
<div id="compose-root" style="position:absolute; inset:0;"></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "Videobox",
|
||||
"short_name": "VBox",
|
||||
"start_url": "/",
|
||||
"display": "standalone",
|
||||
"icons": [
|
||||
{
|
||||
"src": "icon.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import initSqlJs from "sql.js";
|
||||
|
||||
let db = null;
|
||||
|
||||
async function createDatabase() {
|
||||
let SQL = await initSqlJs({locateFile: file => 'sql-wasm.wasm'});
|
||||
db = new SQL.Database();
|
||||
}
|
||||
|
||||
function onModuleReady() {
|
||||
const data = this.data;
|
||||
|
||||
switch (data && data.action) {
|
||||
case "exec":
|
||||
if (!data["sql"]) {
|
||||
throw new Error("exec: Missing query string");
|
||||
}
|
||||
|
||||
return postMessage({
|
||||
id: data.id,
|
||||
results: db.exec(data.sql, data.params)[0] ?? {values: []}
|
||||
});
|
||||
case "begin_transaction":
|
||||
return postMessage({
|
||||
id: data.id,
|
||||
results: db.exec("BEGIN TRANSACTION;")
|
||||
})
|
||||
case "end_transaction":
|
||||
return postMessage({
|
||||
id: data.id,
|
||||
results: db.exec("END TRANSACTION;")
|
||||
})
|
||||
case "rollback_transaction":
|
||||
return postMessage({
|
||||
id: data.id,
|
||||
results: db.exec("ROLLBACK TRANSACTION;")
|
||||
})
|
||||
default:
|
||||
throw new Error(`Unsupported action: ${data && data.action}`);
|
||||
}
|
||||
}
|
||||
|
||||
function onError(err) {
|
||||
return postMessage({
|
||||
id: this.data.id,
|
||||
error: err
|
||||
});
|
||||
}
|
||||
|
||||
if (typeof importScripts === "function") {
|
||||
db = null;
|
||||
const sqlModuleReady = createDatabase()
|
||||
self.onmessage = (event) => {
|
||||
return sqlModuleReady
|
||||
.then(onModuleReady.bind(event))
|
||||
.catch(onError.bind(event));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user