feat: basic member fetching in servers, roles in sheet
Signed-off-by: Infi <wingit@geist.ga>
This commit is contained in:
parent
6e179d3b49
commit
4f0845bf46
|
|
@ -92,7 +92,6 @@ val mainHandler = Handler(Looper.getMainLooper())
|
|||
object RevoltAPI {
|
||||
const val TOKEN_HEADER_NAME = "x-session-token"
|
||||
|
||||
// FIXME discount caching solutions! LRU would be better but this is fine for now
|
||||
val userCache = mutableStateMapOf<String, User>()
|
||||
val serverCache = mutableStateMapOf<String, Server>()
|
||||
val channelCache = mutableStateMapOf<String, ChannelSchema>()
|
||||
|
|
|
|||
|
|
@ -2,14 +2,23 @@ package chat.revolt.api.internals
|
|||
|
||||
import chat.revolt.api.schemas.Member
|
||||
|
||||
@RequiresOptIn("Dummy API, does nothing or returns null.")
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
|
||||
annotation class RvxDummyMemberAPI
|
||||
|
||||
object Members {
|
||||
@RvxDummyMemberAPI
|
||||
// memberCache (mapping of serverId to userId to member)
|
||||
private val memberCache = mutableMapOf<String, MutableMap<String, Member>>()
|
||||
|
||||
fun getMember(serverId: String, userId: String): Member? {
|
||||
return null
|
||||
return memberCache[serverId]?.get(userId)
|
||||
}
|
||||
|
||||
fun hasMember(serverId: String, userId: String): Boolean {
|
||||
return memberCache[serverId]?.containsKey(userId) ?: false
|
||||
}
|
||||
|
||||
fun addMember(serverId: String, member: Member) {
|
||||
if (!memberCache.containsKey(serverId)) {
|
||||
memberCache[serverId] = mutableMapOf()
|
||||
}
|
||||
|
||||
memberCache[serverId]?.set(member.id.user, member)
|
||||
}
|
||||
}
|
||||
|
|
@ -23,7 +23,7 @@ import kotlinx.serialization.builtins.ListSerializer
|
|||
suspend fun fetchMessagesFromChannel(
|
||||
channelId: String,
|
||||
limit: Int = 50,
|
||||
include_users: Boolean = false,
|
||||
includeUsers: Boolean = false,
|
||||
before: String? = null,
|
||||
after: String? = null,
|
||||
nearby: String? = null,
|
||||
|
|
@ -33,7 +33,7 @@ suspend fun fetchMessagesFromChannel(
|
|||
headers.append(RevoltAPI.TOKEN_HEADER_NAME, RevoltAPI.sessionToken)
|
||||
|
||||
parameter("limit", limit)
|
||||
parameter("include_users", include_users)
|
||||
parameter("include_users", includeUsers)
|
||||
|
||||
if (before != null) parameter("before", before)
|
||||
if (after != null) parameter("after", after)
|
||||
|
|
@ -42,7 +42,7 @@ suspend fun fetchMessagesFromChannel(
|
|||
}
|
||||
.bodyAsText()
|
||||
|
||||
if (include_users) {
|
||||
if (includeUsers) {
|
||||
return RevoltJson.decodeFromString(
|
||||
MessagesInChannel.serializer(),
|
||||
response
|
||||
|
|
|
|||
|
|
@ -1,11 +1,83 @@
|
|||
package chat.revolt.api.routes.server
|
||||
|
||||
import chat.revolt.api.RevoltAPI
|
||||
import chat.revolt.api.RevoltError
|
||||
import chat.revolt.api.RevoltHttp
|
||||
import io.ktor.client.request.*
|
||||
import chat.revolt.api.RevoltJson
|
||||
import chat.revolt.api.internals.Members
|
||||
import chat.revolt.api.schemas.Member
|
||||
import chat.revolt.api.schemas.User
|
||||
import io.ktor.client.request.get
|
||||
import io.ktor.client.request.parameter
|
||||
import io.ktor.client.request.put
|
||||
import io.ktor.client.statement.bodyAsText
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.SerializationException
|
||||
|
||||
@Serializable
|
||||
data class FetchMembersResponse(
|
||||
val members: List<Member>,
|
||||
val users: List<User>
|
||||
)
|
||||
|
||||
suspend fun ackServer(serverId: String) {
|
||||
RevoltHttp.put("/servers/$serverId/ack") {
|
||||
headers.append(RevoltAPI.TOKEN_HEADER_NAME, RevoltAPI.sessionToken)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun fetchMembers(
|
||||
serverId: String,
|
||||
includeOffline: Boolean = false,
|
||||
pure: Boolean = false
|
||||
): FetchMembersResponse {
|
||||
val response = RevoltHttp.get("/servers/$serverId/members") {
|
||||
headers.append(RevoltAPI.TOKEN_HEADER_NAME, RevoltAPI.sessionToken)
|
||||
|
||||
parameter("exclude_offline", !includeOffline)
|
||||
}
|
||||
|
||||
val responseContent = response.bodyAsText()
|
||||
|
||||
try {
|
||||
val error = RevoltJson.decodeFromString(RevoltError.serializer(), responseContent)
|
||||
throw Error(error.type)
|
||||
} catch (e: SerializationException) {
|
||||
// Not an error
|
||||
}
|
||||
|
||||
val membersResponse =
|
||||
RevoltJson.decodeFromString(FetchMembersResponse.serializer(), responseContent)
|
||||
|
||||
if (pure) {
|
||||
return membersResponse
|
||||
}
|
||||
|
||||
membersResponse.members.forEach { member ->
|
||||
if (!Members.hasMember(serverId, member.id.user)) {
|
||||
Members.addMember(serverId, member)
|
||||
}
|
||||
}
|
||||
|
||||
membersResponse.users.forEach { user ->
|
||||
user.id?.let { RevoltAPI.userCache.putIfAbsent(it, user) }
|
||||
}
|
||||
|
||||
return membersResponse
|
||||
}
|
||||
|
||||
suspend fun fetchMember(serverId: String, userId: String, pure: Boolean = false): Member {
|
||||
val response = RevoltHttp.get("/servers/$serverId/members/$userId") {
|
||||
headers.append(RevoltAPI.TOKEN_HEADER_NAME, RevoltAPI.sessionToken)
|
||||
}
|
||||
|
||||
val member = RevoltJson.decodeFromString(Member.serializer(), response.bodyAsText())
|
||||
|
||||
if (!pure) {
|
||||
if (!Members.hasMember(serverId, member.id.user)) {
|
||||
Members.addMember(serverId, member)
|
||||
}
|
||||
}
|
||||
|
||||
return member
|
||||
}
|
||||
|
|
@ -54,6 +54,7 @@ import chat.revolt.R
|
|||
import chat.revolt.api.RevoltAPI
|
||||
import chat.revolt.api.realtime.DisconnectionState
|
||||
import chat.revolt.api.realtime.RealtimeSocket
|
||||
import chat.revolt.api.routes.server.fetchMembers
|
||||
import chat.revolt.api.schemas.User
|
||||
import chat.revolt.api.settings.SyncedSettings
|
||||
import chat.revolt.components.chat.DisconnectedNotice
|
||||
|
|
@ -101,12 +102,12 @@ class ChatRouterViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun setCurrentServer(serverId: String, save: Boolean = true) {
|
||||
private suspend fun setCurrentServer(serverId: String, save: Boolean = true) {
|
||||
currentServer = serverId
|
||||
|
||||
if (save) viewModelScope.launch {
|
||||
kvStorage.set("currentServer", serverId)
|
||||
}
|
||||
if (save) kvStorage.set("currentServer", serverId)
|
||||
|
||||
if (serverId != "home") fetchMembers(serverId, includeOffline = false, pure = false)
|
||||
}
|
||||
|
||||
private fun setSaveCurrentChannel(channelId: String) {
|
||||
|
|
@ -129,13 +130,17 @@ class ChatRouterViewModel @Inject constructor(
|
|||
popUpTo(route)
|
||||
}
|
||||
}
|
||||
setCurrentServer("home")
|
||||
viewModelScope.launch {
|
||||
setCurrentServer("home")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
val channelId = RevoltAPI.serverCache[serverId]?.channels?.firstOrNull()
|
||||
|
||||
setCurrentServer(serverId, channelId != null)
|
||||
viewModelScope.launch {
|
||||
setCurrentServer(serverId, channelId != null)
|
||||
}
|
||||
|
||||
if (channelId != null) {
|
||||
navigateToChannel(channelId, navController)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import androidx.lifecycle.ViewModel
|
|||
import androidx.lifecycle.viewModelScope
|
||||
import chat.revolt.api.RevoltAPI
|
||||
import chat.revolt.api.RevoltJson
|
||||
import chat.revolt.api.internals.Members
|
||||
import chat.revolt.api.internals.ULID
|
||||
import chat.revolt.api.realtime.RealtimeSocketFrames
|
||||
import chat.revolt.api.realtime.frames.receivable.ChannelStartTypingFrame
|
||||
|
|
@ -27,6 +28,7 @@ import chat.revolt.api.routes.channel.sendMessage
|
|||
import chat.revolt.api.routes.microservices.autumn.FileArgs
|
||||
import chat.revolt.api.routes.microservices.autumn.MAX_ATTACHMENTS_PER_MESSAGE
|
||||
import chat.revolt.api.routes.microservices.autumn.uploadToAutumn
|
||||
import chat.revolt.api.routes.server.fetchMember
|
||||
import chat.revolt.api.routes.user.addUserIfUnknown
|
||||
import chat.revolt.api.schemas.Channel
|
||||
import chat.revolt.api.schemas.Message
|
||||
|
|
@ -101,7 +103,7 @@ class ChannelScreenViewModel : ViewModel() {
|
|||
fetchMessagesFromChannel(
|
||||
activeChannel!!.id!!,
|
||||
limit = 50,
|
||||
true,
|
||||
includeUsers = true,
|
||||
before = if (renderableMessages.isNotEmpty()) {
|
||||
renderableMessages.last().id
|
||||
} else {
|
||||
|
|
@ -119,6 +121,18 @@ class ChannelScreenViewModel : ViewModel() {
|
|||
}
|
||||
messages.add(message)
|
||||
}
|
||||
|
||||
it.users?.forEach { user ->
|
||||
if (!RevoltAPI.userCache.containsKey(user.id)) {
|
||||
RevoltAPI.userCache[user.id!!] = user
|
||||
}
|
||||
}
|
||||
|
||||
it.members?.forEach { member ->
|
||||
if (!Members.hasMember(member.id.server, member.id.user)) {
|
||||
Members.addMember(member.id.server, member)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
regroupMessages(renderableMessages + messages)
|
||||
|
|
@ -260,6 +274,7 @@ class ChannelScreenViewModel : ViewModel() {
|
|||
if (it.channel != activeChannel?.id) return@onEach
|
||||
|
||||
addUserIfUnknown(it.author!!)
|
||||
activeChannel?.server?.let { s -> fetchMember(s, it.author) }
|
||||
regroupMessages(listOf(it) + renderableMessages)
|
||||
ackNewest()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,19 @@
|
|||
package chat.revolt.sheets
|
||||
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.LocalTextStyle
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
|
|
@ -17,17 +24,20 @@ import androidx.compose.runtime.remember
|
|||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import chat.revolt.R
|
||||
import chat.revolt.api.RevoltAPI
|
||||
import chat.revolt.api.internals.Members
|
||||
import chat.revolt.api.internals.RvxDummyMemberAPI
|
||||
import chat.revolt.api.internals.WebCompat
|
||||
import chat.revolt.api.internals.solidColor
|
||||
import chat.revolt.api.routes.user.fetchUserProfile
|
||||
import chat.revolt.api.schemas.Profile
|
||||
import chat.revolt.components.generic.UIMarkdown
|
||||
import chat.revolt.components.screens.settings.RawUserOverview
|
||||
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@Composable
|
||||
fun UserContextSheet(
|
||||
userId: String,
|
||||
|
|
@ -36,7 +46,6 @@ fun UserContextSheet(
|
|||
) {
|
||||
val user = RevoltAPI.userCache[userId]
|
||||
|
||||
@OptIn(RvxDummyMemberAPI::class)
|
||||
val member = serverId?.let { Members.getMember(it, userId) }
|
||||
|
||||
val server = RevoltAPI.serverCache[serverId]
|
||||
|
|
@ -66,9 +75,44 @@ fun UserContextSheet(
|
|||
Column(
|
||||
modifier = Modifier.padding(16.dp)
|
||||
) {
|
||||
Text(
|
||||
text = "sheet for ${server?.name ?: "serverless (omg jamstack reference??)"}",
|
||||
)
|
||||
member?.roles?.let {
|
||||
Text(
|
||||
text = stringResource(id = R.string.user_context_sheet_category_roles),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
modifier = Modifier.padding(bottom = 10.dp)
|
||||
)
|
||||
|
||||
FlowRow(
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
it.forEach { roleId ->
|
||||
val role = server?.roles?.get(roleId)
|
||||
role?.let {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.border(
|
||||
border = BorderStroke(
|
||||
width = 1.dp,
|
||||
brush = role.colour?.let { WebCompat.parseColour(it) }
|
||||
?: Brush.solidColor(LocalContentColor.current),
|
||||
),
|
||||
shape = MaterialTheme.shapes.small
|
||||
)
|
||||
.padding(8.dp)
|
||||
) {
|
||||
Text(
|
||||
text = role.name ?: roleId,
|
||||
style = LocalTextStyle.current.copy(
|
||||
brush = role.colour?.let { WebCompat.parseColour(it) }
|
||||
?: Brush.solidColor(LocalContentColor.current)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Text(
|
||||
text = stringResource(id = R.string.user_context_sheet_category_bio),
|
||||
|
|
|
|||
|
|
@ -181,6 +181,7 @@
|
|||
|
||||
<string name="user_context_sheet_category_bio">Bio</string>
|
||||
<string name="user_context_sheet_bio_empty">This user hasn\'t set a bio yet.</string>
|
||||
<string name="user_context_sheet_category_roles">Roles</string>
|
||||
|
||||
<string name="add_server_sheet_title">Add a server</string>
|
||||
<string name="add_server_sheet_join_by_invite">Join by invite code or link</string>
|
||||
|
|
|
|||
Loading…
Reference in New Issue