diff --git a/app/src/main/java/chat/revolt/api/internals/ULID.kt b/app/src/main/java/chat/revolt/api/internals/ULID.kt index a29e5f28..fb3c4b5e 100644 --- a/app/src/main/java/chat/revolt/api/internals/ULID.kt +++ b/app/src/main/java/chat/revolt/api/internals/ULID.kt @@ -88,4 +88,25 @@ object ULID { fun makeNext(): String { return makeSpecial(System.currentTimeMillis(), fetchEntropy()) } + + fun asTimestamp(ulid: String): Long { + if (ulid.length != len) { + throw IllegalArgumentException("ULID must be exactly $len characters") + } + + var timestamp = 0L + + for (i in 0..9) { + val char = ulid[i] + val value = b32chars.indexOf(char) + + if (value == -1) { + throw IllegalArgumentException("Invalid character '$char' at position $i") + } + + timestamp = timestamp or (value.toLong() shl (9 - i) * 5) + } + + return timestamp + } } \ No newline at end of file diff --git a/app/src/main/java/chat/revolt/api/schemas/Channel.kt b/app/src/main/java/chat/revolt/api/schemas/Channel.kt index b878a332..b592d44b 100644 --- a/app/src/main/java/chat/revolt/api/schemas/Channel.kt +++ b/app/src/main/java/chat/revolt/api/schemas/Channel.kt @@ -21,7 +21,7 @@ data class ServerUserChoice( @Serializable data class Member( @SerialName("_id") - val id: ServerUserChoice? = null, + val id: ServerUserChoice, @SerialName("joined_at") val joinedAt: String? = null, diff --git a/app/src/main/java/chat/revolt/components/chat/Message.kt b/app/src/main/java/chat/revolt/components/chat/Message.kt index f62f5856..6a335f45 100644 --- a/app/src/main/java/chat/revolt/components/chat/Message.kt +++ b/app/src/main/java/chat/revolt/components/chat/Message.kt @@ -10,15 +10,19 @@ import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import chat.revolt.api.REVOLT_BASE import chat.revolt.api.REVOLT_FILES import chat.revolt.api.RevoltAPI +import chat.revolt.api.internals.ULID import chat.revolt.api.schemas.AutumnResource import chat.revolt.components.generic.RemoteImage import chat.revolt.api.schemas.Message as MessageSchema @@ -33,6 +37,15 @@ fun viewAttachmentInBrowser(ctx: android.content.Context, attachment: AutumnReso ) } + +fun formatLongAsTime(time: Long): String { + // TODO: look into using a library like kotlinx.datetime + val date = java.util.Date(time) + val format = + java.text.SimpleDateFormat("dd.MM.yyyy HH:mm:ss", java.util.Locale.getDefault()) + return format.format(date) +} + @Composable fun Message( message: MessageSchema @@ -47,6 +60,7 @@ fun Message( modifier = Modifier .size(50.dp) .clip(CircleShape), + crossfade = false, description = "Avatar for ${author.username}" ) } else { @@ -55,17 +69,31 @@ fun Message( modifier = Modifier .size(50.dp) .clip(CircleShape), + crossfade = false, description = "Avatar for ${author.username}" ) } Column(modifier = Modifier.padding(start = 10.dp)) { - author.username?.let { + Row(verticalAlignment = Alignment.CenterVertically) { + author.username?.let { + Text( + text = it, + fontWeight = FontWeight.Bold, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + } + Spacer(modifier = Modifier.width(5.dp)) Text( - text = it, - fontWeight = FontWeight.Bold + text = formatLongAsTime(ULID.asTimestamp(message.id!!)), + fontSize = 12.sp, + color = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.5f), + maxLines = 1, + overflow = TextOverflow.Ellipsis ) } + message.content?.let { Text( text = it @@ -86,6 +114,7 @@ fun Message( width = attachment.metadata.width?.toInt() ?: 0, height = attachment.metadata.height?.toInt() ?: 0, contentScale = ContentScale.Fit, + crossfade = true, description = "Attached image ${attachment.filename}" ) } else {