Skip to content

Commit

Permalink
Merge pull request #31 from mike-n-jordan/web-target
Browse files Browse the repository at this point in the history
Web target
  • Loading branch information
aashay-gaikwad authored Oct 24, 2023
2 parents 1796d1b + 4a62ad6 commit f4c90eb
Show file tree
Hide file tree
Showing 17 changed files with 284 additions and 18 deletions.
4 changes: 0 additions & 4 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,3 @@ plugins {
id("org.jetbrains.compose") version libs.versions.composePlugin.get() apply false
id("com.google.devtools.ksp") version libs.versions.ksp.get() apply false
}

tasks.register("clean", Delete::class) {
delete(rootProject.buildDir)
}
1 change: 1 addition & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ kotlin.mpp.androidSourceSetLayoutVersion=2

#Compose
org.jetbrains.compose.experimental.uikit.enabled=true
org.jetbrains.compose.experimental.jscanvas.enabled=true
1 change: 1 addition & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,5 @@ rootProject.name = "puzzyx"

include(":androidApp")
include(":desktopApp")
include(":webApp")
include(":shared")
7 changes: 7 additions & 0 deletions shared/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ kotlin {
}
}

js(IR) {
// Adding moduleName as a workaround for this issue: https://youtrack.jetbrains.com/issue/KT-51942
moduleName = "puzzyx-common"
browser()
}

sourceSets {
val commonMain by getting {
dependencies {
Expand Down Expand Up @@ -57,4 +63,5 @@ dependencies {
add("kspCommonMainMetadata", libs.appyx.mutable.ui.processor)
add("kspAndroid", libs.appyx.mutable.ui.processor)
add("kspDesktop", libs.appyx.mutable.ui.processor)
add("kspJs", libs.appyx.mutable.ui.processor)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.bumble.puzzyx.imageloader

import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale

@Composable
actual fun EmbeddableResourceImage(
path: String,
modifier: Modifier,
contentDescription: String?,
contentScale: ContentScale
) {
ResourceImage(
path = path,
modifier = modifier,
contentScale = contentScale,
contentDescription = contentDescription,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ class ClipShapeProgress(
coroutineScope: CoroutineScope,
target: Target,
displacement: StateFlow<Float> = MutableStateFlow(0f),
private val shape: @Composable (progress: Float) -> Shape = { RectangleShape },
// web-target doesn't like a lambda here for some reason
private val shape: @Composable ((progress: Float) -> Shape)? = null,
) : MotionProperty<Float, AnimationVector1D>(
coroutineScope = coroutineScope,
animatable = Animatable(target.value),
Expand All @@ -47,10 +48,9 @@ class ClipShapeProgress(
get() = Modifier.composed {
val progress = renderValueFlow.collectAsState().value
if (progress == 0f) this
else this.clip(shape.invoke(progress))
else this.clip(shape?.invoke(progress) ?: RectangleShape)
}


override suspend fun lerpTo(start: Target, end: Target, fraction: Float) {
snapTo(
lerpFloat(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.bumble.puzzyx.imageloader.ResourceImage
import com.bumble.puzzyx.imageloader.EmbeddableResourceImage
import com.bumble.puzzyx.model.Entry
import com.bumble.puzzyx.ui.colors

Expand All @@ -33,7 +33,7 @@ fun EntryCard(
) {
when (entry) {
is Entry.Text -> TextEntry(entry)
is Entry.Image -> ResourceImage(
is Entry.Image -> EmbeddableResourceImage(
path = "participant/${entry.path}",
contentDescription = entry.contentDescription,
contentScale = entry.contentScale,
Expand All @@ -58,7 +58,7 @@ fun GitHubHeader(
verticalAlignment = Alignment.CenterVertically,
modifier = modifier
) {
ResourceImage(
EmbeddableResourceImage(
path = "github.png",
contentScale = ContentScale.Inside,
modifier = Modifier
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,45 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.layout.ContentScale
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.jetbrains.compose.resources.ExperimentalResourceApi
import org.jetbrains.compose.resources.resource

@Composable
expect fun EmbeddableResourceImage(
path: String,
modifier: Modifier = Modifier,
contentDescription: String? = null,
contentScale: ContentScale = ContentScale.Fit,
)

@OptIn(ExperimentalResourceApi::class)
@Composable
fun ResourceImage(
internal fun ResourceImage(
path: String,
fallbackUrl: String = path,
modifier: Modifier = Modifier,
contentDescription: String? = null,
contentScale: ContentScale = ContentScale.Fit
) {
var image: ImageBitmap? by remember { mutableStateOf(null) }
LaunchedEffect(Unit) {
image =
resource(path)
.readBytes()
.toImageBitmap()
image = withContext(Dispatchers.Default) {
try {
resource(path)
.readBytes()
.toImageBitmap()
} catch (e: Throwable) {
try {
resource(fallbackUrl)
.readBytes()
.toImageBitmap()
} catch (e: Throwable) {
null
}
}
}
}
image?.let {
Image(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import com.bumble.puzzyx.appyx.component.gridpuzzle.operation.scatter
import com.bumble.puzzyx.composable.AutoPlayScript
import com.bumble.puzzyx.composable.EntryCardSmall
import com.bumble.puzzyx.composable.FlashCard
import com.bumble.puzzyx.imageloader.ResourceImage
import com.bumble.puzzyx.imageloader.EmbeddableResourceImage
import com.bumble.puzzyx.model.Entry
import com.bumble.puzzyx.model.Puzzle
import com.bumble.puzzyx.model.PuzzlePiece
Expand Down Expand Up @@ -90,7 +90,7 @@ class Puzzle1Node(
FlashCard(
flash = Color.White,
front = { modifier ->
ResourceImage(
EmbeddableResourceImage(
path = "${puzzle.imagesDir}/slice_${puzzlePiece.j}_${puzzlePiece.i}.png",
contentScale = ContentScale.FillBounds,
modifier = modifier
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import androidx.compose.ui.unit.LayoutDirection
import com.bumble.appyx.interactions.core.annotations.FloatRange
import com.bumble.appyx.interactions.core.ui.math.lerpFloat
import com.bumble.puzzyx.math.mapValueRange
import java.lang.Integer.max
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.sqrt

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.bumble.puzzyx.imageloader

import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale

@Composable
actual fun EmbeddableResourceImage(
path: String,
modifier: Modifier,
contentDescription: String?,
contentScale: ContentScale
) {
ResourceImage(
path = path,
modifier = modifier,
contentScale = contentScale,
contentDescription = contentDescription,
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.bumble.puzzyx.imageloader

import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale

private const val EMBED_URL = "appyx/where/hosted/sample"

@Composable
actual fun EmbeddableResourceImage(
path: String,
modifier: Modifier,
contentDescription: String?,
contentScale: ContentScale
) {
ResourceImage(
path = EMBED_URL + path,
fallbackUrl = path,
modifier = modifier,
contentScale = contentScale,
contentDescription = contentDescription,
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.bumble.puzzyx.imageloader

import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.toComposeImageBitmap

actual fun ByteArray.toImageBitmap(): ImageBitmap {
return org.jetbrains.skia.Image.makeFromEncoded(this).toComposeImageBitmap()
}
48 changes: 48 additions & 0 deletions webApp/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import org.jetbrains.compose.desktop.application.dsl.TargetFormat

plugins {
kotlin("multiplatform")
id("org.jetbrains.compose")
}

kotlin {
js(IR) {
moduleName = "puzzyx-web"
browser()
binaries.executable()
}
sourceSets {
val commonMain by getting {
dependencies {
implementation(compose.runtime)
implementation(compose.foundation)
implementation(compose.material3)
implementation(project(":shared"))
implementation(libs.appyx.navigation)
implementation(libs.appyx.components.backstack)
}
}
}
}

compose.experimental {
web.application {}
}

tasks.register<Copy>("copyResources") {
// Dirs containing files we want to copy
from("../shared/src/commonMain/resources")

// Output for web resources
into("$buildDir/processedResources/js/main")

include("**/*")
}

tasks.named("jsBrowserProductionExecutableDistributeResources") {
dependsOn("copyResources")
}

tasks.named("compileKotlinJs") {
dependsOn("copyResources")
}
93 changes: 93 additions & 0 deletions webApp/src/jsMain/kotlin/Main.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import androidx.compose.foundation.focusable
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.Surface
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.input.key.Key
import androidx.compose.ui.input.key.KeyEvent
import androidx.compose.ui.input.key.KeyEventType
import androidx.compose.ui.input.key.key
import androidx.compose.ui.input.key.onKeyEvent
import androidx.compose.ui.input.key.type
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.unit.dp
import com.bumble.appyx.navigation.integration.ScreenSize
import com.bumble.appyx.navigation.integration.WebNodeHost
import com.bumble.puzzyx.node.app.PuzzyxAppNode
import com.bumble.puzzyx.ui.PuzzyxTheme
import com.bumble.puzzyx.ui.appyx_dark
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.launch
import org.jetbrains.skiko.wasm.onWasmReady

fun main() {
val events: Channel<Unit> = Channel()
onWasmReady {
BrowserViewportWindow() {
PuzzyxTheme {
val requester = remember { FocusRequester() }
var hasFocus by remember { mutableStateOf(false) }

var screenSize by remember { mutableStateOf(ScreenSize(0.dp, 0.dp)) }
val eventScope = remember { CoroutineScope(SupervisorJob() + Dispatchers.Main) }

Surface(
modifier = Modifier
.fillMaxSize()
.onSizeChanged { screenSize = ScreenSize(it.width.dp, it.height.dp) }
.onKeyEvent { event ->
onKeyEvent(event, events, eventScope)
}
.focusRequester(requester)
.focusable()
.onFocusChanged { hasFocus = it.hasFocus },
color = appyx_dark,
) {
WebNodeHost(
screenSize = screenSize,
onBackPressedEvents = events.receiveAsFlow(),
) { buildContext ->
PuzzyxAppNode(
buildContext = buildContext,
)
}
}


if (!hasFocus) {
LaunchedEffect(Unit) {
requester.requestFocus()
}
}
}
}
}
}


@OptIn(ExperimentalComposeUiApi::class)
private fun onKeyEvent(
keyEvent: KeyEvent,
events: Channel<Unit>,
coroutineScope: CoroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main),
): Boolean =
when {
keyEvent.type == KeyEventType.KeyUp && keyEvent.key == Key.Backspace -> {
coroutineScope.launch { events.send(Unit) }
true
}

else -> false
}
15 changes: 15 additions & 0 deletions webApp/src/jsMain/resources/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Puzzyx</title>
<script src="skiko.js"></script>
<link type="text/css" rel="stylesheet" href="styles.css"/>
</head>
<body>
<div>
<canvas id="ComposeTarget" width="512" height="512"></canvas>
</div>
<script src="webApp.js"></script>
</body>
</html>
Loading

0 comments on commit f4c90eb

Please sign in to comment.