Files
coop-mobile/shared/src/commonMain/kotlin/su/reya/coop/Room.kt
2026-06-08 16:40:56 +07:00

174 lines
5.4 KiB
Kotlin

package su.reya.coop
import kotlinx.datetime.DateTimeUnit
import kotlinx.datetime.TimeZone
import kotlinx.datetime.minus
import kotlinx.datetime.number
import kotlinx.datetime.toLocalDateTime
import rust.nostr.sdk.PublicKey
import rust.nostr.sdk.Timestamp
import rust.nostr.sdk.UnsignedEvent
import kotlin.time.Clock
import kotlin.time.Instant
enum class RoomKind {
Ongoing,
Request;
companion object {
fun default(): RoomKind = Request
}
}
data class Room(
val id: Long,
val createdAt: Timestamp,
val subject: String?,
val members: Set<PublicKey>,
val kind: RoomKind = RoomKind.default(),
val lastMessage: String? = null
) : Comparable<Room> {
override fun compareTo(other: Room): Int {
return this.createdAt.asSecs().compareTo(other.createdAt.asSecs())
}
companion object {
fun new(rumor: UnsignedEvent, userPubkey: PublicKey): Room {
val id = rumor.roomId()
val createdAt = rumor.createdAt()
val subject = rumor.tags().toVec().find { it.kind() == "subject" }?.content()
// Collect the author's public key and all public keys from tags
val pubkeys: MutableSet<PublicKey> = mutableSetOf()
pubkeys.add(rumor.author())
pubkeys.addAll(rumor.tags().publicKeys())
// Also remove the user's public key from the list, current user is always a member
if (pubkeys.size > 1 && pubkeys.contains(userPubkey)) {
pubkeys.remove(userPubkey)
}
// Create a new Room instance
return Room(
id = id,
createdAt = createdAt,
subject = subject,
members = pubkeys as Set<PublicKey>,
lastMessage = rumor.content()
)
}
}
fun setKind(kind: RoomKind): Room {
return this.copy(kind = kind)
}
fun setCreatedAt(createdAt: Timestamp): Room {
return this.copy(createdAt = createdAt)
}
fun setSubject(subject: String?): Room {
return this.copy(subject = subject)
}
fun setLastMessage(message: String?): Room {
return this.copy(lastMessage = message)
}
fun isGroup(): Boolean {
return members.size > 1
}
}
fun UnsignedEvent.roomId(): Long {
// Collect the author's public key and all public keys from tags
val pubkeys: MutableList<PublicKey> = mutableListOf()
pubkeys.add(this.author())
pubkeys.addAll(this.tags().publicKeys())
// Sort and hash the list of public keys
val sortedUniqueKeys = pubkeys
.distinctBy { it.toBech32() }
.sortedBy { it.toBech32() }
return sortedUniqueKeys.hashCode().toLong()
}
fun Timestamp.ago(): String {
val SECONDS_IN_MINUTE = 60L
val MINUTES_IN_HOUR = 60L
val HOURS_IN_DAY = 24L
val DAYS_IN_MONTH = 30L
val inputInstant = Instant.fromEpochSeconds(this.asSecs().toLong())
val now = Clock.System.now()
val duration = now - inputInstant
return when {
duration.inWholeSeconds < SECONDS_IN_MINUTE -> "Now"
duration.inWholeMinutes < MINUTES_IN_HOUR -> "${duration.inWholeMinutes}m"
duration.inWholeHours < HOURS_IN_DAY -> "${duration.inWholeHours}h"
duration.inWholeDays < DAYS_IN_MONTH -> "${duration.inWholeDays}d"
else -> {
val localDateTime = inputInstant.toLocalDateTime(TimeZone.currentSystemDefault())
val month =
localDateTime.month.name.take(3).lowercase().replaceFirstChar { it.uppercase() }
val day = localDateTime.dayOfMonth.toString().padStart(2, '0')
"$month $day"
}
}
}
fun Timestamp.formatAsGroupHeader(): String {
val timeZone = TimeZone.currentSystemDefault()
val inputInstant = Instant.fromEpochSeconds(this.asSecs().toLong())
val inputDate = inputInstant.toLocalDateTime(timeZone).date
val now = Clock.System.now()
val today = now.toLocalDateTime(timeZone).date
val yesterday = today.minus(1, DateTimeUnit.DAY)
return when (inputDate) {
today -> "Today"
yesterday -> "Yesterday"
else -> {
val day = inputDate.day.toString().padStart(2, '0')
val month = inputDate.month.number.toString().padStart(2, '0')
val year = inputDate.year.toString().takeLast(2)
"$day/$month/$year"
}
}
}
fun Timestamp.humanReadable(): String {
val timeZone = TimeZone.currentSystemDefault()
val inputInstant = Instant.fromEpochSeconds(this.asSecs().toLong())
val inputDateTime = inputInstant.toLocalDateTime(timeZone)
val inputDate = inputDateTime.date
val now = Clock.System.now()
val today = now.toLocalDateTime(timeZone).date
val yesterday = today.minus(1, DateTimeUnit.DAY)
val hour = inputDateTime.hour
val minute = inputDateTime.minute.toString().padStart(2, '0')
val amPm = if (hour < 12) "AM" else "PM"
val hour12 = when {
hour == 0 -> 12
hour > 12 -> hour - 12
else -> hour
}
val timeFormat = "$hour12:$minute $amPm"
return when (inputDate) {
today -> "Today at $timeFormat"
yesterday -> "Yesterday at $timeFormat"
else -> {
val day = inputDateTime.day.toString().padStart(2, '0')
val month = inputDateTime.month.number.toString().padStart(2, '0')
val year = inputDateTime.year.toString().takeLast(2)
"$day/$month/$year, $timeFormat"
}
}
}