From 8643c4c21ddcb1360f90d8947ceaf85d2337a867 Mon Sep 17 00:00:00 2001 From: John O'Reilly Date: Fri, 12 Apr 2024 13:49:54 +0100 Subject: [PATCH] wasm support wip --- androidApp/build.gradle.kts | 1 + .../confetti/speakers/SpeakerView.kt | 2 +- build.gradle.kts | 2 +- buildSrc/settings.gradle.kts | 1 + compose-desktop/src/main/kotlin/HomeUI.kt | 21 + compose-desktop/src/main/kotlin/SpeakersUI.kt | 2 +- compose-web/build.gradle.kts | 52 ++ compose-web/src/wasmJsMain/kotlin/HomeUI.kt | 222 +++++ compose-web/src/wasmJsMain/kotlin/Main.kt | 60 ++ .../src/wasmJsMain/kotlin/SessionsUI.kt | 54 ++ .../src/wasmJsMain/kotlin/SpeakersUI.kt | 62 ++ compose-web/src/wasmJsMain/kotlin/VenueUI.kt | 30 + .../wasmJsMain/resources/images/confetti.png | Bin 0 -> 53757 bytes .../wasmJsMain/resources/images/splash.png | Bin 0 -> 52735 bytes .../src/wasmJsMain/resources/index.html | 61 ++ .../src/wasmJsMain/resources/manifest.json | 13 + gradle.properties | 5 + gradle/libs.versions.toml | 23 +- .../xcshareddata/swiftpm/Package.resolved | 149 ---- .../xcschemes/xcschememanagement.plist | 2 +- kotlin-js-store/yarn.lock | 765 ++++++++++++------ proto/.gitignore | 1 + proto/build.gradle.kts | 46 ++ proto/proguard-rules.pro | 21 + proto/src/androidMain/AndroidManifest.xml | 4 + .../settings/WearSettingsSerializer.kt | 0 .../src/commonMain/proto/wear.proto | 0 settings.gradle.kts | 5 + shared/build.gradle.kts | 43 +- .../johnoreilly/confetti/di/KoinAndroid.kt | 11 +- .../johnoreilly/confetti/ApolloClientCache.kt | 31 +- .../confetti/ConfettiRepository.kt | 1 - .../decompose/SessionDetailsComponent.kt | 4 +- .../confetti/decompose/SessionsComponent.kt | 2 + .../decompose/SpeakerDetailsComponent.kt | 4 +- .../dev/johnoreilly/confetti/di/Koin.kt | 5 +- .../confetti/ui/ConferenceListView.kt | 2 +- .../confetti/ui/SessionDetailsViewShared.kt | 7 +- .../confetti/ui/SessionListGridView.kt | 226 +++--- .../confetti/ui/SessionListView.kt | 234 ++++++ .../confetti/ui/SpeakersDetailsView.kt | 5 +- .../confetti/ui/SpeakersGridView.kt | 8 +- .../dev/johnoreilly/confetti/utils/UIUtils.kt | 7 + .../dev/johnoreilly/confetti/di/KoiniOS.kt | 14 +- .../dev/johnoreilly/confetti/di/KoinJVM.kt | 12 +- .../confetti/decompose/SettingsComponent.kt | 3 + .../dev/johnoreilly/confetti/di/KoinWasmJs.kt | 33 + .../ui/SessionDetailsViewSharedWrapper.kt | 11 + .../confetti/utils/ApolloDebugServer.kt | 12 + .../confetti/utils/WasmDateService.kt | 10 + wearApp/build.gradle.kts | 1 + webApp/src/commonMain/kotlin/App.kt | 1 - 52 files changed, 1697 insertions(+), 594 deletions(-) create mode 100644 compose-web/build.gradle.kts create mode 100644 compose-web/src/wasmJsMain/kotlin/HomeUI.kt create mode 100644 compose-web/src/wasmJsMain/kotlin/Main.kt create mode 100644 compose-web/src/wasmJsMain/kotlin/SessionsUI.kt create mode 100644 compose-web/src/wasmJsMain/kotlin/SpeakersUI.kt create mode 100644 compose-web/src/wasmJsMain/kotlin/VenueUI.kt create mode 100644 compose-web/src/wasmJsMain/resources/images/confetti.png create mode 100644 compose-web/src/wasmJsMain/resources/images/splash.png create mode 100644 compose-web/src/wasmJsMain/resources/index.html create mode 100644 compose-web/src/wasmJsMain/resources/manifest.json delete mode 100644 iosApp/iosApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved create mode 100644 proto/.gitignore create mode 100644 proto/build.gradle.kts create mode 100644 proto/proguard-rules.pro create mode 100644 proto/src/androidMain/AndroidManifest.xml rename {shared => proto}/src/androidMain/kotlin/dev/johnoreilly/confetti/settings/WearSettingsSerializer.kt (100%) rename {shared => proto}/src/commonMain/proto/wear.proto (100%) create mode 100644 shared/src/commonMain/kotlin/dev/johnoreilly/confetti/ui/SessionListView.kt create mode 100644 shared/src/commonMain/kotlin/dev/johnoreilly/confetti/utils/UIUtils.kt create mode 100644 shared/src/wasmJsMain/kotlin/dev/johnoreilly/confetti/decompose/SettingsComponent.kt create mode 100644 shared/src/wasmJsMain/kotlin/dev/johnoreilly/confetti/di/KoinWasmJs.kt create mode 100644 shared/src/wasmJsMain/kotlin/dev/johnoreilly/confetti/ui/SessionDetailsViewSharedWrapper.kt create mode 100644 shared/src/wasmJsMain/kotlin/dev/johnoreilly/confetti/utils/ApolloDebugServer.kt create mode 100644 shared/src/wasmJsMain/kotlin/dev/johnoreilly/confetti/utils/WasmDateService.kt diff --git a/androidApp/build.gradle.kts b/androidApp/build.gradle.kts index 2acc6d0a3..c43861506 100644 --- a/androidApp/build.gradle.kts +++ b/androidApp/build.gradle.kts @@ -9,6 +9,7 @@ plugins { id("com.google.gms.google-services") id("com.google.firebase.crashlytics") id("io.github.takahirom.roborazzi") + id("org.jetbrains.compose") } configureCompilerOptions() diff --git a/androidApp/src/main/java/dev/johnoreilly/confetti/speakers/SpeakerView.kt b/androidApp/src/main/java/dev/johnoreilly/confetti/speakers/SpeakerView.kt index be861dbef..bb807dee3 100644 --- a/androidApp/src/main/java/dev/johnoreilly/confetti/speakers/SpeakerView.kt +++ b/androidApp/src/main/java/dev/johnoreilly/confetti/speakers/SpeakerView.kt @@ -56,7 +56,7 @@ fun SpeakersRoute( when (val state = uiState) { is SpeakersUiState.Success -> { if (windowSizeClass.isExpanded) { - SpeakerGridView(state.speakers, component::onSpeakerClicked) + SpeakerGridView(state.conference, state.speakers, component::onSpeakerClicked) } else { SpeakerListView(state.speakers, component::onSpeakerClicked) } diff --git a/build.gradle.kts b/build.gradle.kts index d7ad24eef..a153eb4be 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -32,7 +32,7 @@ tasks.register("quickChecks") { allprojects { afterEvaluate { extensions.findByType()?.apply { - kotlinCompilerPlugin.set("1.5.9-kt-2.0.0-Beta4") + kotlinCompilerPlugin.set("1.5.11-kt-2.0.0-RC1") } } } diff --git a/buildSrc/settings.gradle.kts b/buildSrc/settings.gradle.kts index 054c7ef62..37f8c63c5 100644 --- a/buildSrc/settings.gradle.kts +++ b/buildSrc/settings.gradle.kts @@ -15,6 +15,7 @@ pluginManagement { includeGroupByRegex(".*android.*") } } + mavenLocal() mavenCentral() gradlePluginPortal() } diff --git a/compose-desktop/src/main/kotlin/HomeUI.kt b/compose-desktop/src/main/kotlin/HomeUI.kt index abff086ac..83fd61b3e 100644 --- a/compose-desktop/src/main/kotlin/HomeUI.kt +++ b/compose-desktop/src/main/kotlin/HomeUI.kt @@ -1,3 +1,4 @@ +import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.padding import androidx.compose.material.BottomNavigation import androidx.compose.material.BottomNavigationItem @@ -7,6 +8,10 @@ import androidx.compose.material.icons.outlined.Home import androidx.compose.material.icons.outlined.LocationOn import androidx.compose.material.icons.outlined.Person import androidx.compose.material.icons.outlined.Search +import androidx.compose.material.icons.outlined.Settings +import androidx.compose.material3.CenterAlignedTopAppBar +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Text @@ -17,6 +22,7 @@ import com.arkivanov.decompose.extensions.compose.stack.Children import com.arkivanov.decompose.extensions.compose.subscribeAsState import dev.johnoreilly.confetti.decompose.ConferenceComponent import dev.johnoreilly.confetti.decompose.HomeComponent +import dev.johnoreilly.confetti.decompose.SessionsUiState import dev.johnoreilly.confetti.ui.ConferenceMaterialTheme import dev.johnoreilly.confetti.ui.SessionListGridView @@ -38,12 +44,27 @@ fun ConferenceView(component: ConferenceComponent) { } +@OptIn(ExperimentalMaterial3Api::class) @Composable fun HomeView(component: HomeComponent) { val stack by component.stack.subscribeAsState() val activeChild = stack.active.instance + val topBarActions: @Composable RowScope.() -> Unit = { + IconButton(onClick = { + component.onSwitchConferenceClicked() + }) { + androidx.compose.material3.Icon(Icons.Outlined.Settings, contentDescription = "Switch Conference") + } + } + Scaffold( + topBar = { + CenterAlignedTopAppBar( + title = { Text("") }, + actions = topBarActions + ) + }, bottomBar = { BottomNavigation(backgroundColor = MaterialTheme.colorScheme.surface) { BottomNavigationItem( diff --git a/compose-desktop/src/main/kotlin/SpeakersUI.kt b/compose-desktop/src/main/kotlin/SpeakersUI.kt index 41e08a757..0e9f547c5 100644 --- a/compose-desktop/src/main/kotlin/SpeakersUI.kt +++ b/compose-desktop/src/main/kotlin/SpeakersUI.kt @@ -26,7 +26,7 @@ fun SpeakersUI(component: SpeakersComponent) { ) { when (val state = uiState) { is SpeakersUiState.Success -> { - SpeakerGridView(state.speakers, component::onSpeakerClicked) + SpeakerGridView(state.conference, state.speakers, component::onSpeakerClicked) } is SpeakersUiState.Loading -> LoadingView() is SpeakersUiState.Error -> ErrorView {} diff --git a/compose-web/build.gradle.kts b/compose-web/build.gradle.kts new file mode 100644 index 000000000..93c64f615 --- /dev/null +++ b/compose-web/build.gradle.kts @@ -0,0 +1,52 @@ +import org.jetbrains.compose.ExperimentalComposeLibrary + +plugins { + kotlin("multiplatform") + id("kotlinx-serialization") + id("org.jetbrains.compose") +} + +group = "com.example" +version = "1.0-SNAPSHOT" + + +val copyWasmResources = tasks.create("copyWasmResourcesWorkaround", Copy::class.java) { + from(project(":shared").file("src/commonMain/composeResources")) + into("build/processedResources/wasmJs/main") +} + + +afterEvaluate { + project.tasks.getByName("wasmJsProcessResources").finalizedBy(copyWasmResources) + project.tasks.getByName("wasmJsDevelopmentExecutableCompileSync").dependsOn(copyWasmResources) + project.tasks.getByName("wasmJsProductionExecutableCompileSync").dependsOn(copyWasmResources) +} + +kotlin { + wasmJs { + moduleName = "confetti" + browser { + commonWebpackConfig { + outputFileName = "confetti.js" + } + } + binaries.executable() + } + + sourceSets { + commonMain { + dependencies { + implementation(compose.ui) + implementation(compose.runtime) + implementation(compose.foundation) + implementation(compose.material3) + implementation(compose.components.resources) + implementation(project(":shared")) + } + } + } +} + +compose.experimental { + web.application {} +} diff --git a/compose-web/src/wasmJsMain/kotlin/HomeUI.kt b/compose-web/src/wasmJsMain/kotlin/HomeUI.kt new file mode 100644 index 000000000..f1ab90901 --- /dev/null +++ b/compose-web/src/wasmJsMain/kotlin/HomeUI.kt @@ -0,0 +1,222 @@ +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.safeDrawingPadding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Home +import androidx.compose.material.icons.filled.Person +import androidx.compose.material.icons.outlined.Home +import androidx.compose.material.icons.outlined.Person +import androidx.compose.material.icons.outlined.Settings +import androidx.compose.material3.CenterAlignedTopAppBar +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.NavigationBar +import androidx.compose.material3.NavigationBarItem +import androidx.compose.material3.NavigationRailItem +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi +import androidx.compose.material3.windowsizeclass.WindowSizeClass +import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass +import androidx.compose.material3.windowsizeclass.calculateWindowSizeClass +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.unit.dp +import com.arkivanov.decompose.extensions.compose.stack.Children +import com.arkivanov.decompose.extensions.compose.subscribeAsState +import dev.johnoreilly.confetti.decompose.ConferenceComponent +import dev.johnoreilly.confetti.decompose.HomeComponent +import dev.johnoreilly.confetti.decompose.SessionsComponent +import dev.johnoreilly.confetti.decompose.SessionsUiState +import dev.johnoreilly.confetti.ui.ConferenceMaterialTheme +import dev.johnoreilly.confetti.ui.SessionListGridView +import dev.johnoreilly.confetti.ui.SessionListView +import dev.johnoreilly.confetti.utils.isExpanded + + +@Composable +fun ConferenceView(component: ConferenceComponent) { + ConferenceMaterialTheme(component.conferenceThemeColor) { + Children( + stack = component.stack, + ) { + when (val child = it.instance) { + is ConferenceComponent.Child.Home -> HomeView(child.component) + is ConferenceComponent.Child.SessionDetails -> SessionDetailsUI(child.component) + is ConferenceComponent.Child.SpeakerDetails -> SpeakerDetailsUI(child.component) + is ConferenceComponent.Child.Settings -> {} //SettingsRoute(child.component) + } + } + } +} + + +@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3WindowSizeClassApi::class) +@Composable +fun HomeView(component: HomeComponent) { + val windowSizeClass = calculateWindowSizeClass() + val shouldShowNavRail = windowSizeClass.isExpanded + Row { + if (shouldShowNavRail) { + NavigationRail(component) + } + + Scaffold( + bottomBar = { if (!shouldShowNavRail) BottomBar(component) } + ) { + Column(modifier = Modifier.padding(it)) { + Children(stack = component.stack,) { + when (val child = it.instance) { + is HomeComponent.Child.Sessions -> SessionsUI(child.component, windowSizeClass, component::onSwitchConferenceClicked) + is HomeComponent.Child.MultiPane -> Text(text = "Multi-pane mode is not yet supported") + is HomeComponent.Child.Speakers -> SpeakersUI(child.component) + is HomeComponent.Child.Bookmarks -> {} + is HomeComponent.Child.Venue -> VenueUI(child.component) + is HomeComponent.Child.Search -> {} + is HomeComponent.Child.Recommendations -> {} + } + } + + } + } + } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +private fun SessionsUI(component: SessionsComponent, windowSizeClass: WindowSizeClass, onSwitchConferenceSelected: () -> Unit) { + + val uiState by component.uiState.subscribeAsState() + + val topBarActions: @Composable RowScope.() -> Unit = { + IconButton(onClick = { onSwitchConferenceSelected() }) { + Icon(Icons.Outlined.Settings, contentDescription = "Switch Conference") + } + } + + val title = (uiState as? SessionsUiState.Success)?.conferenceName ?: "" + Scaffold(topBar = { + CenterAlignedTopAppBar( + title = { Text(title) }, + actions = topBarActions + ) + }) { + Column(Modifier.padding(it)) { + if (windowSizeClass.isExpanded) { + SessionListGridView( + uiState = uiState, + sessionSelected = component::onSessionClicked, + onRefresh = {}, + addBookmark = {}, + removeBookmark = {}, + onNavigateToSignIn = {}, + isLoggedIn = component.isLoggedIn, + ) + } else { + SessionListView( + uiState = uiState, + sessionSelected = component::onSessionClicked, + addBookmark = component::addBookmark, + removeBookmark = component::removeBookmark, + onRefresh = component::refresh, + onNavigateToSignIn = component::onSignInClicked, + isLoggedIn = component.isLoggedIn, + ) + } + + } + } + +} + + + +@Composable +private fun NavigationRail(component: HomeComponent) { + androidx.compose.material3.NavigationRail( + modifier = Modifier.safeDrawingPadding(), + containerColor = Color.Transparent, + contentColor = MaterialTheme.colorScheme.onSurfaceVariant, + ) { + NavigationButtons(component = component) { isSelected, selectedIcon, unselectedIcon, text, onClick -> + NavigationRailItem( + selected = isSelected, + onClick = onClick, + icon = { + Icon( + imageVector = if (isSelected) selectedIcon else unselectedIcon, + contentDescription = text + ) + }, + ) + } + } +} + + + +@Composable +private fun BottomBar(component: HomeComponent) { + Column { + HorizontalDivider() + NavigationBar( + contentColor = MaterialTheme.colorScheme.onSurfaceVariant, + tonalElevation = 0.dp, + ) { + NavigationButtons(component = component) { isSelected, selectedIcon, unselectedIcon, text, onClick -> + NavigationBarItem( + selected = isSelected, + onClick = onClick, + icon = { + Icon( + imageVector = if (isSelected) selectedIcon else unselectedIcon, + contentDescription = text, + ) + }, + label = { Text(text) }, + ) + } + } + } +} + + +@Composable +private fun T.NavigationButtons( + component: HomeComponent, + content: @Composable T.( + isSelected: Boolean, + selectedIcon: ImageVector, + unselectedIcon: ImageVector, + text: String, + onClick: () -> Unit, + ) -> Unit, +) { + val stack by component.stack.subscribeAsState() + val activeChild = stack.active.instance + + content( + activeChild is HomeComponent.Child.Sessions, + Icons.Filled.Home, + Icons.Outlined.Home, + "Schedule", + component::onSessionsTabClicked, + ) + + content( + activeChild is HomeComponent.Child.Speakers, + Icons.Filled.Person, + Icons.Outlined.Person, + "Speakers", + component::onSpeakersTabClicked, + ) +} diff --git a/compose-web/src/wasmJsMain/kotlin/Main.kt b/compose-web/src/wasmJsMain/kotlin/Main.kt new file mode 100644 index 000000000..22709ca25 --- /dev/null +++ b/compose-web/src/wasmJsMain/kotlin/Main.kt @@ -0,0 +1,60 @@ +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.ExperimentalComposeUiApi +import androidx.compose.ui.window.CanvasBasedWindow +import com.apollographql.apollo3.ApolloClient +import com.arkivanov.decompose.DefaultComponentContext +import com.arkivanov.decompose.extensions.compose.stack.Children +import com.arkivanov.essenty.lifecycle.Lifecycle +import com.arkivanov.essenty.lifecycle.LifecycleRegistry +import dev.johnoreilly.confetti.decompose.AppComponent +import dev.johnoreilly.confetti.decompose.DefaultAppComponent +import dev.johnoreilly.confetti.di.initKoin +import dev.johnoreilly.confetti.ui.ConferenceListView +import dev.johnoreilly.confetti.ui.LoadingView +import org.koin.dsl.module + + +private fun mainModule() = module { + factory { + ApolloClient.Builder() + .serverUrl("https://confetti-app.dev/graphql") + } +} + +val koin = initKoin { + modules(mainModule()) +}.koin + +@OptIn(ExperimentalComposeUiApi::class) +fun main() { + val lifecycle = LifecycleRegistry(Lifecycle.State.STARTED) + + val appComponent = + DefaultAppComponent( + componentContext = DefaultComponentContext(lifecycle), + onSignOut = {}, + onSignIn = {} + ) + + + CanvasBasedWindow("Confetti", canvasElementId = "confettiCanvas") { + MaterialTheme { + MainLayout(appComponent) + } + } +} + + +@Composable +fun MainLayout(component: DefaultAppComponent) { + Children( + stack = component.stack + ) { + when (val child = it.instance) { + is AppComponent.Child.Loading -> LoadingView() + is AppComponent.Child.Conferences -> ConferenceListView(child.component) + is AppComponent.Child.Conference -> ConferenceView(child.component) + } + } +} diff --git a/compose-web/src/wasmJsMain/kotlin/SessionsUI.kt b/compose-web/src/wasmJsMain/kotlin/SessionsUI.kt new file mode 100644 index 000000000..cb79bff0d --- /dev/null +++ b/compose-web/src/wasmJsMain/kotlin/SessionsUI.kt @@ -0,0 +1,54 @@ +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material3.CenterAlignedTopAppBar +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Scaffold +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalUriHandler +import com.arkivanov.decompose.extensions.compose.subscribeAsState +import dev.johnoreilly.confetti.decompose.SessionDetailsComponent +import dev.johnoreilly.confetti.decompose.SessionDetailsUiState +import dev.johnoreilly.confetti.dev.johnoreilly.confetti.ui.SessionDetailViewSharedWrapper +import dev.johnoreilly.confetti.ui.ErrorView +import dev.johnoreilly.confetti.ui.LoadingView + + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun SessionDetailsUI(component: SessionDetailsComponent) { + val uriHandler = LocalUriHandler.current + + val uiState by component.uiState.subscribeAsState() + + Scaffold(topBar = { + CenterAlignedTopAppBar( + title = { }, + navigationIcon = { + IconButton(onClick = component::onCloseClicked ) { + Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back") + } + } + ) + }) { + Column(Modifier.padding(it)) { + when (val state = uiState) { + is SessionDetailsUiState.Loading -> LoadingView() + is SessionDetailsUiState.Error -> ErrorView() + + is SessionDetailsUiState.Success -> + SessionDetailViewSharedWrapper( + state.conference, state.sessionDetails, + component::onSpeakerClicked + ) { url -> + uriHandler.openUri(url) + } + } + } + } +} diff --git a/compose-web/src/wasmJsMain/kotlin/SpeakersUI.kt b/compose-web/src/wasmJsMain/kotlin/SpeakersUI.kt new file mode 100644 index 000000000..636a7a9e8 --- /dev/null +++ b/compose-web/src/wasmJsMain/kotlin/SpeakersUI.kt @@ -0,0 +1,62 @@ +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Scaffold +import androidx.compose.material3.CenterAlignedTopAppBar +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import com.arkivanov.decompose.extensions.compose.subscribeAsState +import dev.johnoreilly.confetti.decompose.SessionsUiState +import dev.johnoreilly.confetti.decompose.SpeakerDetailsComponent +import dev.johnoreilly.confetti.decompose.SpeakerDetailsUiState +import dev.johnoreilly.confetti.decompose.SpeakersComponent +import dev.johnoreilly.confetti.decompose.SpeakersUiState +import dev.johnoreilly.confetti.ui.ErrorView +import dev.johnoreilly.confetti.ui.LoadingView +import dev.johnoreilly.confetti.ui.SpeakerDetailsView +import dev.johnoreilly.confetti.ui.SpeakerGridView + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun SpeakersUI(component: SpeakersComponent) { + val uiState by component.uiState.subscribeAsState() + + Scaffold( + topBar = { + CenterAlignedTopAppBar(title = { + // TODO need cleaner way of doing this + val conference = (uiState as? SpeakersUiState.Success)?.conference ?: "" + Text("Speakers ($conference)") + }) + } + ) { + Column(Modifier.padding(it)) { + when (val state = uiState) { + is SpeakersUiState.Success -> { + SpeakerGridView(state.conference, state.speakers, component::onSpeakerClicked) + } + is SpeakersUiState.Loading -> LoadingView() + is SpeakersUiState.Error -> ErrorView {} + } + } + } +} + +@Composable +fun SpeakerDetailsUI(component: SpeakerDetailsComponent) { + val uiState by component.uiState.subscribeAsState() + + when (val state = uiState) { + is SpeakerDetailsUiState.Loading -> LoadingView() + is SpeakerDetailsUiState.Error -> ErrorView() + is SpeakerDetailsUiState.Success -> SpeakerDetailsView( + state.conference, + state.details, + component::onSessionClicked, + component::onCloseClicked, + ) + } + +} diff --git a/compose-web/src/wasmJsMain/kotlin/VenueUI.kt b/compose-web/src/wasmJsMain/kotlin/VenueUI.kt new file mode 100644 index 000000000..604faa217 --- /dev/null +++ b/compose-web/src/wasmJsMain/kotlin/VenueUI.kt @@ -0,0 +1,30 @@ +import androidx.compose.material3.Scaffold +import androidx.compose.material3.CenterAlignedTopAppBar +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import com.arkivanov.decompose.extensions.compose.subscribeAsState +import dev.johnoreilly.confetti.decompose.VenueComponent +import dev.johnoreilly.confetti.ui.ErrorView +import dev.johnoreilly.confetti.ui.LoadingView +import dev.johnoreilly.confetti.ui.VenueView + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun VenueUI(component: VenueComponent) { + val uiState by component.uiState.subscribeAsState() + + Scaffold( + topBar = { + CenterAlignedTopAppBar(title = { Text("Venue")}) + } + ) { + when (val state = uiState) { + is VenueComponent.Success -> VenueView(state.data) + is VenueComponent.Loading -> LoadingView() + is VenueComponent.Error -> ErrorView {} + } + } +} + diff --git a/compose-web/src/wasmJsMain/resources/images/confetti.png b/compose-web/src/wasmJsMain/resources/images/confetti.png new file mode 100644 index 0000000000000000000000000000000000000000..7544feaa54d622093d9e1fc77b7477fbcec6d10a GIT binary patch literal 53757 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

+2VhP&^6 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 literal 0 HcmV?d00001 diff --git a/compose-web/src/wasmJsMain/resources/images/splash.png b/compose-web/src/wasmJsMain/resources/images/splash.png new file mode 100644 index 0000000000000000000000000000000000000000..490858e116196c9693284abe79708147c2025b7d GIT binary patch literal 52735 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

A^xfk1+B=z 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%0A!$ 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 literal 0 HcmV?d00001 diff --git a/compose-web/src/wasmJsMain/resources/index.html b/compose-web/src/wasmJsMain/resources/index.html new file mode 100644 index 000000000..75c465a21 --- /dev/null +++ b/compose-web/src/wasmJsMain/resources/index.html @@ -0,0 +1,61 @@ + + + + Confetti + + + + + + + + + + + + diff --git a/compose-web/src/wasmJsMain/resources/manifest.json b/compose-web/src/wasmJsMain/resources/manifest.json new file mode 100644 index 000000000..3d47a6b9e --- /dev/null +++ b/compose-web/src/wasmJsMain/resources/manifest.json @@ -0,0 +1,13 @@ +{ + "name": "Confetti", + "icons": [ + { + "src": "/images/confetti.png", + "type": "image/png", + "sizes": "512x512" + } + ], + "start_url": "/", + "display": "standalone", + "background_color": "white" +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index d54f0e06a..e06dff4b7 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,9 +17,14 @@ kotlin.mpp.androidSourceSetLayoutVersion=2 org.jetbrains.compose.experimental.uikit.enabled=true org.jetbrains.compose.experimental.macos.enabled=true org.jetbrains.compose.experimental.wasm.enabled=true +org.jetbrains.compose.experimental.jscanvas.enabled=true kotlin.experimental.tryK2=true +kotlin.daemon.jvmargs=-Xmx4096m +xcodeproj=./iosApp + + # KMMBridge LIBRARY_VERSION=0.9 GROUP=dev.johnoreilly.confetti diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 348ae45e0..af6a47260 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,12 +3,13 @@ accompanist = "0.34.0" activity-compose = "1.9.0" androidx-lifecycle = "2.7.0" androidx-datastore = "1.1.0" -apollo = "4.0.0-beta.5" +apollo = "4.0.0-beta.6" compose = "1.6.6" compose-multiplatform = "1.6.2" -compose-compiler = "1.5.11-dev-k2.0.0-Beta4-21f5e479a96" +compose-compiler = "1.5.11-dev-k2.0.0-Beta5-b5a216d0ac6" compose-material-3 = "1.2.1" compose-ui-tooling = "1.3.1" +composeWindowSize = "0.5.0" credentials-play-services-auth = "1.2.2" decompose = "3.0.0-beta01" essenty = "2.0.0-beta02" @@ -22,9 +23,9 @@ kmm-viewmodel = "1.0.0-ALPHA-20" kmmbridge = "0.5.4" koin-android = "3.5.6" koin-android-compose = "3.5.6" -koin-compose-multiplatform = "1.1.5" -koin-core = "3.5.6" -kotlin = "2.0.0-Beta4" +koin-compose-multiplatform = "1.2.0-alpha3" +koin-core = "3.6.0-alpha3" +kotlin = "2.0.0-RC1" ksp-plugin = "2.0.0-RC1-1.0.20" kotlin-coroutines = "1.8.0" kotlinx-coroutines-play-services = "1.8.0" @@ -32,7 +33,7 @@ kotlinx-datetime = "0.6.0-RC.2" lifecycle = "2.7.0" lifecycle-livedata-ktx = "2.7.0" materialkolor = "1.4.4" -multiplatform-settings = "1.1.1" +multiplatform-settings = "1.2.0" nav-compose = "2.7.7" okio = "3.9.0" protolayout-expression = "1.1.0" @@ -43,7 +44,7 @@ roborazzi-gradle-plugin = "1.12.0" room = "2.6.1" tiles-tooling-preview = "1.3.0" work-runtime-ktx = "2.9.0" -generativeai = "0.2.2-1.0.0" +generativeai = "0.2.2-1.0.0-wasm" buildkonfig = "0.15.1" roborazzi = "1.12.0" @@ -154,9 +155,13 @@ ktor-status-pages = "io.ktor:ktor-server-status-pages:2.3.10" lifecycle-runtime-compose = { module = "androidx.lifecycle:lifecycle-runtime-compose", version.ref = "lifecycle" } material3-core = { module = "androidx.compose.material3:material3", version.ref = "compose-material-3" } material3-window-size = { module = "androidx.compose.material3:material3-window-size-class", version.ref = "compose-material-3" } + +compose-window-size = { module = "dev.chrisbanes.material3:material3-window-size-class-multiplatform", version.ref = "composeWindowSize" } + multiplatform-settings = { module = "com.russhwolf:multiplatform-settings", version.ref = "multiplatform-settings" } multiplatform-settings-coroutines = { module = "com.russhwolf:multiplatform-settings-coroutines", version.ref = "multiplatform-settings" } multiplatform-settings-datastore = { module = "com.russhwolf:multiplatform-settings-datastore", version.ref = "multiplatform-settings" } +multiplatform-settings-runtime-observable = { module = "com.russhwolf:multiplatform-settings-runtime-observable", version.ref = "multiplatform-settings" } okhttp = "com.squareup.okhttp3:okhttp:5.0.0-alpha.14" okhttp-coroutines = "com.squareup.okhttp3:okhttp-coroutines:5.0.0-alpha.14" okhttp-logging-interceptor = "com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.14" @@ -202,8 +207,8 @@ roborazzi-compose = { group = "io.github.takahirom.roborazzi", name = "roborazzi roborazzi-rule = { group = "io.github.takahirom.roborazzi", name = "roborazzi-junit-rule", version.ref = "roborazzi" } [bundles] -multiplatform-settings = ["multiplatform-settings", "multiplatform-settings-coroutines"] -apollo = ["apollo-normalized-cache-in-memory", "apollo-normalized-cache-sqlite", "apollo-adapters"] +multiplatform-settings = ["multiplatform-settings", "multiplatform-settings-coroutines", "multiplatform-settings-runtime-observable"] +apollo = ["apollo-normalized-cache-in-memory", "apollo-adapters"] [plugins] buildkonfig = { id = "com.codingfeline.buildkonfig", version.ref = "buildkonfig" } diff --git a/iosApp/iosApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/iosApp/iosApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved deleted file mode 100644 index 30951972e..000000000 --- a/iosApp/iosApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ /dev/null @@ -1,149 +0,0 @@ -{ - "pins" : [ - { - "identity" : "abseil-cpp-swiftpm", - "kind" : "remoteSourceControl", - "location" : "https://github.com/firebase/abseil-cpp-SwiftPM.git", - "state" : { - "revision" : "583de9bd60f66b40e78d08599cc92036c2e7e4e1", - "version" : "0.20220203.2" - } - }, - { - "identity" : "boringssl-swiftpm", - "kind" : "remoteSourceControl", - "location" : "https://github.com/firebase/boringssl-SwiftPM.git", - "state" : { - "revision" : "dd3eda2b05a3f459fc3073695ad1b28659066eab", - "version" : "0.9.1" - } - }, - { - "identity" : "firebase-ios-sdk", - "kind" : "remoteSourceControl", - "location" : "https://github.com/firebase/firebase-ios-sdk.git", - "state" : { - "revision" : "7e80c25b51c2ffa238879b07fbfc5baa54bb3050", - "version" : "9.6.0" - } - }, - { - "identity" : "googleappmeasurement", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/GoogleAppMeasurement.git", - "state" : { - "revision" : "c1cfde8067668027b23a42c29d11c246152fe046", - "version" : "9.6.0" - } - }, - { - "identity" : "googledatatransport", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/GoogleDataTransport.git", - "state" : { - "revision" : "aae45a320fd0d11811820335b1eabc8753902a40", - "version" : "9.2.5" - } - }, - { - "identity" : "googleutilities", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/GoogleUtilities.git", - "state" : { - "revision" : "c38ce365d77b04a9a300c31061c5227589e5597b", - "version" : "7.11.5" - } - }, - { - "identity" : "grpc-ios", - "kind" : "remoteSourceControl", - "location" : "https://github.com/grpc/grpc-ios.git", - "state" : { - "revision" : "8440b914756e0d26d4f4d054a1c1581daedfc5b6", - "version" : "1.44.3-grpc" - } - }, - { - "identity" : "gtm-session-fetcher", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/gtm-session-fetcher.git", - "state" : { - "revision" : "5ccda3981422a84186387dbb763ba739178b529c", - "version" : "2.3.0" - } - }, - { - "identity" : "leveldb", - "kind" : "remoteSourceControl", - "location" : "https://github.com/firebase/leveldb.git", - "state" : { - "revision" : "0706abcc6b0bd9cedfbb015ba840e4a780b5159b", - "version" : "1.22.2" - } - }, - { - "identity" : "markdownui", - "kind" : "remoteSourceControl", - "location" : "https://github.com/gonzalezreal/MarkdownUI", - "state" : { - "revision" : "ae799d015a5374708f7b4c85f3294c05f2a564e2", - "version" : "2.3.0" - } - }, - { - "identity" : "nanopb", - "kind" : "remoteSourceControl", - "location" : "https://github.com/firebase/nanopb.git", - "state" : { - "revision" : "819d0a2173aff699fb8c364b6fb906f7cdb1a692", - "version" : "2.30909.0" - } - }, - { - "identity" : "networkimage", - "kind" : "remoteSourceControl", - "location" : "https://github.com/gonzalezreal/NetworkImage", - "state" : { - "revision" : "7aff8d1b31148d32c5933d75557d42f6323ee3d1", - "version" : "6.0.0" - } - }, - { - "identity" : "promises", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/promises.git", - "state" : { - "revision" : "e70e889c0196c76d22759eb50d6a0270ca9f1d9e", - "version" : "2.3.1" - } - }, - { - "identity" : "swift-async-algorithms", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-async-algorithms", - "state" : { - "revision" : "cca423ff03ab657062f3781847e65a867b51bb01", - "version" : "0.0.3" - } - }, - { - "identity" : "swift-protobuf", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-protobuf.git", - "state" : { - "revision" : "ce20dc083ee485524b802669890291c0d8090170", - "version" : "1.22.1" - } - }, - { - "identity" : "swiftui-flow-layout", - "kind" : "remoteSourceControl", - "location" : "https://github.com/globulus/swiftui-flow-layout", - "state" : { - "branch" : "main", - "revision" : "de7da3440c3b87ba94adfa98c698828d7746a76d" - } - } - ], - "version" : 2 -} diff --git a/iosApp/iosApp.xcodeproj/xcuserdata/joreilly.xcuserdatad/xcschemes/xcschememanagement.plist b/iosApp/iosApp.xcodeproj/xcuserdata/joreilly.xcuserdatad/xcschemes/xcschememanagement.plist index 86c05b5f3..796d72228 100644 --- a/iosApp/iosApp.xcodeproj/xcuserdata/joreilly.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/iosApp/iosApp.xcodeproj/xcuserdata/joreilly.xcuserdatad/xcschemes/xcschememanagement.plist @@ -7,7 +7,7 @@ ConfettiMac.xcscheme_^#shared#^_ orderHint - 1 + 2 Promises (Playground) 1.xcscheme diff --git a/kotlin-js-store/yarn.lock b/kotlin-js-store/yarn.lock index aabcd8155..d1b2fecdd 100644 --- a/kotlin-js-store/yarn.lock +++ b/kotlin-js-store/yarn.lock @@ -12,6 +12,18 @@ resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== +"@isaacs/cliui@^8.0.2": + version "8.0.2" + resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" + integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + dependencies: + string-width "^5.1.2" + string-width-cjs "npm:string-width@^4.2.0" + strip-ansi "^7.0.1" + strip-ansi-cjs "npm:strip-ansi@^6.0.1" + wrap-ansi "^8.1.0" + wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" + "@jridgewell/gen-mapping@^0.3.0": version "0.3.3" resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" @@ -44,7 +56,15 @@ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== -"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": +"@jridgewell/trace-mapping@^0.3.20": + version "0.3.25" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" + integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@jridgewell/trace-mapping@^0.3.9": version "0.3.20" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz#72e45707cf240fa6b081d0366f8265b0cd10197f" integrity sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q== @@ -52,11 +72,21 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" +"@js-joda/core@3.2.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@js-joda/core/-/core-3.2.0.tgz#3e61e21b7b2b8a6be746df1335cf91d70db2a273" + integrity sha512-PMqgJ0sw5B7FKb2d5bWYIoxjri+QlW/Pys7+Rw82jSH0QN3rB05jZ/VrrsUdh1w4+i2kw9JOejXGq/KhDOX7Kg== + "@leichtgewicht/ip-codec@^2.0.1": version "2.0.4" resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz#b2ac626d6cb9c8718ab459166d4bb405b8ffa78b" integrity sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A== +"@pkgjs/parseargs@^0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" + integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== + "@socket.io/component-emitter@~3.1.0": version "3.1.0" resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz#96116f2a912e0c02817345b3c10751069920d553" @@ -70,14 +100,14 @@ "@types/connect" "*" "@types/node" "*" -"@types/bonjour@^3.5.9": +"@types/bonjour@^3.5.13": version "3.5.13" resolved "https://registry.yarnpkg.com/@types/bonjour/-/bonjour-3.5.13.tgz#adf90ce1a105e81dd1f9c61fdc5afda1bfb92956" integrity sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ== dependencies: "@types/node" "*" -"@types/connect-history-api-fallback@^1.3.5": +"@types/connect-history-api-fallback@^1.5.4": version "1.5.4" resolved "https://registry.yarnpkg.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz#7de71645a103056b48ac3ce07b3520b819c1d5b3" integrity sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw== @@ -120,7 +150,7 @@ "@types/estree" "*" "@types/json-schema" "*" -"@types/estree@*", "@types/estree@^1.0.0": +"@types/estree@*", "@types/estree@^1.0.5": version "1.0.5" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== @@ -135,7 +165,7 @@ "@types/range-parser" "*" "@types/send" "*" -"@types/express@*", "@types/express@^4.17.13": +"@types/express@*", "@types/express@^4.17.21": version "4.17.21" resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.21.tgz#c26d4a151e60efe0084b23dc3369ebc631ed192d" integrity sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ== @@ -196,10 +226,10 @@ resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.7.tgz#50ae4353eaaddc04044279812f52c8c65857dbcb" integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== -"@types/retry@0.12.0": - version "0.12.0" - resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d" - integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA== +"@types/retry@0.12.2": + version "0.12.2" + resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.2.tgz#ed279a64fa438bb69f2480eda44937912bb7480a" + integrity sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow== "@types/send@*": version "0.17.4" @@ -209,14 +239,14 @@ "@types/mime" "^1" "@types/node" "*" -"@types/serve-index@^1.9.1": +"@types/serve-index@^1.9.4": version "1.9.4" resolved "https://registry.yarnpkg.com/@types/serve-index/-/serve-index-1.9.4.tgz#e6ae13d5053cb06ed36392110b4f9a49ac4ec898" integrity sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug== dependencies: "@types/express" "*" -"@types/serve-static@*", "@types/serve-static@^1.13.10": +"@types/serve-static@*": version "1.15.5" resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.5.tgz#15e67500ec40789a1e8c9defc2d32a896f05b033" integrity sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ== @@ -225,24 +255,33 @@ "@types/mime" "*" "@types/node" "*" -"@types/sockjs@^0.3.33": +"@types/serve-static@^1.15.5": + version "1.15.7" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.7.tgz#22174bbd74fb97fe303109738e9b5c2f3064f714" + integrity sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw== + dependencies: + "@types/http-errors" "*" + "@types/node" "*" + "@types/send" "*" + +"@types/sockjs@^0.3.36": version "0.3.36" resolved "https://registry.yarnpkg.com/@types/sockjs/-/sockjs-0.3.36.tgz#ce322cf07bcc119d4cbf7f88954f3a3bd0f67535" integrity sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q== dependencies: "@types/node" "*" -"@types/ws@^8.5.1": +"@types/ws@^8.5.10": version "8.5.10" resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.10.tgz#4acfb517970853fa6574a3a6886791d04a396787" integrity sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A== dependencies: "@types/node" "*" -"@webassemblyjs/ast@1.11.6", "@webassemblyjs/ast@^1.11.5": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.6.tgz#db046555d3c413f8966ca50a95176a0e2c642e24" - integrity sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q== +"@webassemblyjs/ast@1.12.1", "@webassemblyjs/ast@^1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.12.1.tgz#bb16a0e8b1914f979f45864c23819cc3e3f0d4bb" + integrity sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg== dependencies: "@webassemblyjs/helper-numbers" "1.11.6" "@webassemblyjs/helper-wasm-bytecode" "1.11.6" @@ -257,10 +296,10 @@ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz#6132f68c4acd59dcd141c44b18cbebbd9f2fa768" integrity sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q== -"@webassemblyjs/helper-buffer@1.11.6": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz#b66d73c43e296fd5e88006f18524feb0f2c7c093" - integrity sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA== +"@webassemblyjs/helper-buffer@1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz#6df20d272ea5439bf20ab3492b7fb70e9bfcb3f6" + integrity sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw== "@webassemblyjs/helper-numbers@1.11.6": version "1.11.6" @@ -276,15 +315,15 @@ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz#bb2ebdb3b83aa26d9baad4c46d4315283acd51e9" integrity sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA== -"@webassemblyjs/helper-wasm-section@1.11.6": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz#ff97f3863c55ee7f580fd5c41a381e9def4aa577" - integrity sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g== +"@webassemblyjs/helper-wasm-section@1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz#3da623233ae1a60409b509a52ade9bc22a37f7bf" + integrity sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g== dependencies: - "@webassemblyjs/ast" "1.11.6" - "@webassemblyjs/helper-buffer" "1.11.6" + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-buffer" "1.12.1" "@webassemblyjs/helper-wasm-bytecode" "1.11.6" - "@webassemblyjs/wasm-gen" "1.11.6" + "@webassemblyjs/wasm-gen" "1.12.1" "@webassemblyjs/ieee754@1.11.6": version "1.11.6" @@ -305,72 +344,72 @@ resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.6.tgz#90f8bc34c561595fe156603be7253cdbcd0fab5a" integrity sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA== -"@webassemblyjs/wasm-edit@^1.11.5": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz#c72fa8220524c9b416249f3d94c2958dfe70ceab" - integrity sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw== +"@webassemblyjs/wasm-edit@^1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz#9f9f3ff52a14c980939be0ef9d5df9ebc678ae3b" + integrity sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g== dependencies: - "@webassemblyjs/ast" "1.11.6" - "@webassemblyjs/helper-buffer" "1.11.6" + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-buffer" "1.12.1" "@webassemblyjs/helper-wasm-bytecode" "1.11.6" - "@webassemblyjs/helper-wasm-section" "1.11.6" - "@webassemblyjs/wasm-gen" "1.11.6" - "@webassemblyjs/wasm-opt" "1.11.6" - "@webassemblyjs/wasm-parser" "1.11.6" - "@webassemblyjs/wast-printer" "1.11.6" - -"@webassemblyjs/wasm-gen@1.11.6": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz#fb5283e0e8b4551cc4e9c3c0d7184a65faf7c268" - integrity sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA== - dependencies: - "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-wasm-section" "1.12.1" + "@webassemblyjs/wasm-gen" "1.12.1" + "@webassemblyjs/wasm-opt" "1.12.1" + "@webassemblyjs/wasm-parser" "1.12.1" + "@webassemblyjs/wast-printer" "1.12.1" + +"@webassemblyjs/wasm-gen@1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz#a6520601da1b5700448273666a71ad0a45d78547" + integrity sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w== + dependencies: + "@webassemblyjs/ast" "1.12.1" "@webassemblyjs/helper-wasm-bytecode" "1.11.6" "@webassemblyjs/ieee754" "1.11.6" "@webassemblyjs/leb128" "1.11.6" "@webassemblyjs/utf8" "1.11.6" -"@webassemblyjs/wasm-opt@1.11.6": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz#d9a22d651248422ca498b09aa3232a81041487c2" - integrity sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g== +"@webassemblyjs/wasm-opt@1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz#9e6e81475dfcfb62dab574ac2dda38226c232bc5" + integrity sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg== dependencies: - "@webassemblyjs/ast" "1.11.6" - "@webassemblyjs/helper-buffer" "1.11.6" - "@webassemblyjs/wasm-gen" "1.11.6" - "@webassemblyjs/wasm-parser" "1.11.6" + "@webassemblyjs/ast" "1.12.1" + "@webassemblyjs/helper-buffer" "1.12.1" + "@webassemblyjs/wasm-gen" "1.12.1" + "@webassemblyjs/wasm-parser" "1.12.1" -"@webassemblyjs/wasm-parser@1.11.6", "@webassemblyjs/wasm-parser@^1.11.5": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz#bb85378c527df824004812bbdb784eea539174a1" - integrity sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ== +"@webassemblyjs/wasm-parser@1.12.1", "@webassemblyjs/wasm-parser@^1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz#c47acb90e6f083391e3fa61d113650eea1e95937" + integrity sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ== dependencies: - "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/ast" "1.12.1" "@webassemblyjs/helper-api-error" "1.11.6" "@webassemblyjs/helper-wasm-bytecode" "1.11.6" "@webassemblyjs/ieee754" "1.11.6" "@webassemblyjs/leb128" "1.11.6" "@webassemblyjs/utf8" "1.11.6" -"@webassemblyjs/wast-printer@1.11.6": - version "1.11.6" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz#a7bf8dd7e362aeb1668ff43f35cb849f188eff20" - integrity sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A== +"@webassemblyjs/wast-printer@1.12.1": + version "1.12.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz#bcecf661d7d1abdaf989d8341a4833e33e2b31ac" + integrity sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA== dependencies: - "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/ast" "1.12.1" "@xtuc/long" "4.2.2" -"@webpack-cli/configtest@^2.1.0": +"@webpack-cli/configtest@^2.1.1": version "2.1.1" resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-2.1.1.tgz#3b2f852e91dac6e3b85fb2a314fb8bef46d94646" integrity sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw== -"@webpack-cli/info@^2.0.1": +"@webpack-cli/info@^2.0.2": version "2.0.2" resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-2.0.2.tgz#cc3fbf22efeb88ff62310cf885c5b09f44ae0fdd" integrity sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A== -"@webpack-cli/serve@^2.0.3": +"@webpack-cli/serve@^2.0.5": version "2.0.5" resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-2.0.5.tgz#325db42395cd49fe6c14057f9a900e427df8810e" integrity sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ== @@ -385,10 +424,12 @@ resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== -abab@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" - integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== +abort-controller@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" + integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== + dependencies: + event-target-shim "^5.0.0" accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8: version "1.3.8" @@ -398,7 +439,7 @@ accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8: mime-types "~2.1.34" negotiator "0.6.3" -acorn-import-assertions@^1.7.6: +acorn-import-assertions@^1.9.0: version "1.9.0" resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz#507276249d684797c84e0734ef84860334cfb1ac" integrity sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA== @@ -462,6 +503,11 @@ ansi-regex@^5.0.1: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== +ansi-regex@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" + integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== + ansi-styles@^4.0.0, ansi-styles@^4.1.0: version "4.3.0" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" @@ -469,6 +515,11 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: dependencies: color-convert "^2.0.1" +ansi-styles@^6.1.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + anymatch@~3.1.2: version "3.1.3" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" @@ -487,11 +538,6 @@ array-flatten@1.1.1: resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== -array-flatten@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099" - integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ== - balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" @@ -507,6 +553,11 @@ batch@0.6.1: resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" integrity sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw== +big.js@5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" + integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== + binary-extensions@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" @@ -548,13 +599,11 @@ body-parser@^1.19.0: type-is "~1.6.18" unpipe "1.0.0" -bonjour-service@^1.0.11: - version "1.1.1" - resolved "https://registry.yarnpkg.com/bonjour-service/-/bonjour-service-1.1.1.tgz#960948fa0e0153f5d26743ab15baf8e33752c135" - integrity sha512-Z/5lQRMOG9k7W+FkeGTNjh7htqn/2LMnfOvBZ8pynNZCM9MwkQkI3zeI4oz09uWdcgmgHugVvBqxGg4VQJ5PCg== +bonjour-service@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/bonjour-service/-/bonjour-service-1.2.1.tgz#eb41b3085183df3321da1264719fbada12478d02" + integrity sha512-oSzCS2zV14bh2kji6vNe7vrpJYCHGvcZnlffFQ1MEoX/WOeQ/teD8SYWKR942OI3INjq8OMNJlbPK5LLLUxFDw== dependencies: - array-flatten "^2.1.2" - dns-equal "^1.0.0" fast-deep-equal "^3.1.3" multicast-dns "^7.2.5" @@ -585,13 +634,13 @@ browser-stdout@1.3.1: resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== -browserslist@^4.14.5: - version "4.22.2" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.22.2.tgz#704c4943072bd81ea18997f3bd2180e89c77874b" - integrity sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A== +browserslist@^4.21.10: + version "4.23.0" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.23.0.tgz#8f3acc2bbe73af7213399430890f86c63a5674ab" + integrity sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ== dependencies: - caniuse-lite "^1.0.30001565" - electron-to-chromium "^1.4.601" + caniuse-lite "^1.0.30001587" + electron-to-chromium "^1.4.668" node-releases "^2.0.14" update-browserslist-db "^1.0.13" @@ -600,6 +649,13 @@ buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== +bundle-name@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bundle-name/-/bundle-name-4.1.0.tgz#f3b96b34160d6431a19d7688135af7cfb8797889" + integrity sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q== + dependencies: + run-applescript "^7.0.0" + bytes@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" @@ -624,10 +680,10 @@ camelcase@^6.0.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001565: - version "1.0.30001570" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001570.tgz#b4e5c1fa786f733ab78fc70f592df6b3f23244ca" - integrity sha512-+3e0ASu4sw1SWaoCtvPeyXp+5PsjigkSt8OXZbF9StH5pQWbxEjLAZE3n8Aup5udop1uRiKA7a4utUk/uoSpUw== +caniuse-lite@^1.0.30001587: + version "1.0.30001609" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001609.tgz#fc34fad75c0c6d6d6303bdbceec2da8f203dabd6" + integrity sha512-JFPQs34lHKx1B5t1EpQpWH4c+29zIyn/haGsbpfq3suuV9v56enjFt23zqijxGTMwy1p/4H2tjnQMY+p1WoAyA== chalk@^4.1.0: version "4.1.2" @@ -637,7 +693,7 @@ chalk@^4.1.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -chokidar@3.5.3, chokidar@^3.5.1, chokidar@^3.5.3: +chokidar@3.5.3, chokidar@^3.5.1: version "3.5.3" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== @@ -652,6 +708,21 @@ chokidar@3.5.3, chokidar@^3.5.1, chokidar@^3.5.3: optionalDependencies: fsevents "~2.3.2" +chokidar@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" + integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + chrome-trace-event@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" @@ -782,7 +853,7 @@ cors@~2.8.5: object-assign "^4" vary "^1" -cross-spawn@^7.0.3: +cross-spawn@^7.0.0, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -820,6 +891,19 @@ decamelize@^4.0.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== +default-browser-id@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/default-browser-id/-/default-browser-id-5.0.0.tgz#a1d98bf960c15082d8a3fa69e83150ccccc3af26" + integrity sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA== + +default-browser@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/default-browser/-/default-browser-5.2.1.tgz#7b7ba61204ff3e425b556869ae6d3e9d9f1712cf" + integrity sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg== + dependencies: + bundle-name "^4.1.0" + default-browser-id "^5.0.0" + default-gateway@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-6.0.3.tgz#819494c888053bdb743edbf343d6cdf7f2943a71" @@ -836,10 +920,10 @@ define-data-property@^1.1.1: gopd "^1.0.1" has-property-descriptors "^1.0.0" -define-lazy-prop@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" - integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== +define-lazy-prop@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz#dbb19adfb746d7fc6d734a06b72f4a00d021255f" + integrity sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg== depd@2.0.0: version "2.0.0" @@ -871,11 +955,6 @@ diff@5.0.0: resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== -dns-equal@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d" - integrity sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg== - dns-packet@^5.2.2: version "5.6.1" resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-5.6.1.tgz#ae888ad425a9d1478a0674256ab866de1012cf2f" @@ -893,21 +972,31 @@ dom-serialize@^2.2.1: extend "^3.0.0" void-elements "^2.0.0" +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== -electron-to-chromium@^1.4.601: - version "1.4.614" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.614.tgz#2fe789d61fa09cb875569f37c309d0c2701f91c0" - integrity sha512-X4ze/9Sc3QWs6h92yerwqv7aB/uU8vCjZcrMjA8N9R1pjMFRe44dLsck5FzLilOYvcXuDn93B+bpGYyufc70gQ== +electron-to-chromium@^1.4.668: + version "1.4.735" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.735.tgz#c32914ef2cd0a3a545a3def841d253a31a8a93be" + integrity sha512-pkYpvwg8VyOTQAeBqZ7jsmpCjko1Qc6We1ZtZCjRyYbT5v4AIUKDy5cQTRotQlSSZmMr8jqpEt6JtOj5k7lR7A== emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" @@ -934,10 +1023,10 @@ engine.io@~6.5.2: engine.io-parser "~5.2.1" ws "~8.11.0" -enhanced-resolve@^5.13.0: - version "5.15.0" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz#1af946c7d93603eb88e9896cee4904dc012e9c35" - integrity sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg== +enhanced-resolve@^5.16.0: + version "5.16.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.16.0.tgz#65ec88778083056cb32487faa9aef82ed0864787" + integrity sha512-O+QWCviPNSSLAD9Ucn8Awv+poAkqn3T1XY5/N7kR7rQO9yfSGWkYZDwpJ+iKF7B8rxaQKWngSqACpgzeapSyoA== dependencies: graceful-fs "^4.2.4" tapable "^2.2.0" @@ -1002,6 +1091,11 @@ etag@~1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== +event-target-shim@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" + integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== + eventemitter3@^4.0.0: version "4.0.7" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" @@ -1155,6 +1249,14 @@ follow-redirects@^1.0.0: resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.3.tgz#fe2f3ef2690afce7e82ed0b44db08165b207123a" integrity sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q== +foreground-child@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.1.1.tgz#1d173e776d75d2772fed08efe4a0de1ea1b12d0d" + integrity sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg== + dependencies: + cross-spawn "^7.0.0" + signal-exit "^4.0.1" + format-util@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/format-util/-/format-util-1.0.5.tgz#1ffb450c8a03e7bccffe40643180918cc297d271" @@ -1179,11 +1281,6 @@ fs-extra@^8.1.0: jsonfile "^4.0.0" universalify "^0.1.0" -fs-monkey@^1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.5.tgz#fe450175f0db0d7ea758102e1d84096acb925788" - integrity sha512-8uMbBjrhzW76TYgEV27Y5E//W2f/lTFmx78P2w19FZSxarhI/798APGQyuGCwmkNxgwGRhrLfvWyLBvNtuOmew== - fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -1231,17 +1328,27 @@ glob-to-regexp@^0.4.1: resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== -glob@7.2.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" - integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== +glob@8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" + integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" inherits "2" - minimatch "^3.0.4" + minimatch "^5.0.1" once "^1.3.0" - path-is-absolute "^1.0.0" + +glob@^10.3.7: + version "10.3.12" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.12.tgz#3a65c363c2e9998d220338e88a5f6ac97302960b" + integrity sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg== + dependencies: + foreground-child "^3.1.0" + jackspeak "^2.3.6" + minimatch "^9.0.1" + minipass "^7.0.4" + path-scurry "^1.10.2" glob@^7.1.3, glob@^7.1.7: version "7.2.3" @@ -1262,7 +1369,7 @@ gopd@^1.0.1: dependencies: get-intrinsic "^1.1.3" -graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.10, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: +graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.10, graceful-fs@^4.2.11, graceful-fs@^4.2.4, graceful-fs@^4.2.6: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== @@ -1316,10 +1423,10 @@ hpack.js@^2.1.6: readable-stream "^2.0.1" wbuf "^1.1.0" -html-entities@^2.3.2: - version "2.4.0" - resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.4.0.tgz#edd0cee70402584c8c76cc2c0556db09d1f45061" - integrity sha512-igBTJcNNNhvZFRtm8uA6xMY6xYleeDwn3PeBCkDz7tHttv4F2hsDI2aPgNERWzvRcNYHNT3ymRaQzllmXj4YsQ== +html-entities@^2.4.0: + version "2.5.2" + resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.5.2.tgz#201a3cf95d3a15be7099521620d19dfb4f65359f" + integrity sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA== http-deceiver@^1.2.7: version "1.2.7" @@ -1427,7 +1534,7 @@ ipaddr.js@1.9.1: resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== -ipaddr.js@^2.0.1: +ipaddr.js@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.1.0.tgz#2119bc447ff8c257753b196fc5f1ce08a4cdf39f" integrity sha512-LlbxQ7xKzfBusov6UMi4MFpEg0m+mAm9xyNGEduwXMEDuf4WfzB/RZwMVYEd7IKGvh4IUkEXYxtAVu9T3OelJQ== @@ -1446,10 +1553,10 @@ is-core-module@^2.13.0: dependencies: hasown "^2.0.0" -is-docker@^2.0.0, is-docker@^2.1.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" - integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== +is-docker@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-3.0.0.tgz#90093aa3106277d8a77a5910dbae71747e15a200" + integrity sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ== is-extglob@^2.1.1: version "2.1.1" @@ -1468,6 +1575,18 @@ is-glob@^4.0.1, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" +is-inside-container@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-inside-container/-/is-inside-container-1.0.0.tgz#e81fba699662eb31dbdaf26766a61d4814717ea4" + integrity sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA== + dependencies: + is-docker "^3.0.0" + +is-network-error@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-network-error/-/is-network-error-1.1.0.tgz#d26a760e3770226d11c169052f266a4803d9c997" + integrity sha512-tUdRRAnhT+OtCZR/LxZelH/C7QtjtFrTu5tXCA8pl55eTUElUHT+GPYV8MBMBvea/j+NxQqVt3LbWMRir7Gx9g== + is-number@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" @@ -1500,12 +1619,12 @@ is-unicode-supported@^0.1.0: resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== -is-wsl@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" - integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== +is-wsl@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-3.1.0.tgz#e1c657e39c10090afcbedec61720f6b924c3cbd2" + integrity sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw== dependencies: - is-docker "^2.0.0" + is-inside-container "^1.0.0" isarray@~1.0.0: version "1.0.0" @@ -1527,6 +1646,15 @@ isobject@^3.0.1: resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== +jackspeak@^2.3.6: + version "2.3.6" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.3.6.tgz#647ecc472238aee4b06ac0e461acc21a8c505ca8" + integrity sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + jest-worker@^27.4.5: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" @@ -1586,19 +1714,19 @@ karma-sourcemap-loader@0.4.0: dependencies: graceful-fs "^4.2.10" -karma-webpack@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/karma-webpack/-/karma-webpack-5.0.0.tgz#2a2c7b80163fe7ffd1010f83f5507f95ef39f840" - integrity sha512-+54i/cd3/piZuP3dr54+NcFeKOPnys5QeM1IY+0SPASwrtHsliXUiCL50iW+K9WWA7RvamC4macvvQ86l3KtaA== +karma-webpack@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/karma-webpack/-/karma-webpack-5.0.1.tgz#4eafd31bbe684a747a6e8f3e4ad373e53979ced4" + integrity sha512-oo38O+P3W2mSPCSUrQdySSPv1LvPpXP+f+bBimNomS5sW+1V4SuhCuW8TfJzV+rDv921w2fDSDw0xJbPe6U+kQ== dependencies: glob "^7.1.3" - minimatch "^3.0.4" + minimatch "^9.0.3" webpack-merge "^4.1.5" -karma@6.4.2: - version "6.4.2" - resolved "https://registry.yarnpkg.com/karma/-/karma-6.4.2.tgz#a983f874cee6f35990c4b2dcc3d274653714de8e" - integrity sha512-C6SU/53LB31BEgRg+omznBEMY4SjHU3ricV6zBcAe1EeILKkeScr+fZXtaI5WyDbkVowJxxAI6h73NcFPmXolQ== +karma@6.4.3: + version "6.4.3" + resolved "https://registry.yarnpkg.com/karma/-/karma-6.4.3.tgz#763e500f99597218bbb536de1a14acc4ceea7ce8" + integrity sha512-LuucC/RE92tJ8mlCwqEoRWXP38UMAqpnq98vktmS9SznSoUPPUJQbc91dHcxcunROvfQjdORVA/YFviH+Xci9Q== dependencies: "@colors/colors" "1.5.0" body-parser "^1.19.0" @@ -1619,7 +1747,7 @@ karma@6.4.2: qjobs "^1.2.0" range-parser "^1.2.1" rimraf "^3.0.2" - socket.io "^4.4.1" + socket.io "^4.7.2" source-map "^0.6.1" tmp "^0.2.1" ua-parser-js "^0.7.30" @@ -1630,7 +1758,7 @@ kind-of@^6.0.2: resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== -launch-editor@^2.6.0: +launch-editor@^2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/launch-editor/-/launch-editor-2.6.1.tgz#f259c9ef95cbc9425620bbbd14b468fcdb4ffe3c" integrity sha512-eB/uXmFVpY4zezmGp5XtU21kwo7GBbKB+EQ+UZeWtGb9yAM5xt/Evk+lYH3eRNAtId+ej4u7TYPFZ07w4s7rRw== @@ -1681,17 +1809,22 @@ log4js@^6.4.1: rfdc "^1.3.0" streamroller "^3.1.5" +lru-cache@^10.2.0: + version "10.2.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.2.0.tgz#0bd445ca57363465900f4d1f9bd8db343a4d95c3" + integrity sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q== + media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== -memfs@^3.4.3: - version "3.6.0" - resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.6.0.tgz#d7a2110f86f79dd950a8b6df6d57bc984aa185f6" - integrity sha512-EGowvkkgbMcIChjMTMkESFDbZeSh8xZ7kNSF0hAiAN4Jh6jgHCRS0Ga/+C8y6Au+oqpezRHCfPsmJ2+DwAgiwQ== +memfs@^4.6.0: + version "4.8.1" + resolved "https://registry.yarnpkg.com/memfs/-/memfs-4.8.1.tgz#1e02c15c4397212a9a1b037fa4324c6f7dd45b47" + integrity sha512-7q/AdPzf2WpwPlPL4v1kE2KsJsHl7EF4+hAeVzlyanr2+YnR21NVn9mDqo+7DEaKDRsQy8nvxPlKH4WqMtiO0w== dependencies: - fs-monkey "^1.0.4" + tslib "^2.0.0" merge-descriptors@1.0.1: version "1.0.1" @@ -1762,11 +1895,30 @@ minimatch@^3.0.4, minimatch@^3.1.1: dependencies: brace-expansion "^1.1.7" +minimatch@^5.0.1: + version "5.1.6" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" + integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^9.0.1, minimatch@^9.0.3: + version "9.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.4.tgz#8e49c731d1749cbec05050ee5145147b32496a51" + integrity sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw== + dependencies: + brace-expansion "^2.0.1" + minimist@^1.2.3, minimist@^1.2.6: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.0.4: + version "7.0.4" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.4.tgz#dbce03740f50a4786ba994c1fb908844d27b038c" + integrity sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ== + mkdirp@^0.5.5: version "0.5.6" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" @@ -1774,10 +1926,10 @@ mkdirp@^0.5.5: dependencies: minimist "^1.2.6" -mocha@10.2.0: - version "10.2.0" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.2.0.tgz#1fd4a7c32ba5ac372e03a17eef435bd00e5c68b8" - integrity sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg== +mocha@10.3.0: + version "10.3.0" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.3.0.tgz#0e185c49e6dccf582035c05fa91084a4ff6e3fe9" + integrity sha512-uF2XJs+7xSLsrmIvn37i/wnc91nw7XjOQB8ccyx5aEgdnohr7n+rEiZP23WkCYHjilR6+EboEnbq/ZQDz4LSbg== dependencies: ansi-colors "4.1.1" browser-stdout "1.3.1" @@ -1786,13 +1938,12 @@ mocha@10.2.0: diff "5.0.0" escape-string-regexp "4.0.0" find-up "5.0.0" - glob "7.2.0" + glob "8.1.0" he "1.2.0" js-yaml "4.1.0" log-symbols "4.1.0" minimatch "5.0.1" ms "2.1.3" - nanoid "3.3.3" serialize-javascript "6.0.0" strip-json-comments "3.1.1" supports-color "8.1.1" @@ -1824,11 +1975,6 @@ multicast-dns@^7.2.5: dns-packet "^5.2.2" thunky "^1.0.2" -nanoid@3.3.3: - version "3.3.3" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25" - integrity sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w== - negotiator@0.6.3: version "0.6.3" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" @@ -1839,6 +1985,13 @@ neo-async@^2.6.2: resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== +node-fetch@2.6.7: + version "2.6.7" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" + integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== + dependencies: + whatwg-url "^5.0.0" + node-forge@^1: version "1.3.1" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" @@ -1876,7 +2029,7 @@ obuf@^1.0.0, obuf@^1.1.2: resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== -on-finished@2.4.1: +on-finished@2.4.1, on-finished@^2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== @@ -1909,14 +2062,15 @@ onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" -open@^8.0.9: - version "8.4.2" - resolved "https://registry.yarnpkg.com/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9" - integrity sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ== +open@^10.0.3: + version "10.1.0" + resolved "https://registry.yarnpkg.com/open/-/open-10.1.0.tgz#a7795e6e5d519abe4286d9937bb24b51122598e1" + integrity sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw== dependencies: - define-lazy-prop "^2.0.0" - is-docker "^2.1.1" - is-wsl "^2.2.0" + default-browser "^5.2.1" + define-lazy-prop "^3.0.0" + is-inside-container "^1.0.0" + is-wsl "^3.1.0" p-limit@^2.2.0: version "2.3.0" @@ -1946,12 +2100,13 @@ p-locate@^5.0.0: dependencies: p-limit "^3.0.2" -p-retry@^4.5.0: - version "4.6.2" - resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-4.6.2.tgz#9baae7184057edd4e17231cee04264106e092a16" - integrity sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ== +p-retry@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-6.2.0.tgz#8d6df01af298750009691ce2f9b3ad2d5968f3bd" + integrity sha512-JA6nkq6hKyWLLasXQXUrO4z8BUZGUt/LjlJxx8Gb2+2ntodU/SS63YZ8b0LUTbQ8ZB9iwOfhEPhg4ykKnn2KsA== dependencies: - "@types/retry" "0.12.0" + "@types/retry" "0.12.2" + is-network-error "^1.0.0" retry "^0.13.1" p-try@^2.0.0: @@ -1984,6 +2139,14 @@ path-parse@^1.0.7: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== +path-scurry@^1.10.2: + version "1.10.2" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.10.2.tgz#8f6357eb1239d5fa1da8b9f70e9c080675458ba7" + integrity sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA== + dependencies: + lru-cache "^10.2.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + path-to-regexp@0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" @@ -2157,6 +2320,18 @@ rimraf@^3.0.0, rimraf@^3.0.2: dependencies: glob "^7.1.3" +rimraf@^5.0.5: + version "5.0.5" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-5.0.5.tgz#9be65d2d6e683447d2e9013da2bf451139a61ccf" + integrity sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A== + dependencies: + glob "^10.3.7" + +run-applescript@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/run-applescript/-/run-applescript-7.0.0.tgz#e5a553c2bffd620e169d276c1cd8f1b64778fbeb" + integrity sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A== + safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" @@ -2172,7 +2347,7 @@ safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.1.0, safe-buffer@~5.2.0: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -schema-utils@^3.1.1, schema-utils@^3.1.2: +schema-utils@^3.1.1, schema-utils@^3.2.0: version "3.3.0" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.3.0.tgz#f50a88877c3c01652a15b622ae9e9795df7a60fe" integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg== @@ -2181,7 +2356,7 @@ schema-utils@^3.1.1, schema-utils@^3.1.2: ajv "^6.12.5" ajv-keywords "^3.5.2" -schema-utils@^4.0.0: +schema-utils@^4.0.0, schema-utils@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.2.0.tgz#70d7c93e153a273a805801882ebd3bff20d89c8b" integrity sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw== @@ -2196,7 +2371,7 @@ select-hose@^2.0.0: resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" integrity sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg== -selfsigned@^2.1.1: +selfsigned@^2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-2.4.1.tgz#560d90565442a3ed35b674034cec4e95dceb4ae0" integrity sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q== @@ -2318,6 +2493,11 @@ signal-exit@^3.0.3: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== +signal-exit@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + socket.io-adapter@~2.5.2: version "2.5.2" resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz#5de9477c9182fdc171cd8c8364b9a8894ec75d12" @@ -2333,10 +2513,10 @@ socket.io-parser@~4.2.4: "@socket.io/component-emitter" "~3.1.0" debug "~4.3.1" -socket.io@^4.4.1: - version "4.7.2" - resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.7.2.tgz#22557d76c3f3ca48f82e73d68b7add36a22df002" - integrity sha512-bvKVS29/I5fl2FGLNHuXlQaUH/BlzX1IN6S+NKLNZpBsPZIDH+90eQmCs2Railn4YUiww4SzUedJ6+uzwFnKLw== +socket.io@^4.7.2: + version "4.7.5" + resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.7.5.tgz#56eb2d976aef9d1445f373a62d781a41c7add8f8" + integrity sha512-DmeAkF6cwM9jSfmp6Dr/5/mfMwb5Z5qRrSXLpo3Fq5SqyU8CMF15jIN4ZhfSwu35ksM1qmHZDQ/DK5XTccSTvA== dependencies: accepts "~1.3.4" base64id "~2.0.0" @@ -2360,12 +2540,11 @@ source-map-js@^1.0.2: resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== -source-map-loader@4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/source-map-loader/-/source-map-loader-4.0.1.tgz#72f00d05f5d1f90f80974eda781cbd7107c125f2" - integrity sha512-oqXpzDIByKONVY8g1NUPOTQhe0UTU5bWUl32GSkqK2LjJj0HmwTMVKxcUip0RgAYhY1mqgOxjbQM48a0mmeNfA== +source-map-loader@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/source-map-loader/-/source-map-loader-5.0.0.tgz#f593a916e1cc54471cfc8851b905c8a845fc7e38" + integrity sha512-k2Dur7CbSLcAH73sBcIkV5xjPV4SzqO1NJ7+XaQl8if3VODDUj3FNchNGpqgJSKbvUfJuhVdv8K2Eu8/TNl2eA== dependencies: - abab "^2.0.6" iconv-lite "^0.6.3" source-map-js "^1.0.2" @@ -2424,7 +2603,7 @@ streamroller@^3.1.5: debug "^4.3.4" fs-extra "^8.1.0" -string-width@^4.1.0, string-width@^4.2.0: +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -2433,6 +2612,15 @@ string-width@^4.1.0, string-width@^4.2.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" +string-width@^5.0.1, string-width@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" @@ -2447,13 +2635,20 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== dependencies: ansi-regex "^5.0.1" +strip-ansi@^7.0.1: + version "7.1.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== + dependencies: + ansi-regex "^6.0.1" + strip-final-newline@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" @@ -2488,21 +2683,21 @@ tapable@^2.1.1, tapable@^2.2.0: resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== -terser-webpack-plugin@^5.3.7: - version "5.3.9" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz#832536999c51b46d468067f9e37662a3b96adfe1" - integrity sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA== +terser-webpack-plugin@^5.3.10: + version "5.3.10" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz#904f4c9193c6fd2a03f693a2150c62a92f40d199" + integrity sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w== dependencies: - "@jridgewell/trace-mapping" "^0.3.17" + "@jridgewell/trace-mapping" "^0.3.20" jest-worker "^27.4.5" schema-utils "^3.1.1" serialize-javascript "^6.0.1" - terser "^5.16.8" + terser "^5.26.0" -terser@^5.16.8: - version "5.26.0" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.26.0.tgz#ee9f05d929f4189a9c28a0feb889d96d50126fe1" - integrity sha512-dytTGoE2oHgbNV9nTzgBEPaqAWvcJNl66VZ0BkJqlvp71IjO8CxdBx/ykCNb47cLnCmCvRZ6ZR0tLkqvZCdVBQ== +terser@^5.26.0: + version "5.30.3" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.30.3.tgz#f1bb68ded42408c316b548e3ec2526d7dd03f4d2" + integrity sha512-STdUgOUx8rLbMGO9IOwHLpCqolkDITFFQSMYYwKE1N2lY6MVSaeoi10z/EhWxRc6ybqoVmKSkhKYH/XUpl7vSA== dependencies: "@jridgewell/source-map" "^0.3.3" acorn "^8.8.2" @@ -2533,6 +2728,16 @@ toidentifier@1.0.1: resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== + +tslib@^2.0.0: + version "2.6.2" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" + integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== + type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" @@ -2541,10 +2746,10 @@ type-is@~1.6.18: media-typer "0.3.0" mime-types "~2.1.24" -typescript@5.0.4: - version "5.0.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.0.4.tgz#b217fd20119bd61a94d4011274e0ab369058da3b" - integrity sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw== +typescript@5.4.3: + version "5.4.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.4.3.tgz#5c6fedd4c87bee01cd7a528a30145521f8e0feff" + integrity sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg== ua-parser-js@^0.7.30: version "0.7.37" @@ -2606,10 +2811,10 @@ void-elements@^2.0.0: resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec" integrity sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung== -watchpack@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d" - integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg== +watchpack@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.1.tgz#29308f2cac150fa8e4c92f90e0ec954a9fed7fff" + integrity sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg== dependencies: glob-to-regexp "^0.4.1" graceful-fs "^4.1.2" @@ -2621,15 +2826,20 @@ wbuf@^1.1.0, wbuf@^1.7.3: dependencies: minimalistic-assert "^1.0.0" -webpack-cli@5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-5.1.0.tgz#abc4b1f44b50250f2632d8b8b536cfe2f6257891" - integrity sha512-a7KRJnCxejFoDpYTOwzm5o21ZXMaNqtRlvS183XzGDUPRdVEzJNImcQokqYZ8BNTnk9DkKiuWxw75+DCCoZ26w== +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== + +webpack-cli@5.1.4: + version "5.1.4" + resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-5.1.4.tgz#c8e046ba7eaae4911d7e71e2b25b776fcc35759b" + integrity sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg== dependencies: "@discoveryjs/json-ext" "^0.5.0" - "@webpack-cli/configtest" "^2.1.0" - "@webpack-cli/info" "^2.0.1" - "@webpack-cli/serve" "^2.0.3" + "@webpack-cli/configtest" "^2.1.1" + "@webpack-cli/info" "^2.0.2" + "@webpack-cli/serve" "^2.0.5" colorette "^2.0.14" commander "^10.0.1" cross-spawn "^7.0.3" @@ -2640,52 +2850,53 @@ webpack-cli@5.1.0: rechoir "^0.8.0" webpack-merge "^5.7.3" -webpack-dev-middleware@^5.3.1: - version "5.3.3" - resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz#efae67c2793908e7311f1d9b06f2a08dcc97e51f" - integrity sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA== +webpack-dev-middleware@^7.1.0: + version "7.2.1" + resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-7.2.1.tgz#2af00538b6e4eda05f5afdd5d711dbebc05958f7" + integrity sha512-hRLz+jPQXo999Nx9fXVdKlg/aehsw1ajA9skAneGmT03xwmyuhvF93p6HUKKbWhXdcERtGTzUCtIQr+2IQegrA== dependencies: colorette "^2.0.10" - memfs "^3.4.3" + memfs "^4.6.0" mime-types "^2.1.31" + on-finished "^2.4.1" range-parser "^1.2.1" schema-utils "^4.0.0" -webpack-dev-server@4.15.0: - version "4.15.0" - resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-4.15.0.tgz#87ba9006eca53c551607ea0d663f4ae88be7af21" - integrity sha512-HmNB5QeSl1KpulTBQ8UT4FPrByYyaLxpJoQ0+s7EvUrMc16m0ZS1sgb1XGqzmgCPk0c9y+aaXxn11tbLzuM7NQ== - dependencies: - "@types/bonjour" "^3.5.9" - "@types/connect-history-api-fallback" "^1.3.5" - "@types/express" "^4.17.13" - "@types/serve-index" "^1.9.1" - "@types/serve-static" "^1.13.10" - "@types/sockjs" "^0.3.33" - "@types/ws" "^8.5.1" +webpack-dev-server@5.0.4: + version "5.0.4" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-5.0.4.tgz#cb6ea47ff796b9251ec49a94f24a425e12e3c9b8" + integrity sha512-dljXhUgx3HqKP2d8J/fUMvhxGhzjeNVarDLcbO/EWMSgRizDkxHQDZQaLFL5VJY9tRBj2Gz+rvCEYYvhbqPHNA== + dependencies: + "@types/bonjour" "^3.5.13" + "@types/connect-history-api-fallback" "^1.5.4" + "@types/express" "^4.17.21" + "@types/serve-index" "^1.9.4" + "@types/serve-static" "^1.15.5" + "@types/sockjs" "^0.3.36" + "@types/ws" "^8.5.10" ansi-html-community "^0.0.8" - bonjour-service "^1.0.11" - chokidar "^3.5.3" + bonjour-service "^1.2.1" + chokidar "^3.6.0" colorette "^2.0.10" compression "^1.7.4" connect-history-api-fallback "^2.0.0" default-gateway "^6.0.3" express "^4.17.3" graceful-fs "^4.2.6" - html-entities "^2.3.2" + html-entities "^2.4.0" http-proxy-middleware "^2.0.3" - ipaddr.js "^2.0.1" - launch-editor "^2.6.0" - open "^8.0.9" - p-retry "^4.5.0" - rimraf "^3.0.2" - schema-utils "^4.0.0" - selfsigned "^2.1.1" + ipaddr.js "^2.1.0" + launch-editor "^2.6.1" + open "^10.0.3" + p-retry "^6.2.0" + rimraf "^5.0.5" + schema-utils "^4.2.0" + selfsigned "^2.4.1" serve-index "^1.9.1" sockjs "^0.3.24" spdy "^4.0.2" - webpack-dev-middleware "^5.3.1" - ws "^8.13.0" + webpack-dev-middleware "^7.1.0" + ws "^8.16.0" webpack-merge@^4.1.5: version "4.2.2" @@ -2708,34 +2919,34 @@ webpack-sources@^3.2.3: resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== -webpack@5.82.0: - version "5.82.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.82.0.tgz#3c0d074dec79401db026b4ba0fb23d6333f88e7d" - integrity sha512-iGNA2fHhnDcV1bONdUu554eZx+XeldsaeQ8T67H6KKHl2nUSwX8Zm7cmzOA46ox/X1ARxf7Bjv8wQ/HsB5fxBg== +webpack@5.91.0: + version "5.91.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.91.0.tgz#ffa92c1c618d18c878f06892bbdc3373c71a01d9" + integrity sha512-rzVwlLeBWHJbmgTC/8TvAcu5vpJNII+MelQpylD4jNERPwpBJOE2lEcko1zJX3QJeLjTTAnQxn/OJ8bjDzVQaw== dependencies: "@types/eslint-scope" "^3.7.3" - "@types/estree" "^1.0.0" - "@webassemblyjs/ast" "^1.11.5" - "@webassemblyjs/wasm-edit" "^1.11.5" - "@webassemblyjs/wasm-parser" "^1.11.5" + "@types/estree" "^1.0.5" + "@webassemblyjs/ast" "^1.12.1" + "@webassemblyjs/wasm-edit" "^1.12.1" + "@webassemblyjs/wasm-parser" "^1.12.1" acorn "^8.7.1" - acorn-import-assertions "^1.7.6" - browserslist "^4.14.5" + acorn-import-assertions "^1.9.0" + browserslist "^4.21.10" chrome-trace-event "^1.0.2" - enhanced-resolve "^5.13.0" + enhanced-resolve "^5.16.0" es-module-lexer "^1.2.1" eslint-scope "5.1.1" events "^3.2.0" glob-to-regexp "^0.4.1" - graceful-fs "^4.2.9" + graceful-fs "^4.2.11" json-parse-even-better-errors "^2.3.1" loader-runner "^4.2.0" mime-types "^2.1.27" neo-async "^2.6.2" - schema-utils "^3.1.2" + schema-utils "^3.2.0" tapable "^2.1.1" - terser-webpack-plugin "^5.3.7" - watchpack "^2.4.0" + terser-webpack-plugin "^5.3.10" + watchpack "^2.4.1" webpack-sources "^3.2.3" websocket-driver@>=0.5.1, websocket-driver@^0.7.4: @@ -2752,6 +2963,14 @@ websocket-extensions@>=0.1.1: resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42" integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + which@^1.2.1: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" @@ -2776,7 +2995,7 @@ workerpool@6.2.1: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== -wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -2785,15 +3004,29 @@ wrap-ansi@^7.0.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== -ws@^8.13.0: - version "8.15.1" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.15.1.tgz#271ba33a45ca0cc477940f7f200cd7fba7ee1997" - integrity sha512-W5OZiCjXEmk0yZ66ZN82beM5Sz7l7coYxpRkzS+p9PP+ToQry8szKh+61eNktr7EA9DOwvFGhfC605jDHbP6QQ== +ws@8.5.0: + version "8.5.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.5.0.tgz#bfb4be96600757fe5382de12c670dab984a1ed4f" + integrity sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg== + +ws@^8.16.0: + version "8.16.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.16.0.tgz#d1cd774f36fbc07165066a60e40323eab6446fd4" + integrity sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ== ws@~8.11.0: version "8.11.0" diff --git a/proto/.gitignore b/proto/.gitignore new file mode 100644 index 000000000..42afabfd2 --- /dev/null +++ b/proto/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/proto/build.gradle.kts b/proto/build.gradle.kts new file mode 100644 index 000000000..f3b9c3eab --- /dev/null +++ b/proto/build.gradle.kts @@ -0,0 +1,46 @@ +plugins { + kotlin("multiplatform") + id("com.android.library") + id("com.google.devtools.ksp") + id("com.squareup.wire") +} + +wire { + kotlin { + } +} + +kotlin { + jvmToolchain(17) + + androidTarget() + jvm() + + sourceSets { + androidMain { + dependencies { + implementation(libs.androidx.datastore) + } + } + } +} + +android { + compileSdk = AndroidSdk.compile + sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml") + + defaultConfig { + minSdk = AndroidSdk.min + } + + compileOptions { + isCoreLibraryDesugaringEnabled = true + } + + namespace = "dev.johnoreilly.confetti.proto" +} + + +dependencies { + coreLibraryDesugaring(libs.desugar) +} diff --git a/proto/proguard-rules.pro b/proto/proguard-rules.pro new file mode 100644 index 000000000..481bb4348 --- /dev/null +++ b/proto/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/proto/src/androidMain/AndroidManifest.xml b/proto/src/androidMain/AndroidManifest.xml new file mode 100644 index 000000000..a5918e68a --- /dev/null +++ b/proto/src/androidMain/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/shared/src/androidMain/kotlin/dev/johnoreilly/confetti/settings/WearSettingsSerializer.kt b/proto/src/androidMain/kotlin/dev/johnoreilly/confetti/settings/WearSettingsSerializer.kt similarity index 100% rename from shared/src/androidMain/kotlin/dev/johnoreilly/confetti/settings/WearSettingsSerializer.kt rename to proto/src/androidMain/kotlin/dev/johnoreilly/confetti/settings/WearSettingsSerializer.kt diff --git a/shared/src/commonMain/proto/wear.proto b/proto/src/commonMain/proto/wear.proto similarity index 100% rename from shared/src/commonMain/proto/wear.proto rename to proto/src/commonMain/proto/wear.proto diff --git a/settings.gradle.kts b/settings.gradle.kts index b08baf8b1..e9dc90468 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -9,9 +9,11 @@ pluginManagement { includeGroupByRegex(".*android.*") } } + mavenLocal() mavenCentral() maven(url = "https://androidx.dev/storage/compose-compiler/repository") maven(url = "https://maven.pkg.jetbrains.space/public/p/compose/dev") + maven("https://maven.pkg.jetbrains.space/kotlin/p/wasm/experimental") gradlePluginPortal { content { } @@ -45,6 +47,9 @@ include(":wearApp") include(":wearBenchmark") include(":webApp") include(":compose-desktop") +include(":compose-web") +include(":proto") + check(JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_17)) { "This project needs to be run with Java 17 or higher (found: ${JavaVersion.current()})." diff --git a/shared/build.gradle.kts b/shared/build.gradle.kts index eec36961d..bd515465d 100644 --- a/shared/build.gradle.kts +++ b/shared/build.gradle.kts @@ -1,5 +1,6 @@ @file:Suppress("OPT_IN_USAGE") import com.codingfeline.buildkonfig.compiler.FieldSpec +import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl import java.util.Properties plugins { @@ -8,7 +9,6 @@ plugins { id("com.apollographql.apollo3") id("org.jetbrains.compose") id("com.google.devtools.ksp") - id("com.squareup.wire") id("maven-publish") id("kotlinx-serialization") alias(libs.plugins.kmmbridge) @@ -21,12 +21,19 @@ dependencies { implementation(platform(libs.firebase.bom)) } -wire { - kotlin { - } -} kotlin { + @OptIn(ExperimentalWasmDsl::class) + wasmJs { + moduleName = "confetti" + browser { + commonWebpackConfig { + outputFileName = "confetti.js" + } + } + binaries.executable() + } + androidTarget() jvm() @@ -63,9 +70,15 @@ kotlin { implementation(libs.atomicfu) api(libs.kotlinx.datetime) + api("io.github.qdsfdhvh:image-loader:1.7.8") { + exclude(group = "io.ktor") + + // group = "io.ktor", name = "ktor-client-core" + } + + implementation("io.ktor:ktor-client-core:3.0.0-wasm2") + api(libs.bundles.multiplatform.settings) - api(libs.androidx.datastore) - api(libs.androidx.datastore.preferences) api(libs.koin.core) implementation(libs.koin.compose.multiplatform) @@ -75,10 +88,8 @@ kotlin { api(libs.decompose.decompose) api(libs.decompose.extensions.compose) - api(libs.essenty.lifecycle) - // Multiplatform Logging api(libs.kermit) implementation(compose.ui) @@ -86,9 +97,11 @@ kotlin { implementation(compose.foundation) implementation(compose.material3) implementation(compose.components.resources) + implementation(compose.materialIconsExtended) implementation(libs.coil3.compose) implementation(libs.coil3.network.ktor) api(libs.materialkolor) + api(libs.compose.window.size) api(libs.generativeai) } } @@ -101,6 +114,7 @@ kotlin { val mobileMain by getting { dependencies { implementation(libs.firebase.mpp.auth) + implementation(libs.apollo.normalized.cache.sqlite) } } @@ -111,6 +125,7 @@ kotlin { androidMain { dependsOn(mobileMain) dependencies { + api(project(":proto")) api(libs.androidx.lifecycle.viewmodel.ktx) implementation(libs.okhttp) implementation(libs.okhttp.coroutines) @@ -129,6 +144,9 @@ kotlin { api(libs.androidx.work.runtime.ktx) api(libs.multiplatform.settings.datastore) + api(libs.androidx.datastore) + api(libs.androidx.datastore.preferences) + api("com.mikepenz:multiplatform-markdown-renderer:0.14.0") } @@ -147,6 +165,13 @@ kotlin { implementation(libs.okhttp) implementation(libs.okhttp.coroutines) implementation(libs.apollo.testing) + implementation(libs.apollo.normalized.cache.sqlite) + } + } + + val wasmJsMain by getting { + dependencies { + implementation("io.ktor:ktor-client-js:3.0.0-wasm2") } } } diff --git a/shared/src/androidMain/kotlin/dev/johnoreilly/confetti/di/KoinAndroid.kt b/shared/src/androidMain/kotlin/dev/johnoreilly/confetti/di/KoinAndroid.kt index ed1e2a828..b86708955 100644 --- a/shared/src/androidMain/kotlin/dev/johnoreilly/confetti/di/KoinAndroid.kt +++ b/shared/src/androidMain/kotlin/dev/johnoreilly/confetti/di/KoinAndroid.kt @@ -14,6 +14,9 @@ import coil.ImageLoader import coil.decode.SvgDecoder import com.apollographql.apollo3.ApolloClient import com.apollographql.apollo3.cache.normalized.FetchPolicy +import com.apollographql.apollo3.cache.normalized.api.MemoryCacheFactory +import com.apollographql.apollo3.cache.normalized.api.NormalizedCacheFactory +import com.apollographql.apollo3.cache.normalized.sql.SqlNormalizedCacheFactory import com.apollographql.apollo3.network.http.DefaultHttpEngine import com.apollographql.apollo3.network.ws.DefaultWebSocketEngine import com.google.android.horologist.annotations.ExperimentalHorologistApi @@ -45,6 +48,7 @@ import org.koin.core.module.dsl.singleOf import org.koin.core.qualifier.named import org.koin.dsl.module +@OptIn(ExperimentalHorologistApi::class, ExperimentalSettingsImplementation::class) actual fun platformModule() = module { singleOf(::AndroidDateService) { bind() } single { @@ -115,4 +119,9 @@ actual fun platformModule() = module { val Context.settingsStore by preferencesDataStore("settings") -actual fun getDatabaseName(conference: String, uid: String?) = "$conference$uid.db" + +actual fun getNormalizedCacheFactory(conference: String, uid: String?): NormalizedCacheFactory { + val sqlNormalizedCacheFactory = SqlNormalizedCacheFactory("$conference$uid.db") + return MemoryCacheFactory(10 * 1024 * 1024) + .chain(sqlNormalizedCacheFactory) +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/dev/johnoreilly/confetti/ApolloClientCache.kt b/shared/src/commonMain/kotlin/dev/johnoreilly/confetti/ApolloClientCache.kt index 0534c1da6..cef4148bc 100644 --- a/shared/src/commonMain/kotlin/dev/johnoreilly/confetti/ApolloClientCache.kt +++ b/shared/src/commonMain/kotlin/dev/johnoreilly/confetti/ApolloClientCache.kt @@ -6,17 +6,14 @@ import com.apollographql.apollo3.api.ApolloRequest import com.apollographql.apollo3.api.ApolloResponse import com.apollographql.apollo3.api.ExecutionContext import com.apollographql.apollo3.api.Operation -import com.apollographql.apollo3.cache.normalized.api.MemoryCacheFactory import com.apollographql.apollo3.cache.normalized.apolloStore import com.apollographql.apollo3.cache.normalized.normalizedCache -import com.apollographql.apollo3.cache.normalized.sql.SqlNormalizedCacheFactory import com.apollographql.apollo3.exception.ApolloException import com.apollographql.apollo3.exception.ApolloHttpException import com.apollographql.apollo3.interceptor.ApolloInterceptor import com.apollographql.apollo3.interceptor.ApolloInterceptorChain import com.apollographql.apollo3.network.NetworkMonitor -import com.apollographql.apollo3.network.http.HttpNetworkTransport -import dev.johnoreilly.confetti.di.getDatabaseName +import dev.johnoreilly.confetti.di.getNormalizedCacheFactory import dev.johnoreilly.confetti.utils.registerApolloDebugServer import dev.johnoreilly.confetti.utils.unregisterApolloDebugServer import kotlinx.atomicfu.locks.reentrantLock @@ -42,8 +39,8 @@ class TokenProviderContext(val tokenProvider: TokenProvider) : ExecutionContext. } class ApolloClientCache : KoinComponent { - val _clients = mutableMapOf() - val mutex = reentrantLock() + private val _clients = mutableMapOf() + private val mutex = reentrantLock() private val tokenProviderInterceptor = object : ApolloInterceptor { override fun intercept( @@ -107,22 +104,20 @@ class ApolloClientCache : KoinComponent { uid: String?, writeToCacheAsynchronously: Boolean ): ApolloClient { - val sqlNormalizedCacheFactory = SqlNormalizedCacheFactory(getDatabaseName(conference, uid)) - val memoryFirstThenSqlCacheFactory = MemoryCacheFactory(10 * 1024 * 1024) - .chain(sqlNormalizedCacheFactory) + val normalizedCacheFactory = getNormalizedCacheFactory(conference, uid) return get() - .networkMonitor( - object : NetworkMonitor { - override val isOnline: Boolean - get() = true - override suspend fun waitForNetwork() {} - override fun close() {} - } - ) +// .networkMonitor( +// object : NetworkMonitor { +// override val isOnline: Boolean +// get() = true +// override suspend fun waitForNetwork() {} +// override fun close() {} +// } +// ) .addHttpHeader("conference", conference) .normalizedCache( - memoryFirstThenSqlCacheFactory, + normalizedCacheFactory, writeToCacheAsynchronously = writeToCacheAsynchronously ) .autoPersistedQueries() diff --git a/shared/src/commonMain/kotlin/dev/johnoreilly/confetti/ConfettiRepository.kt b/shared/src/commonMain/kotlin/dev/johnoreilly/confetti/ConfettiRepository.kt index ed68d3587..4566ff17d 100644 --- a/shared/src/commonMain/kotlin/dev/johnoreilly/confetti/ConfettiRepository.kt +++ b/shared/src/commonMain/kotlin/dev/johnoreilly/confetti/ConfettiRepository.kt @@ -70,7 +70,6 @@ class ConfettiRepository : KoinComponent { return response.data != null } - suspend fun addBookmark( conference: String, uid: String?, diff --git a/shared/src/commonMain/kotlin/dev/johnoreilly/confetti/decompose/SessionDetailsComponent.kt b/shared/src/commonMain/kotlin/dev/johnoreilly/confetti/decompose/SessionDetailsComponent.kt index aaef264de..92bd2705b 100644 --- a/shared/src/commonMain/kotlin/dev/johnoreilly/confetti/decompose/SessionDetailsComponent.kt +++ b/shared/src/commonMain/kotlin/dev/johnoreilly/confetti/decompose/SessionDetailsComponent.kt @@ -37,7 +37,7 @@ interface SessionDetailsComponent { sealed class SessionDetailsUiState { object Loading : SessionDetailsUiState() object Error : SessionDetailsUiState() - data class Success(val sessionDetails: SessionDetails) : SessionDetailsUiState() + data class Success(val conference: String, val sessionDetails: SessionDetails) : SessionDetailsUiState() } class DefaultSessionDetailsComponent( @@ -63,7 +63,7 @@ class DefaultSessionDetailsComponent( .map { val details = it.data?.session?.sessionDetails if (details != null) { - SessionDetailsUiState.Success(details) + SessionDetailsUiState.Success(conference, details) } else { SessionDetailsUiState.Error } diff --git a/shared/src/commonMain/kotlin/dev/johnoreilly/confetti/decompose/SessionsComponent.kt b/shared/src/commonMain/kotlin/dev/johnoreilly/confetti/decompose/SessionsComponent.kt index e46fcc028..2a1f5114d 100644 --- a/shared/src/commonMain/kotlin/dev/johnoreilly/confetti/decompose/SessionsComponent.kt +++ b/shared/src/commonMain/kotlin/dev/johnoreilly/confetti/decompose/SessionsComponent.kt @@ -304,6 +304,7 @@ class SessionsSimpleComponent( } return SessionsUiState.Success( now = dateService.now(), + conference = conference, conferenceName = conferenceName, venueLat = venueLat, venueLon = venueLon, @@ -326,6 +327,7 @@ sealed interface SessionsUiState { data class Success( val now: LocalDateTime, + val conference: String, val conferenceName: String, val venueLat: Double?, val venueLon: Double?, diff --git a/shared/src/commonMain/kotlin/dev/johnoreilly/confetti/decompose/SpeakerDetailsComponent.kt b/shared/src/commonMain/kotlin/dev/johnoreilly/confetti/decompose/SpeakerDetailsComponent.kt index 7509aa32a..1524b0782 100644 --- a/shared/src/commonMain/kotlin/dev/johnoreilly/confetti/decompose/SpeakerDetailsComponent.kt +++ b/shared/src/commonMain/kotlin/dev/johnoreilly/confetti/decompose/SpeakerDetailsComponent.kt @@ -23,7 +23,7 @@ interface SpeakerDetailsComponent { sealed class SpeakerDetailsUiState { object Loading : SpeakerDetailsUiState() object Error : SpeakerDetailsUiState() - class Success(val details: SpeakerDetails) : SpeakerDetailsUiState() + class Success(val conference: String, val details: SpeakerDetails) : SpeakerDetailsUiState() } class DefaultSpeakerDetailsComponent( @@ -43,7 +43,7 @@ class DefaultSpeakerDetailsComponent( ?.firstOrNull { it.id == speakerId } if (details != null) { - emit(Success(details)) + emit(Success(conference, details)) } else { emit(Error) } diff --git a/shared/src/commonMain/kotlin/dev/johnoreilly/confetti/di/Koin.kt b/shared/src/commonMain/kotlin/dev/johnoreilly/confetti/di/Koin.kt index 172184476..c1d505e13 100644 --- a/shared/src/commonMain/kotlin/dev/johnoreilly/confetti/di/Koin.kt +++ b/shared/src/commonMain/kotlin/dev/johnoreilly/confetti/di/Koin.kt @@ -2,6 +2,7 @@ package dev.johnoreilly.confetti.di +import com.apollographql.apollo3.cache.normalized.api.NormalizedCacheFactory import com.russhwolf.settings.ExperimentalSettingsApi import dev.johnoreilly.confetti.ApolloClientCache import dev.johnoreilly.confetti.AppSettings @@ -22,7 +23,7 @@ fun initKoin(appDeclaration: KoinAppDeclaration = {}) = } // called by iOS client -fun initKoin() = initKoin() {} +fun initKoin() = initKoin {} fun commonModule() = module { includes(platformModule()) @@ -34,4 +35,4 @@ fun commonModule() = module { singleOf(::GeminiApi) } -expect fun getDatabaseName(conference: String, uid: String?): String +expect fun getNormalizedCacheFactory(conference: String, uid: String?): NormalizedCacheFactory diff --git a/shared/src/commonMain/kotlin/dev/johnoreilly/confetti/ui/ConferenceListView.kt b/shared/src/commonMain/kotlin/dev/johnoreilly/confetti/ui/ConferenceListView.kt index 214f9d514..104600169 100644 --- a/shared/src/commonMain/kotlin/dev/johnoreilly/confetti/ui/ConferenceListView.kt +++ b/shared/src/commonMain/kotlin/dev/johnoreilly/confetti/ui/ConferenceListView.kt @@ -94,10 +94,10 @@ fun ConferenceCard( Card( modifier = Modifier .clip(shape = RoundedCornerShape(8.dp)) + .padding(8.dp) .clickable(onClick = { navigateToConference(conference) }) - .padding(8.dp) .fillMaxWidth() ) { Column(Modifier.fillMaxWidth().padding(16.dp), diff --git a/shared/src/commonMain/kotlin/dev/johnoreilly/confetti/ui/SessionDetailsViewShared.kt b/shared/src/commonMain/kotlin/dev/johnoreilly/confetti/ui/SessionDetailsViewShared.kt index b073ed0e5..543003d88 100644 --- a/shared/src/commonMain/kotlin/dev/johnoreilly/confetti/ui/SessionDetailsViewShared.kt +++ b/shared/src/commonMain/kotlin/dev/johnoreilly/confetti/ui/SessionDetailsViewShared.kt @@ -53,6 +53,7 @@ import sessionTimeFormat @OptIn(ExperimentalLayoutApi::class) @Composable internal fun SessionDetailViewShared( + conference: String, session: SessionDetails?, onSpeakerClick: (speakerId: String) -> Unit, onSocialLinkClicked: (String) -> Unit @@ -120,7 +121,7 @@ internal fun SessionDetailViewShared( Column(modifier = Modifier.padding(contentPadding)) { session.speakers.forEach { speaker -> - SessionSpeakerInfo(speaker.speakerDetails, onSpeakerClick, onSocialLinkClicked) + SessionSpeakerInfo(conference, speaker.speakerDetails, onSpeakerClick, onSocialLinkClicked) } } @@ -134,6 +135,7 @@ internal fun SessionDetailViewShared( @OptIn(ExperimentalResourceApi::class) @Composable internal fun SessionSpeakerInfo( + conference: String, speaker: SpeakerDetails, onSpeakerClick: (speakerId: String) -> Unit, onSocialLinkClick: (String) -> Unit @@ -144,8 +146,9 @@ internal fun SessionSpeakerInfo( ) { Row { speaker.photoUrl?.let { + val url = "https://confetti-app.dev/images/avatar/${conference}/${speaker.id}" AsyncImage( - model = speaker.photoUrl, + model = url, contentDescription = speaker.name, contentScale = ContentScale.Crop, modifier = Modifier.size(64.dp) diff --git a/shared/src/commonMain/kotlin/dev/johnoreilly/confetti/ui/SessionListGridView.kt b/shared/src/commonMain/kotlin/dev/johnoreilly/confetti/ui/SessionListGridView.kt index f842041e6..cf95598fe 100644 --- a/shared/src/commonMain/kotlin/dev/johnoreilly/confetti/ui/SessionListGridView.kt +++ b/shared/src/commonMain/kotlin/dev/johnoreilly/confetti/ui/SessionListGridView.kt @@ -8,7 +8,6 @@ import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ExperimentalLayoutApi -import androidx.compose.foundation.layout.FlowColumn import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -23,6 +22,11 @@ import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Bolt +import androidx.compose.material.icons.outlined.Bookmark +import androidx.compose.material.icons.outlined.BookmarkAdd +import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface @@ -38,7 +42,6 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @@ -47,6 +50,7 @@ import dev.johnoreilly.confetti.decompose.SessionsUiState import dev.johnoreilly.confetti.fragment.RoomDetails import dev.johnoreilly.confetti.fragment.SessionDetails import dev.johnoreilly.confetti.isLightning +import dev.johnoreilly.confetti.isService @OptIn(ExperimentalFoundationApi::class) @Composable @@ -59,13 +63,13 @@ fun SessionListGridView( isLoggedIn: Boolean, onRefresh: () -> Unit, ) { - println(uiState) when (uiState) { SessionsUiState.Error -> ErrorView(onRefresh) SessionsUiState.Loading -> LoadingView() is SessionsUiState.Success -> { + println("SessionsUiState.Success, conference = ${uiState.conferenceName}") Column { val pagerState = rememberPagerState { uiState.formattedConfDates.size @@ -75,8 +79,7 @@ fun SessionListGridView( HorizontalPager(state = pagerState) { page -> - Row(Modifier.horizontalScroll(rememberScrollState())) { - val sessionsByStartTime = uiState.sessionsByStartTimeList[page] + Row(Modifier.horizontalScroll(rememberScrollState())) { val sessionsByStartTime = uiState.sessionsByStartTimeList[page] val rooms = uiState.rooms.filter { room -> sessionsByStartTime.values.any { session -> session.any { it.room?.name == room.name } } @@ -112,6 +115,7 @@ fun SessionListGridView( sessionsByStartTime.forEach { item { SessionGridRow( + conference = uiState.conference, sessionByTimeList = it, rooms = rooms, bookmarks = uiState.bookmarks, @@ -137,6 +141,7 @@ fun SessionListGridView( @Composable fun SessionGridRow( + conference: String, sessionByTimeList: Map.Entry>, bookmarks: Set, rooms: List, @@ -163,58 +168,73 @@ fun SessionGridRow( sessionByTimeList.value.find { it.room?.name == room.name } } - sessionList.forEach { session -> - Surface( - modifier = Modifier - .width(sessionInfoWidth) - .height(220.dp) - .padding(bottom = 16.dp) - .border(BorderStroke(1.dp, MaterialTheme.colorScheme.primary)), - color = MaterialTheme.colorScheme.surfaceContainerLow - ) { - Box(Modifier.fillMaxSize()) { - Column( - modifier = Modifier - .clickable(onClick = { - sessionSelected(session.id) - }) - .padding(16.dp), - horizontalAlignment = Alignment.CenterHorizontally - ) { - Text( - modifier = Modifier.align(Alignment.Start), - text = session.title, - fontSize = 16.sp, - textAlign = TextAlign.Start, - color = MaterialTheme.colorScheme.onSecondaryContainer - ) - Spacer(modifier = Modifier.weight(1f)) - Speakers(session = session) - if (session.isLightning()) { - Surface( - modifier = Modifier.fillMaxWidth().padding(top = 8.dp), - shape = MaterialTheme.shapes.small, - color = MaterialTheme.colorScheme.primaryContainer - ) { - Row(Modifier.padding(vertical = 4.dp, horizontal = 8.dp)) { - // TODO find alternative - //Icon(Icons.Default.Bolt, "lightning") - Spacer(Modifier.width(4.dp)) - Text("Lightning / ${session.startsAt.time}-${session.endsAt.time}") + val height = if (sessionList.size == 1 && sessionList[0].isService()) + 100.dp + else + 220.dp + + rooms.forEach { room -> + val session = sessionList.firstOrNull { it.room?.name == room.name } + + if (session != null) { + Surface( + modifier = Modifier + .width(sessionInfoWidth) + .height(height) + .padding(bottom = 16.dp) + .clickable(onClick = { + sessionSelected(session.id) + }) + .border(BorderStroke(1.dp, MaterialTheme.colorScheme.primary)), + color = MaterialTheme.colorScheme.surfaceContainerLow + ) { + Box(Modifier.fillMaxSize()) { + Column( + modifier = Modifier + .padding(16.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + modifier = Modifier.align(Alignment.Start), + text = session.title, + fontSize = 16.sp, + textAlign = TextAlign.Start, + color = MaterialTheme.colorScheme.onSecondaryContainer + ) + //Spacer(modifier = Modifier.weight(1f)) + Spacer(modifier = Modifier.height(8.dp)) + Speakers(conference, session) + if (session.isLightning()) { + Surface( + modifier = Modifier.fillMaxWidth().padding(top = 8.dp), + shape = MaterialTheme.shapes.small, + color = MaterialTheme.colorScheme.primaryContainer + ) { + Row(Modifier.padding(vertical = 4.dp, horizontal = 8.dp)) { + // TODO find alternative + Icon(Icons.Default.Bolt, "lightning") + Spacer(Modifier.width(4.dp)) + Text("Lightning / ${session.startsAt.time}-${session.endsAt.time}") + } } } } + Bookmark( + modifier = Modifier.align(Alignment.CenterEnd), + bookmarks = bookmarks, + session = session, + isLoggedIn = isLoggedIn, + removeBookmark = removeBookmark, + addBookmark = addBookmark, + onNavigateToSignIn = onNavigateToSignIn + ) } - Bookmark( - modifier = Modifier.align(Alignment.CenterEnd), - bookmarks = bookmarks, - session = session, - isLoggedIn = isLoggedIn, - removeBookmark = removeBookmark, - addBookmark = addBookmark, - onNavigateToSignIn = onNavigateToSignIn - ) } + } else { + Box(Modifier + .width(sessionInfoWidth) + .height(height) + ) } } } @@ -222,57 +242,33 @@ fun SessionGridRow( @Composable @OptIn(ExperimentalLayoutApi::class) -private fun Speakers(session: SessionDetails) { - if (session.speakers.count() < 4) { - session.speakers.forEach { speaker -> - Row( - Modifier - .fillMaxWidth() - .padding(2.dp), - verticalAlignment = Alignment.CenterVertically - ) { - if (speaker.speakerDetails.photoUrl?.isNotEmpty() == true) { - AsyncImage( - model = speaker.speakerDetails.photoUrl, - contentDescription = speaker.speakerDetails.name, - contentScale = ContentScale.Fit, - modifier = Modifier - .size(20.dp) - .clip(RoundedCornerShape(16.dp)) - ) - } - - Spacer(modifier = Modifier.width(8.dp)) - Text( - text = speaker.speakerDetails.name, - style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.primary +private fun Speakers(conference: String, session: SessionDetails) { + session.speakers.forEach { speaker -> + Row( + Modifier + .fillMaxWidth() + .padding(2.dp), + verticalAlignment = Alignment.CenterVertically + ) { + if (speaker.speakerDetails.photoUrl?.isNotEmpty() == true) { + val url = "https://confetti-app.dev/images/avatar/${conference}/${speaker.id}" + AsyncImage( + model = url, + contentDescription = speaker.speakerDetails.name, + contentScale = ContentScale.Fit, + modifier = Modifier + .size(20.dp) + .clip(RoundedCornerShape(16.dp)) ) - - } - } - } else { - FlowColumn(modifier = Modifier.fillMaxWidth(), maxItemsInEachColumn = 2) { - session.speakers.forEach { speaker -> - Row { - AsyncImage( - model = speaker.speakerDetails.photoUrl, - contentDescription = speaker.speakerDetails.name, - contentScale = ContentScale.Fit, - modifier = Modifier - .size(20.dp) - .clip(RoundedCornerShape(16.dp)) - ) - Spacer(modifier = Modifier.width(8.dp)) - Text( - text = speaker.speakerDetails.name, - style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.primary, - overflow = TextOverflow.Ellipsis - ) - Spacer(modifier = Modifier.width(4.dp)) - } } + + Spacer(modifier = Modifier.width(8.dp)) + Text( + text = speaker.speakerDetails.name, + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.primary + ) + } } } @@ -301,15 +297,16 @@ private fun Bookmark( } } ) { - // TODO find alternative -// Icon( -// imageVector = Icons.Outlined.Bookmark, -// contentDescription = "remove bookmark", -// tint = MaterialTheme.colorScheme.primary, -// modifier = Modifier.padding(8.dp) -// ) + Icon( + imageVector = Icons.Outlined.Bookmark, + contentDescription = "remove bookmark", + tint = MaterialTheme.colorScheme.primary, + modifier = Modifier.padding(8.dp) + ) } } else { + // disable from this view for now +/* IconButton( modifier = modifier, onClick = { @@ -320,13 +317,14 @@ private fun Bookmark( } } ) { -// TODO find alternative -// Icon( -// imageVector = Icons.Outlined.BookmarkAdd, -// contentDescription = "add bookmark", -// modifier = Modifier.padding(8.dp) -// ) + Icon( + imageVector = Icons.Outlined.BookmarkAdd, + contentDescription = "add bookmark", + modifier = Modifier.padding(8.dp) + ) } + + */ } if (showDialog) { SignInDialog( diff --git a/shared/src/commonMain/kotlin/dev/johnoreilly/confetti/ui/SessionListView.kt b/shared/src/commonMain/kotlin/dev/johnoreilly/confetti/ui/SessionListView.kt new file mode 100644 index 000000000..ba8caf99d --- /dev/null +++ b/shared/src/commonMain/kotlin/dev/johnoreilly/confetti/ui/SessionListView.kt @@ -0,0 +1,234 @@ +@file:OptIn(ExperimentalFoundationApi::class) + +package dev.johnoreilly.confetti.ui + +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.foundation.pager.HorizontalPager +import androidx.compose.foundation.pager.PagerState +import androidx.compose.foundation.pager.rememberPagerState +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.AccessTime +import androidx.compose.material.icons.filled.Bolt +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.TabRow +import androidx.compose.material3.TabRowDefaults +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clipToBounds +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import dev.johnoreilly.confetti.decompose.SessionsUiState +import dev.johnoreilly.confetti.fragment.SessionDetails +import dev.johnoreilly.confetti.isBreak +import dev.johnoreilly.confetti.isLightning +import dev.johnoreilly.confetti.isService +import dev.johnoreilly.confetti.sessionSpeakers +import dev.johnoreilly.confetti.ui.ErrorView +import dev.johnoreilly.confetti.ui.LoadingView +import dev.johnoreilly.confetti.ui.SignInDialog +import dev.johnoreilly.confetti.ui.component.ConfettiHeader +import dev.johnoreilly.confetti.ui.component.ConfettiTab +import dev.johnoreilly.confetti.ui.component.pagerTabIndicatorOffset +import kotlinx.coroutines.launch +import kotlin.math.abs + +@OptIn(ExperimentalFoundationApi::class) +@Composable +fun SessionListView( + uiState: SessionsUiState, + sessionSelected: (sessionId: String) -> Unit, + addBookmark: (sessionId: String) -> Unit, + removeBookmark: (sessionId: String) -> Unit, + onRefresh: () -> Unit, + onNavigateToSignIn: () -> Unit, + isLoggedIn: Boolean, +) { + when (uiState) { + SessionsUiState.Error -> ErrorView(onRefresh) + SessionsUiState.Loading -> LoadingView() + is SessionsUiState.Success -> { + Column { + val initialPageIndex by remember { + derivedStateOf { uiState.confDates.indexOf(uiState.now.date) } + } + + val pagerState = rememberPagerState( + if (initialPageIndex == -1) 0 else initialPageIndex + ) { + uiState.formattedConfDates.size + } + + SessionListTabRow(pagerState, uiState) + + HorizontalPager(state = pagerState) { page -> + val sessions = uiState.sessionsByStartTimeList[page] + + val initialItemIndex by remember { + derivedStateOf { + // If initial page is null, we are in the wrong date and should not + // consider the current hour. + if (initialPageIndex != page) return@derivedStateOf 0 + + // Retrieves the initial item matching an hour block, including the + // aggregated index (ignores time grouping). + val initialItem = sessions + .values + .flatten() + .withIndex() + .minByOrNull { (_, session) -> + val timeOfNowInMillis = uiState + .now + .time + .toMillisecondOfDay() + val timeOfSessionStartInMillis = session + .startsAt + .time + .toMillisecondOfDay() + abs(timeOfNowInMillis - timeOfSessionStartInMillis) + } + + // Count the number of sticky headers until the initial item. + val stickyHeader = sessions + .entries + .withIndex() + .firstOrNull { + it.value.value.contains(initialItem?.value) + } + + val stickyHeaderIndex = stickyHeader?.index ?: 0 + val initialItemIndex = initialItem?.index ?: 0 + + // Sum the index of the initial item and the sticky headers. + stickyHeaderIndex + initialItemIndex + } + } + + val listState = rememberLazyListState(initialItemIndex) + + Box( + Modifier + .clipToBounds() + ) { + LazyColumn(state = listState) { + sessions.forEach { (startTime, sessions) -> + + stickyHeader { + ConfettiHeader(icon = Icons.Filled.AccessTime, text = startTime) + } + + val sortedSessions = + sessions.sortedBy { session -> uiState.rooms.indexOfFirst { it.name == session.room?.name } } + items(sortedSessions) { session -> + SessionItemView( + session = session, + sessionSelected = sessionSelected, + isBookmarked = uiState.bookmarks.contains(session.id), + addBookmark = addBookmark, + removeBookmark = removeBookmark, + onNavigateToSignIn = onNavigateToSignIn, + isLoggedIn = isLoggedIn, + ) + } + } + } + } + } + } + } + } +} + + +@Composable +fun SessionItemView( + session: SessionDetails, + sessionSelected: (sessionId: String) -> Unit, + isBookmarked: Boolean, + addBookmark: (String) -> Unit, + removeBookmark: (String) -> Unit, + onNavigateToSignIn: () -> Unit = {}, + isLoggedIn: Boolean, +) { + + var modifier = Modifier.fillMaxSize() + if (!session.isBreak()) { + modifier = modifier.clickable(onClick = { + sessionSelected(session.id) + }) + } + + if (session.isService()) { + modifier = modifier.background(Color.White) + } + + Row(modifier.padding(horizontal = 16.dp, vertical = 8.dp)) { + Column(modifier = Modifier.weight(1f)) { + Row(verticalAlignment = Alignment.CenterVertically) { + Text( + text = session.title, + style = MaterialTheme.typography.bodyLarge.copy(fontWeight = FontWeight.Bold) + ) + } + + session.room?.let { room -> + Row( + modifier = Modifier.padding(top = 4.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Text( + session.sessionSpeakers() ?: "", + style = MaterialTheme.typography.bodyMedium + ) + } + + Row( + modifier = Modifier.padding(top = 4.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Text( + room.name, + style = MaterialTheme.typography.bodyMedium, + color = Color.Gray + ) + } + } + + if (session.isLightning()) { + Surface( + modifier = Modifier.padding(top = 8.dp), + shape = MaterialTheme.shapes.small, + color = MaterialTheme.colorScheme.primaryContainer, + ) { + Row(Modifier.padding(vertical = 4.dp, horizontal = 8.dp)) { + Icon(Icons.Default.Bolt, "lightning") + Spacer(Modifier.width(4.dp)) + Text("Lightning / ${session.startsAt.time}-${session.endsAt.time}") + } + } + } + } + } +} diff --git a/shared/src/commonMain/kotlin/dev/johnoreilly/confetti/ui/SpeakersDetailsView.kt b/shared/src/commonMain/kotlin/dev/johnoreilly/confetti/ui/SpeakersDetailsView.kt index 640fba1a8..2931cbcca 100644 --- a/shared/src/commonMain/kotlin/dev/johnoreilly/confetti/ui/SpeakersDetailsView.kt +++ b/shared/src/commonMain/kotlin/dev/johnoreilly/confetti/ui/SpeakersDetailsView.kt @@ -45,6 +45,7 @@ import org.jetbrains.compose.resources.stringResource @OptIn(ExperimentalMaterial3Api::class) @Composable fun SpeakerDetailsView( + conference: String, speaker: SpeakerDetails, navigateToSession: (id: String) -> Unit, popBack: () -> Unit @@ -98,9 +99,9 @@ fun SpeakerDetailsView( Spacer(modifier = Modifier.size(16.dp)) - + val url = "https://confetti-app.dev/images/avatar/${conference}/${speaker.id}" AsyncImage( - model = speaker.photoUrl, + model = url, contentDescription = speaker.name, contentScale = ContentScale.Fit, modifier = Modifier diff --git a/shared/src/commonMain/kotlin/dev/johnoreilly/confetti/ui/SpeakersGridView.kt b/shared/src/commonMain/kotlin/dev/johnoreilly/confetti/ui/SpeakersGridView.kt index 3c11469ad..855ee42e4 100644 --- a/shared/src/commonMain/kotlin/dev/johnoreilly/confetti/ui/SpeakersGridView.kt +++ b/shared/src/commonMain/kotlin/dev/johnoreilly/confetti/ui/SpeakersGridView.kt @@ -25,6 +25,7 @@ import dev.johnoreilly.confetti.fragment.SpeakerDetails @Composable fun SpeakerGridView( + conference: String, speakers: List, navigateToSpeaker: (id: String) -> Unit ) { @@ -39,8 +40,11 @@ fun SpeakerGridView( .padding(12.dp), horizontalAlignment = Alignment.CenterHorizontally ) { + + // proxy image requests through backend + val url = "https://confetti-app.dev/images/avatar/${conference}/${speaker.id}" AsyncImage( - model = speaker.photoUrl, + model = url, contentDescription = speaker.name, contentScale = ContentScale.Fit, modifier = Modifier @@ -58,4 +62,4 @@ fun SpeakerGridView( } } ) -} +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/dev/johnoreilly/confetti/utils/UIUtils.kt b/shared/src/commonMain/kotlin/dev/johnoreilly/confetti/utils/UIUtils.kt new file mode 100644 index 000000000..7284b09c3 --- /dev/null +++ b/shared/src/commonMain/kotlin/dev/johnoreilly/confetti/utils/UIUtils.kt @@ -0,0 +1,7 @@ +package dev.johnoreilly.confetti.utils + +import androidx.compose.material3.windowsizeclass.WindowSizeClass +import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass + +val WindowSizeClass.isExpanded: Boolean + get() = widthSizeClass == WindowWidthSizeClass.Expanded \ No newline at end of file diff --git a/shared/src/iosMain/kotlin/dev/johnoreilly/confetti/di/KoiniOS.kt b/shared/src/iosMain/kotlin/dev/johnoreilly/confetti/di/KoiniOS.kt index bc3ac9ee5..ad31af3c4 100644 --- a/shared/src/iosMain/kotlin/dev/johnoreilly/confetti/di/KoiniOS.kt +++ b/shared/src/iosMain/kotlin/dev/johnoreilly/confetti/di/KoiniOS.kt @@ -4,6 +4,7 @@ package dev.johnoreilly.confetti.di import com.apollographql.apollo3.ApolloClient import com.apollographql.apollo3.cache.normalized.FetchPolicy +import com.apollographql.apollo3.cache.normalized.api.MemoryCacheFactory import com.apollographql.apollo3.cache.normalized.api.NormalizedCacheFactory import com.apollographql.apollo3.cache.normalized.sql.SqlNormalizedCacheFactory import com.russhwolf.settings.ExperimentalSettingsApi @@ -11,22 +12,19 @@ import com.russhwolf.settings.NSUserDefaultsSettings import com.russhwolf.settings.ObservableSettings import com.russhwolf.settings.coroutines.toFlowSettings import dev.johnoreilly.confetti.auth.Authentication -import dev.johnoreilly.confetti.auth.DefaultAuthentication import dev.johnoreilly.confetti.utils.DateService import dev.johnoreilly.confetti.utils.IosDateService -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers import org.koin.core.module.dsl.bind import org.koin.core.module.dsl.singleOf import org.koin.dsl.module import platform.Foundation.NSUserDefaults -import platform.posix.bind + +@OptIn(ExperimentalSettingsApi::class) actual fun platformModule() = module { single { Authentication.Disabled } single { NSUserDefaultsSettings(NSUserDefaults.standardUserDefaults) } single { get().toFlowSettings() } - single { SqlNormalizedCacheFactory("confetti.db") } singleOf(::IosDateService) { bind() } single { FetchPolicy.CacheAndNetwork } factory { @@ -35,4 +33,8 @@ actual fun platformModule() = module { } } -actual fun getDatabaseName(conference: String, uid: String?) = "$conference$uid.db" +actual fun getNormalizedCacheFactory(conference: String, uid: String?): NormalizedCacheFactory { + val sqlNormalizedCacheFactory = SqlNormalizedCacheFactory("$conference$uid.db") + return MemoryCacheFactory(10 * 1024 * 1024) + .chain(sqlNormalizedCacheFactory) +} \ No newline at end of file diff --git a/shared/src/jvmMain/kotlin/dev/johnoreilly/confetti/di/KoinJVM.kt b/shared/src/jvmMain/kotlin/dev/johnoreilly/confetti/di/KoinJVM.kt index 8e64d3b15..e0048ebff 100644 --- a/shared/src/jvmMain/kotlin/dev/johnoreilly/confetti/di/KoinJVM.kt +++ b/shared/src/jvmMain/kotlin/dev/johnoreilly/confetti/di/KoinJVM.kt @@ -1,9 +1,11 @@ -@file:OptIn(ExperimentalSettingsApi::class, ApolloExperimental::class) +@file:OptIn(ExperimentalSettingsApi::class) package dev.johnoreilly.confetti.di -import com.apollographql.apollo3.annotations.ApolloExperimental import com.apollographql.apollo3.cache.normalized.FetchPolicy +import com.apollographql.apollo3.cache.normalized.api.MemoryCacheFactory +import com.apollographql.apollo3.cache.normalized.api.NormalizedCacheFactory +import com.apollographql.apollo3.cache.normalized.sql.SqlNormalizedCacheFactory import com.russhwolf.settings.ExperimentalSettingsApi import com.russhwolf.settings.ObservableSettings import com.russhwolf.settings.PreferencesSettings @@ -32,4 +34,8 @@ actual fun platformModule() = module { } } -actual fun getDatabaseName(conference: String, uid: String?) = "jdbc:sqlite:$conference$uid.db" +actual fun getNormalizedCacheFactory(conference: String, uid: String?): NormalizedCacheFactory { + val sqlNormalizedCacheFactory = SqlNormalizedCacheFactory("jdbc:sqlite:$conference$uid.db") + return MemoryCacheFactory(10 * 1024 * 1024) + .chain(sqlNormalizedCacheFactory) +} \ No newline at end of file diff --git a/shared/src/wasmJsMain/kotlin/dev/johnoreilly/confetti/decompose/SettingsComponent.kt b/shared/src/wasmJsMain/kotlin/dev/johnoreilly/confetti/decompose/SettingsComponent.kt new file mode 100644 index 000000000..7c9da56ea --- /dev/null +++ b/shared/src/wasmJsMain/kotlin/dev/johnoreilly/confetti/decompose/SettingsComponent.kt @@ -0,0 +1,3 @@ +package dev.johnoreilly.confetti.decompose + +actual interface SettingsComponent \ No newline at end of file diff --git a/shared/src/wasmJsMain/kotlin/dev/johnoreilly/confetti/di/KoinWasmJs.kt b/shared/src/wasmJsMain/kotlin/dev/johnoreilly/confetti/di/KoinWasmJs.kt new file mode 100644 index 000000000..199288022 --- /dev/null +++ b/shared/src/wasmJsMain/kotlin/dev/johnoreilly/confetti/di/KoinWasmJs.kt @@ -0,0 +1,33 @@ +@file:OptIn(ExperimentalSettingsApi::class, ApolloExperimental::class) + +package dev.johnoreilly.confetti.di + +import com.apollographql.apollo3.annotations.ApolloExperimental +import com.apollographql.apollo3.cache.normalized.FetchPolicy +import com.apollographql.apollo3.cache.normalized.api.MemoryCacheFactory +import com.apollographql.apollo3.cache.normalized.api.NormalizedCacheFactory +import com.russhwolf.settings.ExperimentalSettingsApi +import com.russhwolf.settings.StorageSettings +import com.russhwolf.settings.coroutines.FlowSettings +import com.russhwolf.settings.coroutines.toFlowSettings +import com.russhwolf.settings.serialization.toRuntimeObservable +import dev.johnoreilly.confetti.auth.Authentication +import dev.johnoreilly.confetti.utils.DateService +import dev.johnoreilly.confetti.utils.WasmDateService +import org.koin.core.module.dsl.bind +import org.koin.core.module.dsl.singleOf +import org.koin.dsl.module + +@OptIn(ExperimentalSettingsApi::class) +actual fun platformModule() = module { + single { Authentication.Disabled } + single { StorageSettings().toRuntimeObservable().toFlowSettings() } + singleOf(::WasmDateService) { bind() } + single { + FetchPolicy.CacheAndNetwork + } +} + +actual fun getNormalizedCacheFactory(conference: String, uid: String?): NormalizedCacheFactory { + return MemoryCacheFactory(10 * 1024 * 1024) +} \ No newline at end of file diff --git a/shared/src/wasmJsMain/kotlin/dev/johnoreilly/confetti/ui/SessionDetailsViewSharedWrapper.kt b/shared/src/wasmJsMain/kotlin/dev/johnoreilly/confetti/ui/SessionDetailsViewSharedWrapper.kt new file mode 100644 index 000000000..6313e2043 --- /dev/null +++ b/shared/src/wasmJsMain/kotlin/dev/johnoreilly/confetti/ui/SessionDetailsViewSharedWrapper.kt @@ -0,0 +1,11 @@ +package dev.johnoreilly.confetti.dev.johnoreilly.confetti.ui + +import androidx.compose.runtime.Composable +import dev.johnoreilly.confetti.fragment.SessionDetails +import dev.johnoreilly.confetti.ui.SessionDetailViewShared + +@Composable +fun SessionDetailViewSharedWrapper(conference: String, session: SessionDetails?, onSpeakerClick: (speakerId: String) -> Unit, onSocialLinkClicked: (String) -> Unit) { + SessionDetailViewShared(conference, session, onSpeakerClick, onSocialLinkClicked) +} + diff --git a/shared/src/wasmJsMain/kotlin/dev/johnoreilly/confetti/utils/ApolloDebugServer.kt b/shared/src/wasmJsMain/kotlin/dev/johnoreilly/confetti/utils/ApolloDebugServer.kt new file mode 100644 index 000000000..23bc91f38 --- /dev/null +++ b/shared/src/wasmJsMain/kotlin/dev/johnoreilly/confetti/utils/ApolloDebugServer.kt @@ -0,0 +1,12 @@ +package dev.johnoreilly.confetti.utils + +import com.apollographql.apollo3.ApolloClient + +actual fun ApolloClient.registerApolloDebugServer(conference: String) { + // no-op +} + +actual fun ApolloClient.unregisterApolloDebugServer() { + // no-op +} + diff --git a/shared/src/wasmJsMain/kotlin/dev/johnoreilly/confetti/utils/WasmDateService.kt b/shared/src/wasmJsMain/kotlin/dev/johnoreilly/confetti/utils/WasmDateService.kt new file mode 100644 index 000000000..2feac658c --- /dev/null +++ b/shared/src/wasmJsMain/kotlin/dev/johnoreilly/confetti/utils/WasmDateService.kt @@ -0,0 +1,10 @@ +package dev.johnoreilly.confetti.utils + +import kotlinx.datetime.Clock +import kotlinx.datetime.LocalDateTime +import kotlinx.datetime.TimeZone +import kotlinx.datetime.toLocalDateTime + +class WasmDateService: DateService { + override fun now(): LocalDateTime = Clock.System.now().toLocalDateTime(TimeZone.currentSystemDefault()) +} \ No newline at end of file diff --git a/wearApp/build.gradle.kts b/wearApp/build.gradle.kts index 73a76208b..7d6a278c5 100644 --- a/wearApp/build.gradle.kts +++ b/wearApp/build.gradle.kts @@ -10,6 +10,7 @@ plugins { id("com.google.firebase.crashlytics") id("kotlinx-serialization") id("io.github.takahirom.roborazzi") + id("org.jetbrains.compose") } configureCompilerOptions() diff --git a/webApp/src/commonMain/kotlin/App.kt b/webApp/src/commonMain/kotlin/App.kt index 19ca67f8a..42a945858 100644 --- a/webApp/src/commonMain/kotlin/App.kt +++ b/webApp/src/commonMain/kotlin/App.kt @@ -13,7 +13,6 @@ import androidx.compose.ui.unit.dp import com.apollographql.apollo3.ApolloClient import com.apollographql.apollo3.api.ApolloResponse import com.apollographql.apollo3.api.Operation -import com.apollographql.apollo3.exception.ApolloException import confetti.web.GetConferencesQuery import kotlinx.coroutines.flow.Flow