feat: add status functionality to status sheet
Signed-off-by: Infi <infi@infi.sh>
This commit is contained in:
parent
717e9f7890
commit
f942218b6a
|
|
@ -5,10 +5,18 @@ import chat.revolt.api.RevoltError
|
||||||
import chat.revolt.api.RevoltHttp
|
import chat.revolt.api.RevoltHttp
|
||||||
import chat.revolt.api.RevoltJson
|
import chat.revolt.api.RevoltJson
|
||||||
import chat.revolt.api.schemas.Profile
|
import chat.revolt.api.schemas.Profile
|
||||||
|
import chat.revolt.api.schemas.Status
|
||||||
import chat.revolt.api.schemas.User
|
import chat.revolt.api.schemas.User
|
||||||
import io.ktor.client.request.get
|
import io.ktor.client.request.get
|
||||||
|
import io.ktor.client.request.patch
|
||||||
|
import io.ktor.client.request.setBody
|
||||||
import io.ktor.client.statement.bodyAsText
|
import io.ktor.client.statement.bodyAsText
|
||||||
|
import io.ktor.http.ContentType
|
||||||
|
import io.ktor.http.contentType
|
||||||
import kotlinx.serialization.SerializationException
|
import kotlinx.serialization.SerializationException
|
||||||
|
import kotlinx.serialization.builtins.MapSerializer
|
||||||
|
import kotlinx.serialization.builtins.serializer
|
||||||
|
import kotlinx.serialization.json.JsonElement
|
||||||
|
|
||||||
suspend fun fetchSelf(): User {
|
suspend fun fetchSelf(): User {
|
||||||
val response = RevoltHttp.get("/users/@me")
|
val response = RevoltHttp.get("/users/@me")
|
||||||
|
|
@ -33,6 +41,41 @@ suspend fun fetchSelf(): User {
|
||||||
return user
|
return user
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun patchSelf(
|
||||||
|
status: Status? = null,
|
||||||
|
pure: Boolean = false,
|
||||||
|
) {
|
||||||
|
val body = mutableMapOf<String, JsonElement>()
|
||||||
|
if (status != null) {
|
||||||
|
body["status"] = RevoltJson.encodeToJsonElement(Status.serializer(), status)
|
||||||
|
}
|
||||||
|
|
||||||
|
val response = RevoltHttp.patch("/users/@me") {
|
||||||
|
contentType(ContentType.Application.Json)
|
||||||
|
setBody(
|
||||||
|
RevoltJson.encodeToString(
|
||||||
|
MapSerializer(
|
||||||
|
String.serializer(),
|
||||||
|
JsonElement.serializer()
|
||||||
|
), body
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.bodyAsText()
|
||||||
|
|
||||||
|
if (RevoltAPI.selfId == null) {
|
||||||
|
throw Error("Self ID is null")
|
||||||
|
}
|
||||||
|
|
||||||
|
val currentUser = RevoltAPI.userCache[RevoltAPI.selfId] ?: fetchSelf()
|
||||||
|
val newUserKeys = RevoltJson.decodeFromString(User.serializer(), response)
|
||||||
|
val mergedUser = currentUser.mergeWithPartial(newUserKeys)
|
||||||
|
|
||||||
|
if (!pure) {
|
||||||
|
RevoltAPI.userCache[RevoltAPI.selfId!!] = mergedUser
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun fetchUser(id: String): User {
|
suspend fun fetchUser(id: String): User {
|
||||||
val res = RevoltHttp.get("/users/$id")
|
val res = RevoltHttp.get("/users/$id")
|
||||||
|
|
||||||
|
|
@ -81,4 +124,4 @@ suspend fun fetchUserProfile(id: String): Profile {
|
||||||
}
|
}
|
||||||
|
|
||||||
return RevoltJson.decodeFromString(Profile.serializer(), response)
|
return RevoltJson.decodeFromString(Profile.serializer(), response)
|
||||||
}
|
}
|
||||||
|
|
@ -45,13 +45,23 @@ fun presenceFromStatus(status: String?, online: Boolean = true): Presence {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Presence.asApiName(): String {
|
||||||
|
return when (this) {
|
||||||
|
Presence.Online -> "Online"
|
||||||
|
Presence.Idle -> "Idle"
|
||||||
|
Presence.Dnd -> "Busy"
|
||||||
|
Presence.Focus -> "Focus"
|
||||||
|
Presence.Offline -> "Invisible"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun presenceColour(presence: Presence): Color {
|
fun presenceColour(presence: Presence): Color {
|
||||||
return when (presence) {
|
return when (presence) {
|
||||||
Presence.Online -> Color(0xFF00C853)
|
Presence.Online -> Color(0xFF3ABF7E)
|
||||||
Presence.Idle -> Color(0xFFFFD600)
|
Presence.Idle -> Color(0xFFF39F00)
|
||||||
Presence.Dnd -> Color(0xFFD50000)
|
Presence.Dnd -> Color(0xFFF84848)
|
||||||
Presence.Focus -> Color(0xFF0091EA)
|
Presence.Focus -> Color(0xFF4799F0)
|
||||||
Presence.Offline -> Color(0xff546e7a)
|
Presence.Offline -> Color(0xFFA5A5A5)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,99 @@
|
||||||
|
package chat.revolt.components.settings.profile
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.surfaceColorAtElevation
|
||||||
|
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.res.stringResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import chat.revolt.R
|
||||||
|
import chat.revolt.components.generic.Presence
|
||||||
|
import chat.revolt.components.generic.presenceColour
|
||||||
|
|
||||||
|
fun Presence.stringResource(): Int {
|
||||||
|
return when (this) {
|
||||||
|
Presence.Online -> R.string.status_online
|
||||||
|
Presence.Idle -> R.string.status_idle
|
||||||
|
Presence.Dnd -> R.string.status_dnd
|
||||||
|
Presence.Focus -> R.string.status_focus
|
||||||
|
Presence.Offline -> R.string.status_invisible
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun StatusPicker(
|
||||||
|
currentStatus: Presence,
|
||||||
|
onStatusChange: (Presence) -> Unit,
|
||||||
|
modifier: Modifier = Modifier
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
modifier = modifier
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.status),
|
||||||
|
style = MaterialTheme.typography.labelLarge
|
||||||
|
)
|
||||||
|
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
modifier = Modifier
|
||||||
|
.clip(MaterialTheme.shapes.small)
|
||||||
|
.background(MaterialTheme.colorScheme.surfaceColorAtElevation(2.dp))
|
||||||
|
) {
|
||||||
|
Presence.entries.forEach {
|
||||||
|
StatusButton(
|
||||||
|
presence = it,
|
||||||
|
selected = it == currentStatus,
|
||||||
|
onClick = onStatusChange,
|
||||||
|
modifier = modifier
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = stringResource(currentStatus.stringResource()),
|
||||||
|
style = MaterialTheme.typography.bodyLarge
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun StatusButton(
|
||||||
|
presence: Presence,
|
||||||
|
selected: Boolean,
|
||||||
|
onClick: (Presence) -> Unit,
|
||||||
|
modifier: Modifier = Modifier
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = modifier
|
||||||
|
.size(48.dp)
|
||||||
|
.clickable { onClick(presence) }
|
||||||
|
.then(
|
||||||
|
if (selected) Modifier.background(
|
||||||
|
MaterialTheme.colorScheme.surfaceColorAtElevation(6.dp)
|
||||||
|
) else Modifier
|
||||||
|
),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.clip(CircleShape)
|
||||||
|
.size(24.dp)
|
||||||
|
.background(presenceColour(presence))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,11 +1,9 @@
|
||||||
package chat.revolt.sheets
|
package chat.revolt.sheets
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.width
|
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
|
|
@ -13,60 +11,45 @@ import androidx.compose.material.icons.filled.Settings
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.AnnotatedString
|
|
||||||
import androidx.compose.ui.text.SpanStyle
|
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import chat.revolt.R
|
import chat.revolt.R
|
||||||
import chat.revolt.api.RevoltAPI
|
import chat.revolt.api.RevoltAPI
|
||||||
import chat.revolt.api.internals.ULID
|
import chat.revolt.api.routes.user.patchSelf
|
||||||
import chat.revolt.components.generic.SheetClickable
|
import chat.revolt.components.generic.SheetClickable
|
||||||
import chat.revolt.components.generic.UserAvatar
|
import chat.revolt.components.generic.asApiName
|
||||||
import chat.revolt.components.generic.presenceFromStatus
|
import chat.revolt.components.generic.presenceFromStatus
|
||||||
|
import chat.revolt.components.screens.settings.UserOverview
|
||||||
|
import chat.revolt.components.settings.profile.StatusPicker
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun StatusSheet(onBeforeNavigation: () -> Unit, onGoSettings: () -> Unit) {
|
fun StatusSheet(onBeforeNavigation: () -> Unit, onGoSettings: () -> Unit) {
|
||||||
val selfUser = RevoltAPI.userCache[RevoltAPI.selfId]!!
|
val selfUser = RevoltAPI.userCache[RevoltAPI.selfId]!!
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(horizontal = 16.dp, vertical = 8.dp)
|
.padding(horizontal = 16.dp, vertical = 8.dp)
|
||||||
.verticalScroll(rememberScrollState())
|
.verticalScroll(rememberScrollState())
|
||||||
) {
|
) {
|
||||||
Row(
|
UserOverview(selfUser)
|
||||||
verticalAlignment = Alignment.CenterVertically
|
|
||||||
) {
|
|
||||||
UserAvatar(
|
|
||||||
username = selfUser.displayName ?: stringResource(id = R.string.unknown),
|
|
||||||
userId = selfUser.id ?: ULID.makeSpecial(0),
|
|
||||||
avatar = selfUser.avatar,
|
|
||||||
size = 48.dp,
|
|
||||||
presence = presenceFromStatus(
|
|
||||||
selfUser.status?.presence,
|
|
||||||
selfUser.online ?: false
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
Spacer(modifier = Modifier.width(12.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
Text(
|
StatusPicker(
|
||||||
text = AnnotatedString.Builder().apply {
|
currentStatus = presenceFromStatus(selfUser.status?.presence, selfUser.online ?: false),
|
||||||
if (selfUser.displayName != null) {
|
onStatusChange = {
|
||||||
pushStyle(SpanStyle(fontWeight = FontWeight.Bold))
|
onBeforeNavigation()
|
||||||
append(selfUser.displayName)
|
scope.launch {
|
||||||
pop()
|
patchSelf(status = selfUser.status?.copy(presence = it.asApiName()))
|
||||||
append("\n")
|
}
|
||||||
}
|
}
|
||||||
append("${selfUser.username}")
|
)
|
||||||
pushStyle(SpanStyle(fontWeight = FontWeight.ExtraLight))
|
|
||||||
append("#${selfUser.discriminator}")
|
|
||||||
pop()
|
|
||||||
}.toAnnotatedString()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -181,6 +181,7 @@
|
||||||
<string name="reconnecting">Reconnecting…</string>
|
<string name="reconnecting">Reconnecting…</string>
|
||||||
<string name="reconnected">Reconnected</string>
|
<string name="reconnected">Reconnected</string>
|
||||||
|
|
||||||
|
<string name="status">Status</string>
|
||||||
<string name="status_online">Online</string>
|
<string name="status_online">Online</string>
|
||||||
<string name="status_idle">Idle</string>
|
<string name="status_idle">Idle</string>
|
||||||
<string name="status_focus">Focus</string>
|
<string name="status_focus">Focus</string>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue