feat: refactor member manager, expand websocket coverage
Signed-off-by: Infi <wingit@geist.ga>
This commit is contained in:
parent
33fd3c6bd4
commit
98aa10c39e
|
|
@ -5,6 +5,7 @@ import android.os.Looper
|
|||
import android.util.Log
|
||||
import androidx.compose.runtime.mutableStateMapOf
|
||||
import chat.revolt.BuildConfig
|
||||
import chat.revolt.api.internals.Members
|
||||
import chat.revolt.api.realtime.DisconnectionState
|
||||
import chat.revolt.api.realtime.RealtimeSocket
|
||||
import chat.revolt.api.routes.user.fetchSelf
|
||||
|
|
@ -98,6 +99,8 @@ object RevoltAPI {
|
|||
val emojiCache = mutableStateMapOf<String, Emoji>()
|
||||
val messageCache = mutableStateMapOf<String, Message>()
|
||||
|
||||
val members = Members()
|
||||
|
||||
val unreads = Unreads()
|
||||
|
||||
var selfId: String? = null
|
||||
|
|
@ -182,6 +185,7 @@ object RevoltAPI {
|
|||
emojiCache.clear()
|
||||
messageCache.clear()
|
||||
|
||||
members.clear()
|
||||
unreads.clear()
|
||||
|
||||
socketCoroutine?.cancel()
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package chat.revolt.api.internals
|
|||
|
||||
import chat.revolt.api.schemas.Member
|
||||
|
||||
object Members {
|
||||
class Members {
|
||||
// memberCache (mapping of serverId to userId to member)
|
||||
private val memberCache = mutableMapOf<String, MutableMap<String, Member>>()
|
||||
|
||||
|
|
@ -14,11 +14,19 @@ object Members {
|
|||
return memberCache[serverId]?.containsKey(userId) ?: false
|
||||
}
|
||||
|
||||
fun addMember(serverId: String, member: Member) {
|
||||
fun setMember(serverId: String, member: Member) {
|
||||
if (!memberCache.containsKey(serverId)) {
|
||||
memberCache[serverId] = mutableMapOf()
|
||||
}
|
||||
|
||||
|
||||
memberCache[serverId]?.set(member.id.user, member)
|
||||
}
|
||||
|
||||
fun removeMember(serverId: String, userId: String) {
|
||||
memberCache[serverId]?.remove(userId)
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
memberCache.clear()
|
||||
}
|
||||
}
|
||||
|
|
@ -17,9 +17,15 @@ import chat.revolt.api.realtime.frames.receivable.MessageUpdateFrame
|
|||
import chat.revolt.api.realtime.frames.receivable.PongFrame
|
||||
import chat.revolt.api.realtime.frames.receivable.ReadyFrame
|
||||
import chat.revolt.api.realtime.frames.receivable.ServerCreateFrame
|
||||
import chat.revolt.api.realtime.frames.receivable.ServerDeleteFrame
|
||||
import chat.revolt.api.realtime.frames.receivable.ServerMemberJoinFrame
|
||||
import chat.revolt.api.realtime.frames.receivable.ServerMemberLeaveFrame
|
||||
import chat.revolt.api.realtime.frames.receivable.ServerMemberUpdateFrame
|
||||
import chat.revolt.api.realtime.frames.receivable.ServerUpdateFrame
|
||||
import chat.revolt.api.realtime.frames.receivable.UserUpdateFrame
|
||||
import chat.revolt.api.realtime.frames.sendable.AuthorizationFrame
|
||||
import chat.revolt.api.realtime.frames.sendable.PingFrame
|
||||
import chat.revolt.api.routes.server.fetchMember
|
||||
import io.ktor.client.plugins.websocket.ws
|
||||
import io.ktor.websocket.CloseReason
|
||||
import io.ktor.websocket.Frame
|
||||
|
|
@ -288,6 +294,97 @@ object RealtimeSocket {
|
|||
RevoltAPI.wsFrameChannel.send(channelStopTypingFrame)
|
||||
}
|
||||
|
||||
"ServerUpdate" -> {
|
||||
val serverUpdateFrame =
|
||||
RevoltJson.decodeFromString(ServerUpdateFrame.serializer(), rawFrame)
|
||||
Log.d(
|
||||
"RealtimeSocket",
|
||||
"Received server update frame for ${serverUpdateFrame.id}."
|
||||
)
|
||||
|
||||
val existing = RevoltAPI.serverCache[serverUpdateFrame.id]
|
||||
?: return // if we don't have the server no point in updating it
|
||||
|
||||
var updated =
|
||||
existing.mergeWithPartial(serverUpdateFrame.data)
|
||||
|
||||
serverUpdateFrame.clear?.forEach {
|
||||
when (it) {
|
||||
"Icon" -> updated = updated.copy(icon = null)
|
||||
"Banner" -> updated = updated.copy(banner = null)
|
||||
"Description" -> updated = updated.copy(description = null)
|
||||
else -> Log.e("RealtimeSocket", "Unknown server clear field: $it")
|
||||
}
|
||||
}
|
||||
|
||||
RevoltAPI.serverCache[serverUpdateFrame.id] = updated
|
||||
}
|
||||
|
||||
"ServerDelete" -> {
|
||||
val serverDeleteFrame =
|
||||
RevoltJson.decodeFromString(ServerDeleteFrame.serializer(), rawFrame)
|
||||
Log.d(
|
||||
"RealtimeSocket",
|
||||
"Received server delete frame for ${serverDeleteFrame.id}."
|
||||
)
|
||||
|
||||
RevoltAPI.serverCache.remove(serverDeleteFrame.id)
|
||||
}
|
||||
|
||||
"ServerMemberUpdate" -> {
|
||||
val serverMemberUpdateFrame =
|
||||
RevoltJson.decodeFromString(ServerMemberUpdateFrame.serializer(), rawFrame)
|
||||
Log.d(
|
||||
"RealtimeSocket",
|
||||
"Received server member update frame for ${serverMemberUpdateFrame.id.user} in ${serverMemberUpdateFrame.id.server}."
|
||||
)
|
||||
|
||||
val existing = RevoltAPI.members.getMember(
|
||||
serverMemberUpdateFrame.id.server,
|
||||
serverMemberUpdateFrame.id.user
|
||||
)
|
||||
?: return // if we don't have the member no point in updating them
|
||||
|
||||
var updated = existing.mergeWithPartial(serverMemberUpdateFrame.data)
|
||||
|
||||
serverMemberUpdateFrame.clear?.forEach {
|
||||
when (it) {
|
||||
"Avatar" -> updated = updated.copy(avatar = null)
|
||||
"Nickname" -> updated = updated.copy(nickname = null)
|
||||
else -> Log.e("RealtimeSocket", "Unknown server member clear field: $it")
|
||||
}
|
||||
}
|
||||
|
||||
RevoltAPI.members.setMember(serverMemberUpdateFrame.id.server, updated)
|
||||
}
|
||||
|
||||
"ServerMemberJoin" -> {
|
||||
val serverMemberJoinFrame =
|
||||
RevoltJson.decodeFromString(ServerMemberJoinFrame.serializer(), rawFrame)
|
||||
Log.d(
|
||||
"RealtimeSocket",
|
||||
"Received server member join frame for ${serverMemberJoinFrame.user} in ${serverMemberJoinFrame.id}."
|
||||
)
|
||||
|
||||
val member = fetchMember(serverMemberJoinFrame.id, serverMemberJoinFrame.user)
|
||||
|
||||
RevoltAPI.members.setMember(serverMemberJoinFrame.id, member)
|
||||
}
|
||||
|
||||
"ServerMemberLeave" -> {
|
||||
val serverMemberLeaveFrame =
|
||||
RevoltJson.decodeFromString(ServerMemberLeaveFrame.serializer(), rawFrame)
|
||||
Log.d(
|
||||
"RealtimeSocket",
|
||||
"Received server member leave frame for ${serverMemberLeaveFrame.user} in ${serverMemberLeaveFrame.id}."
|
||||
)
|
||||
|
||||
RevoltAPI.members.removeMember(
|
||||
serverMemberLeaveFrame.id,
|
||||
serverMemberLeaveFrame.user
|
||||
)
|
||||
}
|
||||
|
||||
"Authenticated" -> {
|
||||
/* no-op */
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import chat.revolt.api.RevoltAPI
|
|||
import chat.revolt.api.RevoltError
|
||||
import chat.revolt.api.RevoltHttp
|
||||
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
|
||||
|
|
@ -54,8 +53,8 @@ suspend fun fetchMembers(
|
|||
}
|
||||
|
||||
membersResponse.members.forEach { member ->
|
||||
if (!Members.hasMember(serverId, member.id.user)) {
|
||||
Members.addMember(serverId, member)
|
||||
if (!RevoltAPI.members.hasMember(serverId, member.id.user)) {
|
||||
RevoltAPI.members.setMember(serverId, member)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -81,8 +80,8 @@ suspend fun fetchMember(serverId: String, userId: String, pure: Boolean = false)
|
|||
val member = RevoltJson.decodeFromString(Member.serializer(), response.bodyAsText())
|
||||
|
||||
if (!pure) {
|
||||
if (!Members.hasMember(serverId, member.id.user)) {
|
||||
Members.addMember(serverId, member)
|
||||
if (!RevoltAPI.members.hasMember(serverId, member.id.user)) {
|
||||
RevoltAPI.members.setMember(serverId, member)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,13 @@
|
|||
package chat.revolt.api.schemas
|
||||
|
||||
import kotlinx.serialization.*
|
||||
import kotlinx.serialization.descriptors.*
|
||||
import kotlinx.serialization.encoding.*
|
||||
import kotlinx.serialization.json.*
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.descriptors.PrimitiveKind
|
||||
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
|
||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
|
||||
@Serializable
|
||||
data class MessagesInChannel(
|
||||
|
|
@ -29,7 +33,17 @@ data class Member(
|
|||
val avatar: AutumnResource? = null,
|
||||
val roles: List<String>? = null,
|
||||
val nickname: String? = null
|
||||
)
|
||||
) {
|
||||
fun mergeWithPartial(other: Member): Member {
|
||||
return Member(
|
||||
id = other.id,
|
||||
joinedAt = other.joinedAt ?: joinedAt,
|
||||
avatar = other.avatar ?: avatar,
|
||||
roles = other.roles ?: roles,
|
||||
nickname = other.nickname ?: nickname
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class Channel(
|
||||
|
|
|
|||
|
|
@ -20,7 +20,26 @@ data class Server(
|
|||
val flags: Long? = null,
|
||||
val analytics: Boolean? = null,
|
||||
val discoverable: Boolean? = null,
|
||||
)
|
||||
) {
|
||||
fun mergeWithPartial(other: Server): Server {
|
||||
return Server(
|
||||
id = other.id ?: id,
|
||||
owner = other.owner ?: owner,
|
||||
name = other.name ?: name,
|
||||
description = other.description ?: description,
|
||||
channels = other.channels ?: channels,
|
||||
categories = other.categories ?: categories,
|
||||
systemMessages = other.systemMessages ?: systemMessages,
|
||||
roles = other.roles ?: roles,
|
||||
defaultPermissions = other.defaultPermissions ?: defaultPermissions,
|
||||
icon = other.icon ?: icon,
|
||||
banner = other.banner ?: banner,
|
||||
flags = other.flags ?: flags,
|
||||
analytics = other.analytics ?: analytics,
|
||||
discoverable = other.discoverable ?: discoverable,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class Category(
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ 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
|
||||
|
|
@ -129,8 +128,8 @@ class ChannelScreenViewModel : ViewModel() {
|
|||
}
|
||||
|
||||
it.members?.forEach { member ->
|
||||
if (!Members.hasMember(member.id.server, member.id.user)) {
|
||||
Members.addMember(member.id.server, member)
|
||||
if (!RevoltAPI.members.hasMember(member.id.server, member.id.user)) {
|
||||
RevoltAPI.members.setMember(member.id.server, member)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ 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.WebCompat
|
||||
import chat.revolt.api.internals.solidColor
|
||||
import chat.revolt.api.routes.user.fetchUserProfile
|
||||
|
|
@ -44,7 +43,7 @@ fun UserContextSheet(
|
|||
) {
|
||||
val user = RevoltAPI.userCache[userId]
|
||||
|
||||
val member = serverId?.let { Members.getMember(it, userId) }
|
||||
val member = serverId?.let { RevoltAPI.members.getMember(it, userId) }
|
||||
|
||||
val server = RevoltAPI.serverCache[serverId]
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue