Skip to content

Commit 4e46b1f

Browse files
committed
wip kotlinx.datetime
1 parent f94c932 commit 4e46b1f

File tree

12 files changed

+212
-191
lines changed

12 files changed

+212
-191
lines changed

shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/APIClient.kt

Lines changed: 23 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,12 @@ import io.ktor.http.isSuccess
2323
import io.ktor.http.path
2424
import io.ktor.http.takeFrom
2525
import io.ktor.serialization.kotlinx.json.json
26-
import io.ktor.util.date.GMTDate
27-
import io.ktor.util.date.Month
2826
import io.ktor.utils.io.core.Closeable
27+
import kotlinx.datetime.LocalDateTime
2928
import org.jetbrains.kotlinconf.utils.appLogger
3029

31-
val HTTP_CLIENT = HttpClient()
30+
private const val MAX_RETRIES = 3
31+
private const val RETRY_DELAY_MS = 10000L
3232

3333
/**
3434
* Adapter to handle backend API and manage auth information.
@@ -38,7 +38,7 @@ class APIClient(
3838
) : Closeable {
3939
var userId: String? = null
4040

41-
private val client = HTTP_CLIENT.config {
41+
private val client = HttpClient {
4242
install(ContentNegotiation) {
4343
json()
4444
}
@@ -55,17 +55,23 @@ class APIClient(
5555
TOO_LATE_STATUS -> throw TooLateVote()
5656
HttpStatusCode.Conflict -> return@validateResponse
5757
HttpStatusCode.Unauthorized -> throw Unauthorized()
58+
HttpStatusCode.BadRequest -> throw IllegalArgumentException("Invalid request parameters")
59+
HttpStatusCode.NotFound -> throw NoSuchElementException("Resource not found")
60+
else -> {
61+
if (!it.status.isSuccess()) {
62+
throw IllegalStateException("Request failed with status: ${it.status}")
63+
}
64+
}
5865
}
5966
}
6067
}
6168

6269
install(HttpRequestRetry) {
63-
maxRetries = Int.MAX_VALUE
64-
delay {
65-
kotlinx.coroutines.delay(it)
66-
}
67-
constantDelay(10 * 1000L)
70+
maxRetries = MAX_RETRIES
71+
retryIf { _, response -> !response.status.isSuccess() }
72+
constantDelay(RETRY_DELAY_MS)
6873
retryOnException(retryOnTimeout = true)
74+
retryOnServerErrors()
6975
}
7076

7177
install(DefaultRequest) {
@@ -136,11 +142,11 @@ class APIClient(
136142
}
137143

138144
/**
139-
* Get server time.
145+
* Get server time in milliseconds since epoch.
140146
*/
141-
suspend fun getServerTime(): GMTDate = client.get {
147+
suspend fun getServerTime(): Long = client.get {
142148
apiUrl("time")
143-
}.bodyAsText().let { response -> GMTDate(response.toLong()) }
149+
}.bodyAsText().toLong()
144150

145151
// TODO real api call https://github.com/JetBrains/kotlinconf-app/issues/268
146152
suspend fun getNews(): List<NewsItem> = EXAMPLE_NEWS_ITEMS
@@ -171,56 +177,35 @@ private val EXAMPLE_NEWS_ITEMS = listOf(
171177
id = "0",
172178
title = "Kotlin 1.9 Released",
173179
content = "**Exciting news for Kotlin developers!** The latest version of Kotlin brings significant improvements and new features to enhance your development experience.\n\nSome highlights include:\n- *K2 compiler* improvements for faster compilation\n- Enhanced type inference system\n- New stdlib functions\n\nCheck out the detailed release notes at [kotlinlang.org](https://kotlinlang.org) and start exploring these amazing features today! The Kotlin team has been working hard to make this release even more **powerful** and *developer-friendly*.",
174-
date = GMTDate(
175-
year = 2024,
176-
month = Month.APRIL,
177-
dayOfMonth = 23,
178-
hours = 10,
179-
minutes = 24,
180-
seconds = 2
181-
),
180+
date = LocalDateTime.parse("2024-04-23T10:24:02"),
182181
photoUrl = "https://picsum.photos/1800/900"
183182
),
184183
NewsItem(
185184
id = "1",
186185
title = "KotlinConf 2024 Announced",
187186
content = "Get ready for the most anticipated Kotlin event of the year! **KotlinConf 2024** brings together developers from around the world for an unforgettable experience.\n\n*What to expect:*\n- Inspiring keynotes from Kotlin leaders\n- In-depth technical sessions\n- Hands-on workshops\n- Networking opportunities\n\nDon't miss the chance to meet fellow Kotlin enthusiasts and learn from industry experts. Early bird tickets are now available at [kotlinconf.com/2024](https://kotlinconf.com/2024).\n\n**Pro tip:** Check out the *conference app* to plan your schedule and connect with other attendees!",
188-
date = GMTDate(year = 2024, month = Month.MAY, dayOfMonth = 22, hours = 10, minutes = 24, seconds = 2),
187+
date = LocalDateTime.parse("2024-05-22T10:24:02"),
189188
photoUrl = null
190189
),
191190
NewsItem(
192191
id = "2",
193192
title = "Jetpack Compose Updates",
194193
content = "The world of **Jetpack Compose** continues to evolve with exciting new features for both Android and Desktop development!\n\n*Latest improvements include:*\n- Enhanced performance optimizations\n- New material design components\n- Improved animation APIs\n- Better desktop window management\n\nRead the comprehensive guide on the [Android Developers Blog](https://android-developers.googleblog.com) and explore the [Compose Multiplatform documentation](https://www.jetbrains.com/compose-multiplatform/).\n\n**Did you know?** You can now easily share up to *90% of your UI code* between Android and Desktop applications using Compose Multiplatform!",
195-
date = GMTDate(
196-
year = 2024,
197-
month = Month.JANUARY,
198-
dayOfMonth = 22,
199-
hours = 10,
200-
minutes = 24,
201-
seconds = 2
202-
),
194+
date = LocalDateTime.parse("2024-01-22T10:24:02"),
203195
photoUrl = null
204196
),
205197
NewsItem(
206198
id = "3",
207199
title = "New Kotlin Multiplatform Features",
208200
content = "**Kotlin Multiplatform** technology reaches new heights with groundbreaking features and improvements!\n\n*Key highlights of the latest release:*\n- Simplified project setup and configuration\n- Enhanced iOS integration with new Kotlin/Native features\n- Improved dependency management\n- Extended WebAssembly support\n\nStart building your next cross-platform project with [KMP](https://kotlinlang.org/docs/multiplatform.html) today!\n\n**Success Story:** *Philips* recently shared how they achieved a **75% code sharing rate** across platforms using Kotlin Multiplatform. Read their detailed case study on the [Kotlin Blog](https://blog.jetbrains.com/kotlin/).\n\nExplore the [official documentation](https://kotlinlang.org/docs/multiplatform-get-started.html) to learn more about these exciting developments!",
209-
date = GMTDate(year = 2024, month = Month.MAY, dayOfMonth = 23, hours = 10, minutes = 24, seconds = 2),
201+
date = LocalDateTime.parse("2024-05-23T10:24:02"),
210202
photoUrl = "https://picsum.photos/1800/900"
211203
),
212204
NewsItem(
213205
id = "4",
214206
title = "Kotlin Community Highlights",
215207
content = "The **Kotlin community** continues to innovate and inspire! Let's celebrate some remarkable community contributions.\n\n*Featured Projects:*\n- **Ktor 2.0**: A powerful framework for building asynchronous servers and clients\n- *Kotlin Native Bridge*: Seamless integration between Kotlin and native platforms\n- **KMP-NativeCoroutines**: Simplified concurrency for multiplatform projects\n\nJoin the community on [Kotlin Slack](https://kotlinlang.slack.com) with over *100,000 members* and share your own projects!\n\n**Want to contribute?** Check out the [Kotlin Contributing Guidelines](https://github.com/JetBrains/kotlin) and help shape the future of Kotlin. The community has already contributed more than *500 patches* to the latest release!\n\nExplore more community projects on [Kotlin Weekly](https://kotlinweekly.net) and get inspired for your next project.",
216-
date = GMTDate(
217-
year = 2024,
218-
month = Month.APRIL,
219-
dayOfMonth = 20,
220-
hours = 10,
221-
minutes = 24,
222-
seconds = 2
223-
),
208+
date = LocalDateTime.parse("2024-04-20T10:24:02"),
224209
photoUrl = "https://picsum.photos/1800/900"
225210
)
226211
)

shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/ConferenceService.kt

Lines changed: 42 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package org.jetbrains.kotlinconf
22

3-
import io.ktor.util.date.GMTDate
43
import kotlinx.coroutines.CoroutineScope
54
import kotlinx.coroutines.Dispatchers
65
import kotlinx.coroutines.SupervisorJob
@@ -14,6 +13,7 @@ import kotlinx.coroutines.flow.flowOn
1413
import kotlinx.coroutines.flow.map
1514
import kotlinx.coroutines.flow.stateIn
1615
import kotlinx.coroutines.launch
16+
import kotlinx.datetime.LocalDateTime
1717
import org.jetbrains.kotlinconf.storage.ApplicationStorage
1818
import org.jetbrains.kotlinconf.utils.time
1919

@@ -22,8 +22,8 @@ val UNKNOWN_SESSION_CARD: SessionCardView = SessionCardView(
2222
title = "unknown",
2323
speakerLine = "unknown",
2424
locationLine = "unknown",
25-
startsAt = GMTDate.START,
26-
endsAt = GMTDate.START,
25+
startsAt = LocalDateTime.parse("2020-01-01T00:00:00"),
26+
endsAt = LocalDateTime.parse("2020-01-01T00:00:00"),
2727
speakerIds = emptyList(),
2828
isFavorite = false,
2929
description = "unknown",
@@ -199,41 +199,41 @@ class ConferenceService(
199199
return PARTNER_DESCRIPTIONS[name] ?: ""
200200
}
201201

202-
private fun scheduleNotification(session: SessionCardView) {
203-
scope.launch {
204-
val notificationsAllowed = storage.getNotificationsAllowed().first()
205-
if (!notificationsAllowed) return@launch
206-
207-
val startTimestamp = session.startsAt.timestamp
208-
val reminderTimestamp = startTimestamp - 5 * 60 * 1000
209-
val nowTimestamp = timeProvider.now().timestamp
210-
val delay = reminderTimestamp - nowTimestamp
211-
val voteTimeStamp = session.endsAt.timestamp
212-
213-
when {
214-
delay >= 0 -> {
215-
notificationManager.schedule(delay, session.title, "Starts in 5 minutes.")
216-
}
217-
218-
nowTimestamp in reminderTimestamp..<startTimestamp -> {
219-
notificationManager.schedule(0, session.title, "The session is about to start.")
220-
}
221-
222-
nowTimestamp in startTimestamp..<voteTimeStamp -> {
223-
notificationManager.schedule(0, session.title, "Hurry up! The session has already started!")
224-
}
225-
}
226-
227-
if (nowTimestamp > voteTimeStamp) return@launch
228-
229-
val voteDelay = voteTimeStamp - nowTimestamp
230-
notificationManager.schedule(
231-
voteDelay,
232-
"${session.title} finished",
233-
"How was the talk?"
234-
)
235-
}
236-
}
202+
// private fun scheduleNotification(session: SessionCardView) {
203+
// scope.launch {
204+
// val notificationsAllowed = storage.getNotificationsAllowed().first()
205+
// if (!notificationsAllowed) return@launch
206+
//
207+
// val startTimestamp = session.startsAt
208+
// val reminderTimestamp = startTimestamp - 5 * 60 * 1000
209+
// val nowTimestamp = timeProvider.now()
210+
// val delay = reminderTimestamp - nowTimestamp
211+
// val voteTimeStamp = session.endsAt
212+
//
213+
// when {
214+
// delay >= 0 -> {
215+
// notificationManager.schedule(delay, session.title, "Starts in 5 minutes.")
216+
// }
217+
//
218+
// nowTimestamp in reminderTimestamp..<startTimestamp -> {
219+
// notificationManager.schedule(0, session.title, "The session is about to start.")
220+
// }
221+
//
222+
// nowTimestamp in startTimestamp..<voteTimeStamp -> {
223+
// notificationManager.schedule(0, session.title, "Hurry up! The session has already started!")
224+
// }
225+
// }
226+
//
227+
// if (nowTimestamp > voteTimeStamp) return@launch
228+
//
229+
// val voteDelay = voteTimeStamp - nowTimestamp
230+
// notificationManager.schedule(
231+
// voteDelay,
232+
// "${session.title} finished",
233+
// "How was the talk?"
234+
// )
235+
// }
236+
// }
237237

238238
private fun cancelNotification(session: SessionCardView) {
239239
scope.launch {
@@ -247,7 +247,7 @@ class ConferenceService(
247247

248248
private fun mapNewsItemToDisplayItem(
249249
item: NewsItem,
250-
now: GMTDate,
250+
now: LocalDateTime,
251251
): NewsDisplayItem {
252252
return NewsDisplayItem(
253253
id = item.id,
@@ -258,13 +258,13 @@ class ConferenceService(
258258
)
259259
}
260260

261-
private fun GMTDate.toNewsDisplayTime(now: GMTDate): String {
261+
private fun LocalDateTime.toNewsDisplayTime(now: LocalDateTime): String {
262262
return if (year == now.year && dayOfYear == now.dayOfYear) {
263263
return time()
264264
} else if (year == now.year) {
265-
"${month.value} $dayOfMonth"
265+
"${month} $dayOfMonth"
266266
} else {
267-
"${month.value} $dayOfMonth, $year"
267+
"${month} $dayOfMonth, $year"
268268
}
269269
}
270270

shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/Model.kt

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
package org.jetbrains.kotlinconf
22

3-
import io.ktor.util.date.*
4-
import kotlinx.serialization.*
5-
import org.jetbrains.kotlinconf.utils.*
3+
import kotlinx.datetime.LocalDateTime
4+
import kotlinx.serialization.Serializable
65
import kotlin.jvm.JvmInline
76

8-
typealias GMTDateSerializable = @Serializable(GMTDateSerializer::class) GMTDate
9-
107
@Serializable
118
@JvmInline
129
value class SpeakerId(val id: String) {
@@ -46,12 +43,10 @@ class Session(
4643
val description: String,
4744
val speakerIds: List<SpeakerId>,
4845
val location: String,
49-
val startsAt: GMTDateSerializable,
50-
val endsAt: GMTDateSerializable,
46+
val startsAt: LocalDateTime,
47+
val endsAt: LocalDateTime,
5148
val tags: List<String>? = null
52-
) {
53-
val timeLine get() = startsAt.time() + " - " + endsAt.time()
54-
}
49+
)
5550

5651
@Serializable
5752
class VoteInfo(
@@ -92,7 +87,7 @@ enum class Theme {
9287
class NewsItem(
9388
val id: String,
9489
val photoUrl: String?,
95-
val date: GMTDateSerializable,
90+
val date: LocalDateTime,
9691
val title: String,
9792
val content: String,
9893
)

shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/SessionCardView.kt

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
package org.jetbrains.kotlinconf
22

3-
import io.ktor.util.date.GMTDate
3+
import kotlinx.datetime.LocalDateTime
4+
import kotlinx.datetime.TimeZone
5+
import kotlinx.datetime.toInstant
46
import org.jetbrains.kotlinconf.utils.dayAndMonth
57
import org.jetbrains.kotlinconf.utils.time
8+
import kotlin.time.Duration.Companion.milliseconds
9+
import kotlin.time.Duration.Companion.minutes
610

711
data class SessionCardView(
812
val id: SessionId,
913
val title: String,
1014
val speakerLine: String,
1115
val locationLine: String,
12-
val startsAt: GMTDate,
13-
val endsAt: GMTDate,
16+
val startsAt: LocalDateTime,
17+
val endsAt: LocalDateTime,
1418
val state: SessionState,
1519
val speakerIds: List<SpeakerId>,
1620
val vote: Score?,
@@ -29,12 +33,15 @@ data class SessionCardView(
2933
append("-")
3034
append(endsAt.time())
3135
},
32-
val isLightning: Boolean = endsAt.timestamp - startsAt.timestamp <= 15 * 60 * 1000,
36+
val isLightning: Boolean = (endsAt.toInstant(TimeZone.UTC).toEpochMilliseconds() - startsAt.toInstant(TimeZone.UTC)
37+
.toEpochMilliseconds()).milliseconds <= 15.minutes,
3338
val startsInMinutes: Int?,
3439
)
3540

3641
val SessionCardView.isLive get() = state == SessionState.Live
3742
val SessionCardView.isUpcoming get() = state == SessionState.Upcoming
3843
val SessionCardView.isPast get() = state == SessionState.Past
3944

40-
val Session.isLightning: Boolean get() = endsAt.timestamp - startsAt.timestamp <= 15 * 60 * 1000
45+
val Session.isLightning: Boolean
46+
get() = (endsAt.toInstant(TimeZone.UTC).toEpochMilliseconds() - startsAt.toInstant(TimeZone.UTC)
47+
.toEpochMilliseconds()).milliseconds <= 15.minutes

shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/SessionState.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package org.jetbrains.kotlinconf
22

3-
import io.ktor.util.date.GMTDate
3+
import kotlinx.datetime.LocalDateTime
44

55
enum class SessionState {
66
Live,
@@ -9,7 +9,7 @@ enum class SessionState {
99
;
1010

1111
companion object {
12-
fun from(startsAt: GMTDate, endsAt: GMTDate, now: GMTDate): SessionState = when {
12+
fun from(startsAt: LocalDateTime, endsAt: LocalDateTime, now: LocalDateTime): SessionState = when {
1313
startsAt <= now && now < endsAt -> Live
1414
endsAt <= now -> Past
1515
else -> Upcoming

0 commit comments

Comments
 (0)