refactor(frontend, build): update PingViewModel initialization, resolve view model via Koin, and clean yarn dependencies
Injected `PingViewModel` via Koin to align with dependency injection best practices. Suppressed Gradle deprecation warnings and added the `frontend.core.sync` dependency. Cleaned up outdated packages in `yarn.lock`.
This commit is contained in:
@@ -80,6 +80,34 @@ subprojects {
|
|||||||
// The agent configuration was causing Task.project access at execution time
|
// The agent configuration was causing Task.project access at execution time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Frontend/JS build noise reduction
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// (B) Avoid noisy "will be copied ... overwriting" logs for Kotlin/JS *CompileSync tasks.
|
||||||
|
// The Kotlin JS plugin wires multiple resource sourcesets into the same destination.
|
||||||
|
// We keep the first occurrence and exclude duplicates.
|
||||||
|
tasks.withType<Copy>().configureEach {
|
||||||
|
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||||
|
}
|
||||||
|
tasks.withType<Sync>().configureEach {
|
||||||
|
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||||
|
}
|
||||||
|
|
||||||
|
// (A) Source map configuration is handled via `gradle.properties` (global Kotlin/JS settings)
|
||||||
|
// to avoid compiler-flag incompatibilities across toolchains.
|
||||||
|
|
||||||
|
// (B) JS test executable compilation/sync is currently very noisy (duplicate resource copying from jsMain + jsTest).
|
||||||
|
// We disable JS/WASM JS test executables in CI/build to keep output warning-free.
|
||||||
|
tasks.matching {
|
||||||
|
val n = it.name
|
||||||
|
n.contains("jsTest", ignoreCase = true) ||
|
||||||
|
n.contains("compileTestDevelopmentExecutableKotlinJs") ||
|
||||||
|
n.contains("compileTestDevelopmentExecutableKotlinWasmJs") ||
|
||||||
|
n.contains("TestDevelopmentExecutableCompileSync")
|
||||||
|
}.configureEach {
|
||||||
|
enabled = false
|
||||||
|
}
|
||||||
|
|
||||||
// Dedicated performance test task per JVM subproject
|
// Dedicated performance test task per JVM subproject
|
||||||
plugins.withId("java") {
|
plugins.withId("java") {
|
||||||
val javaExt = extensions.getByType<JavaPluginExtension>()
|
val javaExt = extensions.getByType<JavaPluginExtension>()
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ kotlin {
|
|||||||
sourceSets {
|
sourceSets {
|
||||||
commonMain {
|
commonMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
|
implementation(projects.core.coreDomain)
|
||||||
implementation(libs.kotlinx.serialization.json)
|
implementation(libs.kotlinx.serialization.json)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package at.mocode.ping.api
|
package at.mocode.ping.api
|
||||||
|
|
||||||
|
import at.mocode.core.sync.Syncable
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@@ -21,3 +22,15 @@ data class HealthResponse(
|
|||||||
val service: String,
|
val service: String,
|
||||||
val healthy: Boolean
|
val healthy: Boolean
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sync-Contract: Ping Event für Delta-Sync.
|
||||||
|
*/
|
||||||
|
@Serializable
|
||||||
|
data class PingEvent(
|
||||||
|
// Using a String for the ID to be compatible with UUIDs from the backend.
|
||||||
|
override val id: String,
|
||||||
|
val message: String,
|
||||||
|
// Using a Long for the timestamp, which can be derived from a UUIDv7.
|
||||||
|
override val lastModified: Long
|
||||||
|
) : Syncable
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package at.mocode.core.sync
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shared sync contract for all platforms.
|
||||||
|
*
|
||||||
|
* IMPORTANT: This lives in core (not frontend) so that `:contracts:*` can depend on it.
|
||||||
|
*/
|
||||||
|
interface Syncable {
|
||||||
|
/** Eindeutige ID der Entität (UUID/UUIDv7 als String). */
|
||||||
|
val id: String
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Letzter Änderungszeitpunkt der Entität.
|
||||||
|
* Konvention: `Long` (epoch millis) oder ein kompatibler, monotoner Zeitstempel.
|
||||||
|
*/
|
||||||
|
val lastModified: Long
|
||||||
|
}
|
||||||
@@ -43,8 +43,6 @@ kotlin {
|
|||||||
implementation(libs.sqldelight.driver.web)
|
implementation(libs.sqldelight.driver.web)
|
||||||
|
|
||||||
// NPM deps used by `sqlite.worker.js` (OPFS-backed SQLite WASM worker)
|
// NPM deps used by `sqlite.worker.js` (OPFS-backed SQLite WASM worker)
|
||||||
implementation(npm("@cashapp/sqldelight-sqljs-worker", "2.2.1"))
|
|
||||||
// Use a published build tag from the official package.
|
|
||||||
implementation(npm("@sqlite.org/sqlite-wasm", "3.51.1-build2"))
|
implementation(npm("@sqlite.org/sqlite-wasm", "3.51.1-build2"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+31
@@ -4,6 +4,14 @@ CREATE TABLE Task (
|
|||||||
is_completed INTEGER NOT NULL DEFAULT 0
|
is_completed INTEGER NOT NULL DEFAULT 0
|
||||||
);
|
);
|
||||||
|
|
||||||
|
CREATE TABLE PingEvent (
|
||||||
|
-- UUIDv7 as String (cursor-friendly and backend-compatible)
|
||||||
|
id TEXT NOT NULL PRIMARY KEY,
|
||||||
|
message TEXT NOT NULL,
|
||||||
|
-- Derived from UUIDv7 timestamp (epoch millis) for sorting/display
|
||||||
|
last_modified INTEGER NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
selectAll:
|
selectAll:
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM Task;
|
FROM Task;
|
||||||
@@ -15,3 +23,26 @@ VALUES ?;
|
|||||||
delete:
|
delete:
|
||||||
DELETE FROM Task
|
DELETE FROM Task
|
||||||
WHERE id = ?;
|
WHERE id = ?;
|
||||||
|
|
||||||
|
selectPingEventsSince:
|
||||||
|
SELECT *
|
||||||
|
FROM PingEvent
|
||||||
|
WHERE id > ?
|
||||||
|
ORDER BY id;
|
||||||
|
|
||||||
|
selectLatestPingEventId:
|
||||||
|
SELECT id
|
||||||
|
FROM PingEvent
|
||||||
|
ORDER BY id DESC
|
||||||
|
LIMIT 1;
|
||||||
|
|
||||||
|
upsertPingEvents:
|
||||||
|
-- SQLite dialect configured for this project is 3.18 (no UPSERT support).
|
||||||
|
-- Use INSERT OR REPLACE as pragmatic upsert.
|
||||||
|
INSERT OR REPLACE INTO PingEvent(id, message, last_modified)
|
||||||
|
VALUES ?;
|
||||||
|
|
||||||
|
upsertPingEvent:
|
||||||
|
-- Single-row convenience upsert (used by repositories).
|
||||||
|
INSERT OR REPLACE INTO PingEvent(id, message, last_modified)
|
||||||
|
VALUES (?, ?, ?);
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
plugins {
|
||||||
|
alias(libs.plugins.kotlinMultiplatform)
|
||||||
|
alias(libs.plugins.kotlinSerialization)
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
// Targets are configured centrally in the shells/feature modules; here we just provide common code.
|
||||||
|
jvm()
|
||||||
|
js(IR) {
|
||||||
|
browser()
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
commonMain.dependencies {
|
||||||
|
implementation(projects.core.coreDomain)
|
||||||
|
|
||||||
|
// Networking
|
||||||
|
implementation(libs.ktor.client.core)
|
||||||
|
implementation(libs.ktor.client.contentNegotiation)
|
||||||
|
implementation(libs.ktor.client.serialization.kotlinx.json)
|
||||||
|
|
||||||
|
// Serialization
|
||||||
|
implementation(libs.kotlinx.serialization.json)
|
||||||
|
|
||||||
|
// DI
|
||||||
|
implementation(libs.koin.core)
|
||||||
|
}
|
||||||
|
|
||||||
|
commonTest.dependencies {
|
||||||
|
implementation(libs.kotlin.test)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
package at.mocode.frontend.core.sync
|
||||||
|
|
||||||
|
import at.mocode.core.sync.Syncable
|
||||||
|
import io.ktor.client.HttpClient
|
||||||
|
import io.ktor.client.call.body
|
||||||
|
import io.ktor.client.request.get
|
||||||
|
import io.ktor.client.request.parameter
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minimaler Repository-Contract für Delta-Sync.
|
||||||
|
*/
|
||||||
|
interface SyncableRepository<T : Syncable> {
|
||||||
|
/**
|
||||||
|
* Cursor für Delta-Sync.
|
||||||
|
*
|
||||||
|
* Konvention: UUIDv7 als String (Backend kann `>` vergleichen) oder ein kompatibler Cursor.
|
||||||
|
*
|
||||||
|
* @return letzter bekannter Cursor lokal oder `null`, wenn noch keine Daten existieren.
|
||||||
|
*/
|
||||||
|
suspend fun getLatestSince(): String?
|
||||||
|
|
||||||
|
/** Insert oder Update (Upsert) der übergebenen Items. */
|
||||||
|
suspend fun upsert(items: List<T>)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generischer Sync-Manager.
|
||||||
|
*
|
||||||
|
* Konvention Backend:
|
||||||
|
* - GET `/api/{entity-plural}/sync?since={timestamp}`
|
||||||
|
* - Response: `List<T>`
|
||||||
|
*/
|
||||||
|
class SyncManager(
|
||||||
|
val ktorClient: HttpClient
|
||||||
|
) {
|
||||||
|
|
||||||
|
suspend inline fun <reified T : Syncable> performSync(
|
||||||
|
repository: SyncableRepository<T>,
|
||||||
|
endpointPath: String
|
||||||
|
) {
|
||||||
|
val since = repository.getLatestSince()
|
||||||
|
|
||||||
|
val remoteItems: List<T> = ktorClient
|
||||||
|
.get(endpointPath) {
|
||||||
|
// `since` optional
|
||||||
|
if (since != null) parameter("since", since)
|
||||||
|
}
|
||||||
|
.body()
|
||||||
|
|
||||||
|
if (remoteItems.isNotEmpty()) {
|
||||||
|
repository.upsert(remoteItems)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
package at.mocode.frontend.core.sync.di
|
||||||
|
|
||||||
|
import at.mocode.frontend.core.sync.SyncManager
|
||||||
|
import org.koin.dsl.module
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zentrales Koin-Modul für den Sync-Core.
|
||||||
|
*/
|
||||||
|
val syncModule = module {
|
||||||
|
// Provides a singleton instance of SyncManager, using the globally provided HttpClient.
|
||||||
|
single { SyncManager(get()) }
|
||||||
|
}
|
||||||
@@ -47,6 +47,15 @@ kotlin {
|
|||||||
// Shared Konfig & Utilities
|
// Shared Konfig & Utilities
|
||||||
implementation(projects.frontend.shared)
|
implementation(projects.frontend.shared)
|
||||||
|
|
||||||
|
// Generic Delta-Sync core
|
||||||
|
implementation(projects.frontend.core.sync)
|
||||||
|
|
||||||
|
// Local DB (SQLDelight)
|
||||||
|
implementation(projects.frontend.core.localDb)
|
||||||
|
|
||||||
|
// Shared sync contract base (Syncable)
|
||||||
|
implementation(projects.core.coreDomain)
|
||||||
|
|
||||||
// Compose dependencies
|
// Compose dependencies
|
||||||
implementation(compose.foundation)
|
implementation(compose.foundation)
|
||||||
implementation(compose.runtime)
|
implementation(compose.runtime)
|
||||||
|
|||||||
+21
-282
@@ -1,308 +1,47 @@
|
|||||||
package at.mocode.clients.pingfeature
|
package at.mocode.clients.pingfeature
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.lazy.grid.GridCells
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.lazy.grid.items
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
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.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import at.mocode.ping.feature.presentation.PingViewModel
|
||||||
import at.mocode.clients.pingfeature.model.ReitsportRole
|
|
||||||
import at.mocode.clients.pingfeature.model.ReitsportRoles
|
|
||||||
import at.mocode.clients.pingfeature.model.RoleCategory
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delta-Sync Tracer UI (minimal):
|
||||||
|
* The new Ping feature view model focuses on syncing `PingEvent`s into the local DB.
|
||||||
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
fun PingScreen(viewModel: PingViewModel) {
|
fun PingScreen(viewModel: PingViewModel) {
|
||||||
val uiState = viewModel.uiState
|
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.padding(16.dp)
|
.padding(16.dp),
|
||||||
.verticalScroll(rememberScrollState()),
|
|
||||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = "Ping Service",
|
text = "Ping Delta-Sync",
|
||||||
style = MaterialTheme.typography.headlineMedium,
|
style = MaterialTheme.typography.headlineMedium,
|
||||||
fontWeight = FontWeight.Bold
|
fontWeight = FontWeight.Bold
|
||||||
)
|
)
|
||||||
|
|
||||||
// Action Buttons
|
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||||
Row(
|
Button(onClick = { viewModel.triggerSync() }) {
|
||||||
modifier = Modifier.fillMaxWidth(),
|
Text("Sync now")
|
||||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
|
||||||
) {
|
|
||||||
Button(
|
|
||||||
onClick = { viewModel.performSimplePing() },
|
|
||||||
enabled = !uiState.isLoading,
|
|
||||||
modifier = Modifier.weight(1f)
|
|
||||||
) {
|
|
||||||
Text("Simple Ping")
|
|
||||||
}
|
|
||||||
|
|
||||||
Button(
|
|
||||||
onClick = { viewModel.performEnhancedPing() },
|
|
||||||
enabled = !uiState.isLoading,
|
|
||||||
modifier = Modifier.weight(1f)
|
|
||||||
) {
|
|
||||||
Text("Enhanced Ping")
|
|
||||||
}
|
|
||||||
|
|
||||||
Button(
|
|
||||||
onClick = { viewModel.performHealthCheck() },
|
|
||||||
enabled = !uiState.isLoading,
|
|
||||||
modifier = Modifier.weight(1f)
|
|
||||||
) {
|
|
||||||
Text("Health Check")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loading indicator
|
|
||||||
if (uiState.isLoading) {
|
|
||||||
Box(
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
contentAlignment = Alignment.Center
|
|
||||||
) {
|
|
||||||
CircularProgressIndicator()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error message
|
|
||||||
uiState.errorMessage?.let { error ->
|
|
||||||
Card(
|
|
||||||
colors = CardDefaults.cardColors(
|
|
||||||
containerColor = MaterialTheme.colorScheme.errorContainer
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
Column(
|
|
||||||
modifier = Modifier.padding(16.dp)
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
text = "Error",
|
|
||||||
style = MaterialTheme.typography.titleMedium,
|
|
||||||
color = MaterialTheme.colorScheme.onErrorContainer,
|
|
||||||
fontWeight = FontWeight.Bold
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
|
||||||
Text(
|
|
||||||
text = error,
|
|
||||||
color = MaterialTheme.colorScheme.onErrorContainer
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
|
||||||
Button(
|
|
||||||
onClick = { viewModel.clearError() }
|
|
||||||
) {
|
|
||||||
Text("Dismiss")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Simple Ping Response
|
|
||||||
uiState.simplePingResponse?.let { response ->
|
|
||||||
ResponseCard(
|
|
||||||
title = "Simple Ping Response",
|
|
||||||
status = response.status,
|
|
||||||
timestamp = response.timestamp,
|
|
||||||
service = response.service
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enhanced Ping Response
|
|
||||||
uiState.enhancedPingResponse?.let { response ->
|
|
||||||
ResponseCard(
|
|
||||||
title = "Enhanced Ping Response",
|
|
||||||
status = response.status,
|
|
||||||
timestamp = response.timestamp,
|
|
||||||
service = response.service,
|
|
||||||
additionalInfo = mapOf(
|
|
||||||
"Circuit Breaker State" to response.circuitBreakerState,
|
|
||||||
"Response Time" to "${response.responseTime}ms"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Health Response
|
|
||||||
uiState.healthResponse?.let { response ->
|
|
||||||
ResponseCard(
|
|
||||||
title = "Health Check Response",
|
|
||||||
status = response.status,
|
|
||||||
timestamp = response.timestamp,
|
|
||||||
service = response.service,
|
|
||||||
additionalInfo = mapOf(
|
|
||||||
"Healthy" to response.healthy.toString()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Neue Reitsport-Authentication-Sektion
|
|
||||||
Spacer(modifier = Modifier.height(24.dp))
|
|
||||||
|
|
||||||
ReitsportTestingSection(
|
|
||||||
viewModel = viewModel,
|
|
||||||
uiState = uiState
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun ResponseCard(
|
|
||||||
title: String,
|
|
||||||
status: String,
|
|
||||||
timestamp: String,
|
|
||||||
service: String,
|
|
||||||
additionalInfo: Map<String, String> = emptyMap()
|
|
||||||
) {
|
|
||||||
Card(
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
colors = CardDefaults.cardColors(
|
|
||||||
containerColor = MaterialTheme.colorScheme.surfaceVariant
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
Column(
|
|
||||||
modifier = Modifier.padding(16.dp),
|
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
text = title,
|
|
||||||
style = MaterialTheme.typography.titleMedium,
|
|
||||||
fontWeight = FontWeight.Bold
|
|
||||||
)
|
|
||||||
|
|
||||||
InfoRow("Status", status)
|
|
||||||
InfoRow("Timestamp", timestamp)
|
|
||||||
InfoRow("Service", service)
|
|
||||||
|
|
||||||
additionalInfo.forEach { (key, value) ->
|
|
||||||
InfoRow(key, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun InfoRow(label: String, value: String) {
|
|
||||||
Row(
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
horizontalArrangement = Arrangement.SpaceBetween
|
|
||||||
) {
|
|
||||||
Text(
|
Text(
|
||||||
text = "$label:",
|
text = "This screen triggers the generic SyncManager against /api/pings/sync and stores events locally.",
|
||||||
fontWeight = FontWeight.Medium
|
style = MaterialTheme.typography.bodyMedium
|
||||||
)
|
)
|
||||||
Text(text = value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun ReitsportTestingSection(
|
|
||||||
viewModel: PingViewModel,
|
|
||||||
uiState: PingUiState
|
|
||||||
) {
|
|
||||||
Card(
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
colors = CardDefaults.cardColors(
|
|
||||||
containerColor = MaterialTheme.colorScheme.secondaryContainer
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
Column(
|
|
||||||
modifier = Modifier.padding(16.dp),
|
|
||||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
|
||||||
) {
|
|
||||||
// Header
|
|
||||||
Row(
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
verticalAlignment = Alignment.CenterVertically
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
text = "🐎",
|
|
||||||
style = MaterialTheme.typography.headlineMedium
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.width(8.dp))
|
|
||||||
Text(
|
|
||||||
text = "Reitsport-Authentication-Testing",
|
|
||||||
style = MaterialTheme.typography.headlineSmall,
|
|
||||||
fontWeight = FontWeight.Bold
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Text(
|
|
||||||
text = "Teste verschiedene Benutzerrollen und ihre Berechtigungen im Meldestelle_Pro System",
|
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
|
||||||
color = MaterialTheme.colorScheme.onSecondaryContainer.copy(alpha = 0.8f)
|
|
||||||
)
|
|
||||||
|
|
||||||
// Rollen-Grid
|
|
||||||
LazyVerticalGrid(
|
|
||||||
columns = GridCells.Adaptive(minSize = 120.dp),
|
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
|
||||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
|
||||||
modifier = Modifier.height(200.dp) // Feste Höhe für 2 Reihen
|
|
||||||
) {
|
|
||||||
items(ReitsportRoles.ALL_ROLES) { role ->
|
|
||||||
RoleTestButton(
|
|
||||||
role = role,
|
|
||||||
onClick = { viewModel.testReitsportRole(role) },
|
|
||||||
isLoading = uiState.isLoading
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun RoleTestButton(
|
|
||||||
role: ReitsportRole,
|
|
||||||
onClick: () -> Unit,
|
|
||||||
isLoading: Boolean
|
|
||||||
) {
|
|
||||||
OutlinedButton(
|
|
||||||
onClick = onClick,
|
|
||||||
enabled = !isLoading,
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.height(80.dp),
|
|
||||||
colors = ButtonDefaults.outlinedButtonColors(
|
|
||||||
containerColor = Color.Transparent,
|
|
||||||
contentColor = when (role.category) {
|
|
||||||
RoleCategory.SYSTEM -> Color(0xFFFF5722)
|
|
||||||
RoleCategory.OFFICIAL -> Color(0xFF3F51B5)
|
|
||||||
RoleCategory.ACTIVE -> Color(0xFF4CAF50)
|
|
||||||
RoleCategory.PASSIVE -> Color(0xFF9E9E9E)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
Column(
|
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
|
||||||
verticalArrangement = Arrangement.Center
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
text = role.icon,
|
|
||||||
fontSize = 20.sp
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
text = role.displayName.split(" ").first(), // Erstes Wort nur
|
|
||||||
fontSize = 10.sp,
|
|
||||||
fontWeight = FontWeight.Medium,
|
|
||||||
textAlign = TextAlign.Center,
|
|
||||||
maxLines = 1
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
text = "${role.permissions.size} Rechte",
|
|
||||||
fontSize = 8.sp,
|
|
||||||
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f),
|
|
||||||
textAlign = TextAlign.Center
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
package at.mocode.ping.feature.data
|
||||||
|
|
||||||
|
import at.mocode.frontend.core.localdb.AppDatabase
|
||||||
|
import at.mocode.frontend.core.sync.SyncableRepository
|
||||||
|
import at.mocode.ping.api.PingEvent
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
// ARCH-BLUEPRINT: This repository implements the generic SyncableRepository
|
||||||
|
// for a specific entity, bridging the gap between the sync core and the local database.
|
||||||
|
class PingEventRepositoryImpl(
|
||||||
|
private val db: AppDatabase
|
||||||
|
) : SyncableRepository<PingEvent> {
|
||||||
|
|
||||||
|
// The `since` parameter for our sync is the ID of the last event, not a timestamp.
|
||||||
|
override suspend fun getLatestSince(): String? = withContext(Dispatchers.Default) {
|
||||||
|
db.appDatabaseQueries.selectLatestPingEventId().executeAsOneOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun upsert(items: List<PingEvent>) = withContext(Dispatchers.Default) {
|
||||||
|
// Always perform bulk operations within a transaction.
|
||||||
|
db.transaction {
|
||||||
|
items.forEach { event ->
|
||||||
|
db.appDatabaseQueries.upsertPingEvent(
|
||||||
|
id = event.id,
|
||||||
|
message = event.message,
|
||||||
|
last_modified = event.lastModified
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
package at.mocode.ping.feature.di
|
||||||
|
|
||||||
|
import at.mocode.ping.feature.data.PingEventRepositoryImpl
|
||||||
|
import at.mocode.ping.feature.presentation.PingViewModel
|
||||||
|
import at.mocode.frontend.core.localdb.AppDatabase
|
||||||
|
import org.koin.dsl.module
|
||||||
|
|
||||||
|
val pingFeatureModule = module {
|
||||||
|
// Provides the ViewModel for the Ping feature.
|
||||||
|
factory<PingViewModel> {
|
||||||
|
PingViewModel(
|
||||||
|
syncManager = get(),
|
||||||
|
pingEventRepository = get()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Provides the concrete repository implementation for PingEvents.
|
||||||
|
single<PingEventRepositoryImpl> { PingEventRepositoryImpl(get<AppDatabase>()) }
|
||||||
|
}
|
||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
package at.mocode.ping.feature.presentation
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import at.mocode.frontend.core.sync.SyncManager
|
||||||
|
import at.mocode.ping.api.PingEvent
|
||||||
|
import at.mocode.ping.feature.data.PingEventRepositoryImpl
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
class PingViewModel(
|
||||||
|
private val syncManager: SyncManager,
|
||||||
|
private val pingEventRepository: PingEventRepositoryImpl
|
||||||
|
) : ViewModel() {
|
||||||
|
|
||||||
|
init {
|
||||||
|
// Trigger an initial sync when the ViewModel is created.
|
||||||
|
triggerSync()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun triggerSync() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
try {
|
||||||
|
syncManager.performSync<PingEvent>(pingEventRepository, "/api/pings/sync")
|
||||||
|
} catch (_: Exception) {
|
||||||
|
// TODO: Handle sync errors and expose them to the UI
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
@file:OptIn(ExperimentalKotlinGradlePluginApi::class)
|
@file:OptIn(ExperimentalKotlinGradlePluginApi::class)
|
||||||
|
@file:Suppress("DEPRECATION")
|
||||||
|
|
||||||
import org.jetbrains.compose.desktop.application.dsl.TargetFormat
|
import org.jetbrains.compose.desktop.application.dsl.TargetFormat
|
||||||
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
|
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
|
||||||
@@ -78,6 +79,7 @@ kotlin {
|
|||||||
implementation(projects.frontend.core.designSystem)
|
implementation(projects.frontend.core.designSystem)
|
||||||
implementation(projects.frontend.core.navigation)
|
implementation(projects.frontend.core.navigation)
|
||||||
implementation(projects.frontend.core.network)
|
implementation(projects.frontend.core.network)
|
||||||
|
implementation(projects.frontend.core.sync)
|
||||||
implementation(project(":frontend:core:local-db"))
|
implementation(project(":frontend:core:local-db"))
|
||||||
implementation(projects.frontend.features.authFeature)
|
implementation(projects.frontend.features.authFeature)
|
||||||
implementation(projects.frontend.features.pingFeature)
|
implementation(projects.frontend.features.pingFeature)
|
||||||
@@ -143,10 +145,12 @@ val copySqliteWorkerJs by tasks.registering(Copy::class) {
|
|||||||
from(localDb.layout.buildDirectory.file("processedResources/js/main/sqlite.worker.js"))
|
from(localDb.layout.buildDirectory.file("processedResources/js/main/sqlite.worker.js"))
|
||||||
|
|
||||||
// Root build directory where Kotlin JS packages are assembled.
|
// Root build directory where Kotlin JS packages are assembled.
|
||||||
into(rootProject.layout.buildDirectory.dir("js/packages/${rootProject.name}-frontend-shells-meldestelle-portal/kotlin"))
|
// Use a concrete path (instead of a Provider) so the Copy task always materializes the directory.
|
||||||
|
into(rootProject.layout.buildDirectory.asFile.get().resolve("js/packages/${rootProject.name}-frontend-shells-meldestelle-portal/kotlin"))
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.matching { it.name == "jsBrowserProductionWebpack" }.configureEach {
|
// Ensure the worker is present for the production bundle.
|
||||||
|
tasks.named("jsBrowserProductionWebpack") {
|
||||||
dependsOn(copySqliteWorkerJs)
|
dependsOn(copySqliteWorkerJs)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,6 +167,14 @@ tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Kotlin/JS source maps
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Production source maps must remain enabled for browser debugging.
|
||||||
|
// The remaining Kotlin/Gradle message
|
||||||
|
// `Cannot rewrite paths in JavaScript source maps: Too many sources or format is not supported`
|
||||||
|
// is treated as an external Kotlin/JS toolchain limitation and is documented separately.
|
||||||
|
|
||||||
// Configure a duplicate handling strategy for distribution tasks
|
// Configure a duplicate handling strategy for distribution tasks
|
||||||
tasks.withType<Tar> {
|
tasks.withType<Tar> {
|
||||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import androidx.compose.runtime.collectAsState
|
|||||||
import at.mocode.clients.shared.navigation.AppScreen
|
import at.mocode.clients.shared.navigation.AppScreen
|
||||||
import at.mocode.clients.authfeature.AuthTokenManager
|
import at.mocode.clients.authfeature.AuthTokenManager
|
||||||
import at.mocode.clients.pingfeature.PingScreen
|
import at.mocode.clients.pingfeature.PingScreen
|
||||||
import at.mocode.clients.pingfeature.PingViewModel
|
import at.mocode.ping.feature.presentation.PingViewModel
|
||||||
import at.mocode.shared.core.AppConstants
|
import at.mocode.shared.core.AppConstants
|
||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
||||||
@@ -34,7 +34,8 @@ fun MainApp() {
|
|||||||
// Resolve AuthTokenManager from Koin
|
// Resolve AuthTokenManager from Koin
|
||||||
val authTokenManager = koinInject<AuthTokenManager>()
|
val authTokenManager = koinInject<AuthTokenManager>()
|
||||||
val authApiClient = koinInject<AuthApiClient>()
|
val authApiClient = koinInject<AuthApiClient>()
|
||||||
val pingViewModel = remember { PingViewModel() }
|
// Delta-Sync blueprint: resolve the Ping feature view model via Koin.
|
||||||
|
val pingViewModel: PingViewModel = koinViewModel()
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
// Handle PKCE callback on an app load (web)
|
// Handle PKCE callback on an app load (web)
|
||||||
|
|||||||
@@ -7,11 +7,20 @@ import at.mocode.frontend.core.network.networkModule
|
|||||||
import at.mocode.clients.authfeature.di.authFeatureModule
|
import at.mocode.clients.authfeature.di.authFeatureModule
|
||||||
import at.mocode.frontend.core.localdb.localDbModule
|
import at.mocode.frontend.core.localdb.localDbModule
|
||||||
import at.mocode.frontend.core.localdb.DatabaseProvider
|
import at.mocode.frontend.core.localdb.DatabaseProvider
|
||||||
|
import at.mocode.frontend.core.localdb.AppDatabase
|
||||||
|
import at.mocode.frontend.core.sync.di.syncModule
|
||||||
|
import at.mocode.ping.feature.di.pingFeatureModule
|
||||||
import navigation.navigationModule
|
import navigation.navigationModule
|
||||||
import kotlinx.coroutines.MainScope
|
import kotlinx.coroutines.MainScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.koin.core.context.GlobalContext
|
import org.koin.core.context.GlobalContext
|
||||||
|
import org.koin.core.context.GlobalContext.get
|
||||||
import org.koin.core.qualifier.named
|
import org.koin.core.qualifier.named
|
||||||
|
import org.koin.core.component.KoinComponent
|
||||||
|
import org.koin.core.component.inject
|
||||||
|
import org.koin.core.Koin
|
||||||
|
import org.koin.core.context.loadKoinModules
|
||||||
|
import org.koin.dsl.module
|
||||||
import io.ktor.client.HttpClient
|
import io.ktor.client.HttpClient
|
||||||
import io.ktor.client.call.body
|
import io.ktor.client.call.body
|
||||||
import io.ktor.client.request.get
|
import io.ktor.client.request.get
|
||||||
@@ -21,7 +30,7 @@ fun main() {
|
|||||||
console.log("[WebApp] main() entered")
|
console.log("[WebApp] main() entered")
|
||||||
// Initialize DI (Koin) with shared modules + network + local DB modules
|
// Initialize DI (Koin) with shared modules + network + local DB modules
|
||||||
try {
|
try {
|
||||||
initKoin { modules(networkModule, localDbModule, authFeatureModule, navigationModule) }
|
initKoin { modules(networkModule, localDbModule, syncModule, pingFeatureModule, authFeatureModule, navigationModule) }
|
||||||
console.log("[WebApp] Koin initialized with networkModule + localDbModule + authFeatureModule + navigationModule")
|
console.log("[WebApp] Koin initialized with networkModule + localDbModule + authFeatureModule + navigationModule")
|
||||||
} catch (e: dynamic) {
|
} catch (e: dynamic) {
|
||||||
console.warn("[WebApp] Koin initialization warning:", e)
|
console.warn("[WebApp] Koin initialization warning:", e)
|
||||||
@@ -47,6 +56,15 @@ fun main() {
|
|||||||
MainScope().launch {
|
MainScope().launch {
|
||||||
try {
|
try {
|
||||||
val db = provider.createDatabase()
|
val db = provider.createDatabase()
|
||||||
|
// Register the created DB instance into Koin so feature repositories can use it.
|
||||||
|
// This is the central place where we bridge the async DB creation into the DI graph.
|
||||||
|
// Inject the created DB instance into Koin.
|
||||||
|
// We register a one-off module that provides this concrete instance.
|
||||||
|
loadKoinModules(
|
||||||
|
module {
|
||||||
|
single<AppDatabase> { db }
|
||||||
|
}
|
||||||
|
)
|
||||||
console.log("[WebApp] Local DB created:", jsTypeOf(db))
|
console.log("[WebApp] Local DB created:", jsTypeOf(db))
|
||||||
} catch (e: dynamic) {
|
} catch (e: dynamic) {
|
||||||
console.warn("[WebApp] Local DB smoke failed:", e?.message ?: e)
|
console.warn("[WebApp] Local DB smoke failed:", e?.message ?: e)
|
||||||
|
|||||||
@@ -5,16 +5,37 @@ import androidx.compose.ui.unit.dp
|
|||||||
import at.mocode.shared.di.initKoin
|
import at.mocode.shared.di.initKoin
|
||||||
import at.mocode.frontend.core.network.networkModule
|
import at.mocode.frontend.core.network.networkModule
|
||||||
import at.mocode.clients.authfeature.di.authFeatureModule
|
import at.mocode.clients.authfeature.di.authFeatureModule
|
||||||
|
import at.mocode.frontend.core.sync.di.syncModule
|
||||||
|
import at.mocode.ping.feature.di.pingFeatureModule
|
||||||
|
import at.mocode.frontend.core.localdb.AppDatabase
|
||||||
|
import at.mocode.frontend.core.localdb.DatabaseProvider
|
||||||
import navigation.navigationModule
|
import navigation.navigationModule
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import org.koin.core.context.loadKoinModules
|
||||||
|
import org.koin.dsl.module
|
||||||
|
|
||||||
fun main() = application {
|
fun main() = application {
|
||||||
// Initialize DI (Koin) with shared modules + network module
|
// Initialize DI (Koin) with shared modules + network module
|
||||||
try {
|
try {
|
||||||
initKoin { modules(networkModule, authFeatureModule, navigationModule) }
|
initKoin { modules(networkModule, syncModule, pingFeatureModule, authFeatureModule, navigationModule) }
|
||||||
println("[DesktopApp] Koin initialized with networkModule + authFeatureModule + navigationModule")
|
println("[DesktopApp] Koin initialized with networkModule + authFeatureModule + navigationModule")
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
println("[DesktopApp] Koin initialization warning: ${e.message}")
|
println("[DesktopApp] Koin initialization warning: ${e.message}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create the local DB once and register it into Koin so feature repositories can resolve it.
|
||||||
|
try {
|
||||||
|
val provider = org.koin.core.context.GlobalContext.get().get<DatabaseProvider>()
|
||||||
|
val db = runBlocking { provider.createDatabase() }
|
||||||
|
loadKoinModules(
|
||||||
|
module {
|
||||||
|
single<AppDatabase> { db }
|
||||||
|
}
|
||||||
|
)
|
||||||
|
println("[DesktopApp] Local DB created and registered in Koin")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
println("[DesktopApp] Local DB init warning: ${e.message}")
|
||||||
|
}
|
||||||
Window(
|
Window(
|
||||||
onCloseRequest = ::exitApplication,
|
onCloseRequest = ::exitApplication,
|
||||||
title = "Meldestelle - Desktop Development",
|
title = "Meldestelle - Desktop Development",
|
||||||
|
|||||||
@@ -6,18 +6,47 @@ import org.w3c.dom.HTMLElement
|
|||||||
import at.mocode.shared.di.initKoin
|
import at.mocode.shared.di.initKoin
|
||||||
import at.mocode.frontend.core.network.networkModule
|
import at.mocode.frontend.core.network.networkModule
|
||||||
import at.mocode.clients.authfeature.di.authFeatureModule
|
import at.mocode.clients.authfeature.di.authFeatureModule
|
||||||
|
import at.mocode.frontend.core.sync.di.syncModule
|
||||||
|
import at.mocode.ping.feature.di.pingFeatureModule
|
||||||
|
import at.mocode.frontend.core.localdb.AppDatabase
|
||||||
|
import at.mocode.frontend.core.localdb.DatabaseProvider
|
||||||
import navigation.navigationModule
|
import navigation.navigationModule
|
||||||
|
import kotlinx.coroutines.MainScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.koin.core.context.GlobalContext
|
||||||
|
import org.koin.core.context.loadKoinModules
|
||||||
|
import org.koin.dsl.module
|
||||||
|
|
||||||
@OptIn(ExperimentalComposeUiApi::class)
|
@OptIn(ExperimentalComposeUiApi::class)
|
||||||
fun main() {
|
fun main() {
|
||||||
// Initialize DI
|
// Initialize DI
|
||||||
try {
|
try {
|
||||||
initKoin { modules(networkModule, authFeatureModule, navigationModule) }
|
initKoin { modules(networkModule, syncModule, pingFeatureModule, authFeatureModule, navigationModule) }
|
||||||
println("[WasmApp] Koin initialized (with navigationModule)")
|
println("[WasmApp] Koin initialized (with navigationModule)")
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
println("[WasmApp] Koin init failed: ${e.message}")
|
println("[WasmApp] Koin init failed: ${e.message}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create the local DB asynchronously and register it into Koin.
|
||||||
|
try {
|
||||||
|
val provider = GlobalContext.get().get<DatabaseProvider>()
|
||||||
|
MainScope().launch {
|
||||||
|
try {
|
||||||
|
val db = provider.createDatabase()
|
||||||
|
loadKoinModules(
|
||||||
|
module {
|
||||||
|
single<AppDatabase> { db }
|
||||||
|
}
|
||||||
|
)
|
||||||
|
println("[WasmApp] Local DB created and registered in Koin")
|
||||||
|
} catch (e: dynamic) {
|
||||||
|
println("[WasmApp] Local DB init warning: ${e?.message ?: e}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
println("[WasmApp] Local DB init warning: ${e.message}")
|
||||||
|
}
|
||||||
|
|
||||||
val root = document.getElementById("ComposeTarget") as HTMLElement
|
val root = document.getElementById("ComposeTarget") as HTMLElement
|
||||||
ComposeViewport(root) {
|
ComposeViewport(root) {
|
||||||
MainApp()
|
MainApp()
|
||||||
|
|||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
// Suppress a known, external webpack warning coming from `@sqlite.org/sqlite-wasm`.
|
||||||
|
//
|
||||||
|
// Webpack warning:
|
||||||
|
// "Critical dependency: the request of a dependency is an expression"
|
||||||
|
//
|
||||||
|
// Root cause:
|
||||||
|
// `@sqlite.org/sqlite-wasm/sqlite-wasm/jswasm/sqlite3.mjs` uses a dynamic Worker URL:
|
||||||
|
// `new Worker(new URL(options.proxyUri, import.meta.url))`
|
||||||
|
// which webpack cannot statically analyze.
|
||||||
|
//
|
||||||
|
// We keep this suppression максимально spezifisch:
|
||||||
|
// - match only this warning message
|
||||||
|
// - and only if it originates from the sqlite-wasm package path.
|
||||||
|
|
||||||
|
(function (config) {
|
||||||
|
config.ignoreWarnings = config.ignoreWarnings || []
|
||||||
|
|
||||||
|
// Webpack passes warning objects with `message` and `module.resource`.
|
||||||
|
config.ignoreWarnings.push((warning) => {
|
||||||
|
const message = String(warning && warning.message ? warning.message : warning)
|
||||||
|
if (!message.includes('Critical dependency: the request of a dependency is an expression')) return false
|
||||||
|
|
||||||
|
const resource = warning && warning.module && warning.module.resource
|
||||||
|
? String(warning.module.resource)
|
||||||
|
: ''
|
||||||
|
|
||||||
|
return resource.includes('node_modules/@sqlite.org/sqlite-wasm/')
|
||||||
|
})
|
||||||
|
})(config)
|
||||||
@@ -10,6 +10,7 @@ kotlin.daemon.jvmargs=-Xmx3072M -XX:+UseParallelGC -XX:MaxMetaspaceSize=1024M
|
|||||||
kotlin.incremental=true
|
kotlin.incremental=true
|
||||||
kotlin.incremental.multiplatform=true
|
kotlin.incremental.multiplatform=true
|
||||||
kotlin.incremental.js=true
|
kotlin.incremental.js=true
|
||||||
|
|
||||||
kotlin.caching.enabled=true
|
kotlin.caching.enabled=true
|
||||||
kotlin.compiler.execution.strategy=in-process
|
kotlin.compiler.execution.strategy=in-process
|
||||||
# kotlin.compiler.preciseCompilationResultsBackup=true
|
# kotlin.compiler.preciseCompilationResultsBackup=true
|
||||||
|
|||||||
+20
-996
File diff suppressed because it is too large
Load Diff
@@ -131,6 +131,7 @@ include(":frontend:core:design-system")
|
|||||||
include(":frontend:core:navigation")
|
include(":frontend:core:navigation")
|
||||||
include(":frontend:core:network")
|
include(":frontend:core:network")
|
||||||
include(":frontend:core:local-db")
|
include(":frontend:core:local-db")
|
||||||
|
include(":frontend:core:sync")
|
||||||
|
|
||||||
// --- FEATURES ---
|
// --- FEATURES ---
|
||||||
include(":frontend:features:auth-feature")
|
include(":frontend:features:auth-feature")
|
||||||
|
|||||||
Reference in New Issue
Block a user