Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion kotlin-js-store/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1705,7 +1705,7 @@ source-map-loader@5.0.0:
iconv-lite "^0.6.3"
source-map-js "^1.0.2"

source-map-support@~0.5.20:
source-map-support@0.5.21, source-map-support@~0.5.20:
version "0.5.21"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f"
integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==
Expand Down
11 changes: 11 additions & 0 deletions molecule-runtime/api/molecule-runtime.klib.api
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,14 @@ final val app.cash.molecule/app_cash_molecule_WindowAnimationFrameClock$stablepr

// Targets: [js, wasmJs]
final fun app.cash.molecule/app_cash_molecule_WindowAnimationFrameClock$stableprop_getter(): kotlin/Int // app.cash.molecule/app_cash_molecule_WindowAnimationFrameClock$stableprop_getter|app_cash_molecule_WindowAnimationFrameClock$stableprop_getter(){}[0]

// Targets: [js]
final object app.cash.molecule/NodeJsFrameClock : androidx.compose.runtime/MonotonicFrameClock { // app.cash.molecule/NodeJsFrameClock|null[0]
final suspend fun <#A1: kotlin/Any?> withFrameNanos(kotlin/Function1<kotlin/Long, #A1>): #A1 // app.cash.molecule/NodeJsFrameClock.withFrameNanos|withFrameNanos(kotlin.Function1<kotlin.Long,0:0>){0§<kotlin.Any?>}[0]
}

// Targets: [js]
final val app.cash.molecule/app_cash_molecule_NodeJsFrameClock$stableprop // app.cash.molecule/app_cash_molecule_NodeJsFrameClock$stableprop|#static{}app_cash_molecule_NodeJsFrameClock$stableprop[0]

// Targets: [js]
final fun app.cash.molecule/app_cash_molecule_NodeJsFrameClock$stableprop_getter(): kotlin/Int // app.cash.molecule/app_cash_molecule_NodeJsFrameClock$stableprop_getter|app_cash_molecule_NodeJsFrameClock$stableprop_getter(){}[0]
1 change: 1 addition & 0 deletions molecule-runtime/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ kotlin {

js {
browser()
nodejs()
}

jvm()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright (C) 2024 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package app.cash.molecule

import androidx.compose.runtime.MonotonicFrameClock
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import kotlin.coroutines.suspendCoroutine

public object NodeJsFrameClock : MonotonicFrameClock {
private var lastNanos: Long = 0L

override suspend fun <R> withFrameNanos(
onFrame: (Long) -> R,
): R = suspendCoroutine { continuation ->
setImmediate {
val now = nanoTime()
val frameTimeNanos = if (now <= lastNanos) {
lastNanos + 1L
} else {
now
}
lastNanos = frameTimeNanos
try {
val result = onFrame(frameTimeNanos)
continuation.resume(result)
} catch (t: Throwable) {
continuation.resumeWithException(t)
}
}
}
}

private external fun setImmediate(callback: () -> Unit)
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright (C) 2024 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package app.cash.molecule

import assertk.all
import assertk.assertThat
import assertk.assertions.isLessThan
import assertk.assertions.isPositive
import kotlin.test.Test
import kotlinx.coroutines.test.runTest

class NodeJsFrameClockTest {
@Test fun ticksWithTime() = runTest {
if (js("typeof window !== 'undefined'")) {
return@runTest
}
val frameTimeA = NodeJsFrameClock.withFrameNanos { it }
val frameTimeB = NodeJsFrameClock.withFrameNanos { it }
assertThat(frameTimeA).all {
isPositive()
isLessThan(frameTimeB)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ import kotlinx.coroutines.test.runTest

class WindowAnimationFrameClockTest {
@Test fun ticksWithTime() = runTest {
if (js("typeof window === 'undefined'")) {
return@runTest
}
val frameTimeA = WindowAnimationFrameClock.withFrameNanos { it }
val frameTimeB = WindowAnimationFrameClock.withFrameNanos { it }
assertThat(frameTimeA).all {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ internal external interface Window {
fun requestAnimationFrame(callback: (Double) -> Unit)
}

internal external val performance: Performance

internal external interface Performance {
fun now(): Double
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@
package app.cash.molecule

@Suppress("NOTHING_TO_INLINE")
internal actual inline fun nanoTime(): Long = window.performance.now().toLong() * 1_000_000
internal actual inline fun nanoTime(): Long = (performance.now() * 1_000_000).toLong()
Loading