Skip to content

Commit cece580

Browse files
committed
Address review comments
1 parent f0ff74e commit cece580

File tree

7 files changed

+93
-0
lines changed

7 files changed

+93
-0
lines changed

README.MD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ Kotlin Multiplatform library for network time synchronization. It extends the [`
77
- Android
88
- iOS
99
- Desktop JVM (MacOS, Linux, Windows)
10+
- Web
1011
## Usage
1112
### `kotlin.time` Extension
1213
The library [extends the main `Clock` interface](https://github.com/softartdev/Kronos-Multiplatform/blob/main/kronos/src/commonMain/kotlin/com/softartdev/kronos/ClockExt.kt) from the standard library. You can use the [`Clock.Network`](https://github.com/softartdev/Kronos-Multiplatform/blob/main/kronos/src/commonMain/kotlin/com/softartdev/kronos/NetworkClock.kt) class to retrieve the current network time, similar to using the built-in [`Clock.System`](https://github.com/JetBrains/kotlin/blob/master/libraries/stdlib/src/kotlin/time/Clock.kt#L60) instance.

kronos/build.gradle.kts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ version = libs.versions.kronos.get()
1010
kotlin {
1111
jvmToolchain(libs.versions.jdk.get().toInt())
1212
jvm()
13+
wasmJs()
1314
androidTarget {
1415
publishLibraryVariants("release", "debug")
1516
}
@@ -53,6 +54,8 @@ kotlin {
5354
implementation(libs.androidx.test)
5455
}
5556
}
57+
val wasmJsMain by getting
58+
val wasmJsTest by getting
5659
val iosX64Main by getting
5760
val iosArm64Main by getting
5861
val iosSimulatorArm64Main by getting
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
@file:OptIn(ExperimentalTime::class)
2+
3+
package com.softartdev.kronos
4+
5+
import kotlin.time.Clock
6+
import kotlin.time.ExperimentalTime
7+
8+
actual val Clock.Companion.Network: NetworkClock
9+
get() = WasmNetworkClock
10+
11+
fun NetworkClock.sync() = WasmNetworkClock.sync()
12+
fun NetworkClock.blockingSync(): Boolean = WasmNetworkClock.blockingSync()
13+
suspend fun NetworkClock.awaitSync(): Boolean = WasmNetworkClock.awaitSync()
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package com.softartdev.kronos
2+
3+
import kotlinx.browser.window
4+
import kotlinx.coroutines.GlobalScope
5+
import kotlinx.coroutines.await
6+
import kotlinx.coroutines.launch
7+
import kotlinx.coroutines.runBlocking
8+
import kotlin.time.Clock
9+
10+
/**
11+
* Fetches network time using the public WorldTime API and stores the offset
12+
* from the local system clock. The [sync] functions update the offset
13+
* asynchronously or in a blocking fashion. After synchronization the clock
14+
* provides network based timestamps via [getCurrentNtpTimeMs].
15+
*/
16+
object WasmNetworkClock : NetworkClock {
17+
18+
private var offsetMs: Long? = null
19+
20+
fun sync() {
21+
GlobalScope.launch { awaitSync() }
22+
}
23+
24+
fun blockingSync(): Boolean = runBlocking { awaitSync() }
25+
26+
suspend fun awaitSync(): Boolean = try {
27+
val response = window.fetch("https://worldtimeapi.org/api/ip").await()
28+
val json = response.json().await() as dynamic
29+
val dateStr = json.datetime as String
30+
val networkTimeMs = js("Date.parse(dateStr)").unsafeCast<Double>().toLong()
31+
val systemTimeMs = js("Date.now()").unsafeCast<Double>().toLong()
32+
offsetMs = networkTimeMs - systemTimeMs
33+
true
34+
} catch (t: Throwable) {
35+
console.error("Failed to sync time", t)
36+
false
37+
}
38+
39+
override fun getCurrentNtpTimeMs(): Long? {
40+
val offset = offsetMs ?: return null
41+
val systemTimeMs = js("Date.now()").unsafeCast<Double>().toLong()
42+
return systemTimeMs + offset
43+
}
44+
}

sampleApp/build.gradle.kts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ kotlin {
1616
jvmToolchain(libs.versions.jdk.get().toInt())
1717
jvm("desktop")
1818
androidTarget()
19+
wasmJs()
1920
iosX64()
2021
iosArm64()
2122
iosSimulatorArm64()
@@ -62,6 +63,8 @@ kotlin {
6263
implementation(compose.preview)
6364
}
6465
}
66+
val wasmJsMain by getting
67+
val wasmJsTest by getting
6568
val iosX64Main by getting
6669
val iosArm64Main by getting
6770
val iosSimulatorArm64Main by getting
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
@file:OptIn(ExperimentalTime::class)
2+
3+
package com.softartdev.kronos.sample
4+
5+
import kotlinx.browser.window
6+
import kotlin.time.Clock
7+
import kotlin.time.ExperimentalTime
8+
9+
internal actual fun openUrl(url: String?) {
10+
url ?: return
11+
window.open(url, "_blank")
12+
}
13+
14+
internal actual fun clickSync() = Clock.Network.sync()
15+
16+
internal actual fun clickBlockingSync() {
17+
Clock.Network.blockingSync()
18+
}
19+
20+
internal actual suspend fun clickAwaitSync() {
21+
Clock.Network.awaitSync()
22+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.softartdev.kronos.sample
2+
3+
class WasmJsPlatform : Platform {
4+
override val name: String = "Web"
5+
}
6+
7+
actual fun getPlatform(): Platform = WasmJsPlatform()

0 commit comments

Comments
 (0)