fix: opening member list makes app crash sometimes
Signed-off-by: Infi <infi@infi.sh>
This commit is contained in:
parent
8212dd0fe5
commit
88a5a3e14a
|
|
@ -7,6 +7,7 @@ import chat.revolt.api.internals.ULID
|
||||||
import chat.revolt.api.schemas.Channel
|
import chat.revolt.api.schemas.Channel
|
||||||
import chat.revolt.api.schemas.Message
|
import chat.revolt.api.schemas.Message
|
||||||
import chat.revolt.api.schemas.MessagesInChannel
|
import chat.revolt.api.schemas.MessagesInChannel
|
||||||
|
import chat.revolt.api.schemas.User
|
||||||
import io.ktor.client.request.get
|
import io.ktor.client.request.get
|
||||||
import io.ktor.client.request.parameter
|
import io.ktor.client.request.parameter
|
||||||
import io.ktor.client.request.patch
|
import io.ktor.client.request.patch
|
||||||
|
|
@ -136,3 +137,13 @@ suspend fun fetchSingleChannel(channelId: String): Channel {
|
||||||
response
|
response
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun fetchGroupParticipants(channelId: String): List<User> {
|
||||||
|
val response = RevoltHttp.get("/channels/$channelId/members")
|
||||||
|
.bodyAsText()
|
||||||
|
|
||||||
|
return RevoltJson.decodeFromString(
|
||||||
|
ListSerializer(User.serializer()),
|
||||||
|
response
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -52,7 +52,8 @@ fun ChannelInfoSheet(
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
MemberListSheet(
|
MemberListSheet(
|
||||||
serverId = channel?.server ?: ""
|
channelId = channelId,
|
||||||
|
serverId = channel?.server,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -100,22 +101,28 @@ fun ChannelInfoSheet(
|
||||||
modifier = Modifier.padding(bottom = 4.dp)
|
modifier = Modifier.padding(bottom = 4.dp)
|
||||||
)
|
)
|
||||||
|
|
||||||
SheetClickable(
|
when (channel.channelType) {
|
||||||
icon = { modifier ->
|
ChannelType.TextChannel, ChannelType.VoiceChannel, ChannelType.Group -> {
|
||||||
Icon(
|
SheetClickable(
|
||||||
imageVector = Icons.Default.List,
|
icon = { modifier ->
|
||||||
contentDescription = null,
|
Icon(
|
||||||
modifier = modifier
|
imageVector = Icons.Default.List,
|
||||||
)
|
contentDescription = null,
|
||||||
},
|
modifier = modifier
|
||||||
label = { style ->
|
)
|
||||||
Text(
|
},
|
||||||
text = stringResource(id = R.string.channel_info_sheet_options_members),
|
label = { style ->
|
||||||
style = style
|
Text(
|
||||||
)
|
text = stringResource(id = R.string.channel_info_sheet_options_members),
|
||||||
|
style = style
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
memberListSheetShown = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
) {
|
|
||||||
memberListSheetShown = true
|
else -> {}
|
||||||
}
|
}
|
||||||
|
|
||||||
SheetClickable(
|
SheetClickable(
|
||||||
|
|
|
||||||
|
|
@ -46,13 +46,17 @@ import androidx.lifecycle.viewModelScope
|
||||||
import chat.revolt.R
|
import chat.revolt.R
|
||||||
import chat.revolt.api.REVOLT_FILES
|
import chat.revolt.api.REVOLT_FILES
|
||||||
import chat.revolt.api.RevoltAPI
|
import chat.revolt.api.RevoltAPI
|
||||||
|
import chat.revolt.api.internals.PermissionBit
|
||||||
import chat.revolt.api.internals.Roles
|
import chat.revolt.api.internals.Roles
|
||||||
import chat.revolt.api.internals.WebCompat
|
import chat.revolt.api.internals.WebCompat
|
||||||
|
import chat.revolt.api.internals.hasPermission
|
||||||
import chat.revolt.api.internals.solidColor
|
import chat.revolt.api.internals.solidColor
|
||||||
|
import chat.revolt.api.routes.channel.fetchGroupParticipants
|
||||||
import chat.revolt.api.routes.server.fetchMembers
|
import chat.revolt.api.routes.server.fetchMembers
|
||||||
import chat.revolt.api.schemas.Member
|
import chat.revolt.api.schemas.Member
|
||||||
import chat.revolt.api.schemas.User
|
import chat.revolt.api.schemas.User
|
||||||
import chat.revolt.components.generic.PageHeader
|
import chat.revolt.components.generic.PageHeader
|
||||||
|
import chat.revolt.components.generic.Presence
|
||||||
import chat.revolt.components.generic.UserAvatar
|
import chat.revolt.components.generic.UserAvatar
|
||||||
import chat.revolt.components.generic.presenceFromStatus
|
import chat.revolt.components.generic.presenceFromStatus
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
|
@ -67,6 +71,7 @@ val DO_NOT_FETCH_OFFLINE_MEMBERS_SERVERS = listOf(
|
||||||
|
|
||||||
sealed class MemberListItem {
|
sealed class MemberListItem {
|
||||||
data class MemberItem(val member: Member) : MemberListItem()
|
data class MemberItem(val member: Member) : MemberListItem()
|
||||||
|
data class UserItem(val user: User) : MemberListItem()
|
||||||
data class CategoryItem(val category: String, val count: Int) : MemberListItem()
|
data class CategoryItem(val category: String, val count: Int) : MemberListItem()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -77,14 +82,16 @@ class MemberListSheetViewModel @Inject constructor(
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
val fullItemList = mutableStateListOf<MemberListItem>()
|
val fullItemList = mutableStateListOf<MemberListItem>()
|
||||||
|
|
||||||
fun fetchMemberList(
|
fun fetchServerMemberList(
|
||||||
serverId: String
|
serverId: String,
|
||||||
|
channelId: String
|
||||||
) {
|
) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val memberList = fetchMembers(
|
val memberList = fetchMembers(
|
||||||
serverId = serverId,
|
serverId = serverId,
|
||||||
includeOffline = serverId !in DO_NOT_FETCH_OFFLINE_MEMBERS_SERVERS
|
includeOffline = serverId !in DO_NOT_FETCH_OFFLINE_MEMBERS_SERVERS
|
||||||
).members
|
).members
|
||||||
|
val channel = RevoltAPI.channelCache[channelId] ?: return@launch
|
||||||
|
|
||||||
val categories = mutableMapOf<String, List<Member>>()
|
val categories = mutableMapOf<String, List<Member>>()
|
||||||
|
|
||||||
|
|
@ -115,6 +122,12 @@ class MemberListSheetViewModel @Inject constructor(
|
||||||
defaultCategoryName
|
defaultCategoryName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!Roles.permissionFor(channel, user, member)
|
||||||
|
.hasPermission(PermissionBit.ViewChannel)
|
||||||
|
) {
|
||||||
|
return@forEach
|
||||||
|
}
|
||||||
|
|
||||||
categories[category] = (categories[category] ?: listOf()) + member
|
categories[category] = (categories[category] ?: listOf()) + member
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -156,12 +169,60 @@ class MemberListSheetViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun fetchGroupMemberList(channelId: String) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
val userList = fetchGroupParticipants(channelId)
|
||||||
|
|
||||||
|
val onlinePredicate = { user: User ->
|
||||||
|
presenceFromStatus(
|
||||||
|
user.status?.presence,
|
||||||
|
user.online ?: false
|
||||||
|
) != Presence.Offline
|
||||||
|
}
|
||||||
|
val offlinePredicate = { user: User ->
|
||||||
|
presenceFromStatus(
|
||||||
|
user.status?.presence,
|
||||||
|
user.online ?: false
|
||||||
|
) == Presence.Offline
|
||||||
|
}
|
||||||
|
|
||||||
|
fullItemList.clear()
|
||||||
|
|
||||||
|
if (userList.count(onlinePredicate) > 0) {
|
||||||
|
fullItemList.add(
|
||||||
|
MemberListItem.CategoryItem(
|
||||||
|
context.getString(R.string.status_online),
|
||||||
|
userList.count(onlinePredicate)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
userList.filter(onlinePredicate).forEach { user ->
|
||||||
|
fullItemList.add(MemberListItem.UserItem(user))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userList.count(offlinePredicate) > 0) {
|
||||||
|
fullItemList.add(
|
||||||
|
MemberListItem.CategoryItem(
|
||||||
|
context.getString(R.string.status_offline),
|
||||||
|
userList.count(offlinePredicate)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
userList.filter(offlinePredicate).forEach { user ->
|
||||||
|
fullItemList.add(MemberListItem.UserItem(user))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalFoundationApi::class, ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalFoundationApi::class, ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun MemberListSheet(
|
fun MemberListSheet(
|
||||||
serverId: String,
|
channelId: String,
|
||||||
|
serverId: String? = null,
|
||||||
viewModel: MemberListSheetViewModel = hiltViewModel()
|
viewModel: MemberListSheetViewModel = hiltViewModel()
|
||||||
) {
|
) {
|
||||||
var showUserContextSheet by remember { mutableStateOf(false) }
|
var showUserContextSheet by remember { mutableStateOf(false) }
|
||||||
|
|
@ -170,7 +231,11 @@ fun MemberListSheet(
|
||||||
// We use LaunchedEffect to make sure that this is called every time any of the users status changes
|
// We use LaunchedEffect to make sure that this is called every time any of the users status changes
|
||||||
LaunchedEffect(RevoltAPI.userCache) {
|
LaunchedEffect(RevoltAPI.userCache) {
|
||||||
snapshotFlow { RevoltAPI.userCache }.distinctUntilChanged().collect {
|
snapshotFlow { RevoltAPI.userCache }.distinctUntilChanged().collect {
|
||||||
viewModel.fetchMemberList(serverId)
|
if (serverId != null) {
|
||||||
|
viewModel.fetchServerMemberList(serverId, channelId)
|
||||||
|
} else {
|
||||||
|
viewModel.fetchGroupMemberList(channelId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -211,9 +276,21 @@ fun MemberListSheet(
|
||||||
}
|
}
|
||||||
|
|
||||||
is MemberListItem.MemberItem -> item(key = item.member.id!!.user) {
|
is MemberListItem.MemberItem -> item(key = item.member.id!!.user) {
|
||||||
MemberListMember(
|
MemberListMemberUser(
|
||||||
member = item.member,
|
|
||||||
user = RevoltAPI.userCache[item.member.id.user]!!,
|
user = RevoltAPI.userCache[item.member.id.user]!!,
|
||||||
|
member = item.member,
|
||||||
|
serverId = serverId,
|
||||||
|
onSelectUser = {
|
||||||
|
userContextSheetTarget = it
|
||||||
|
showUserContextSheet = true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
is MemberListItem.UserItem -> item(key = item.user.id!!) {
|
||||||
|
MemberListMemberUser(
|
||||||
|
user = item.user,
|
||||||
|
member = null,
|
||||||
serverId = serverId,
|
serverId = serverId,
|
||||||
onSelectUser = {
|
onSelectUser = {
|
||||||
userContextSheetTarget = it
|
userContextSheetTarget = it
|
||||||
|
|
@ -228,32 +305,38 @@ fun MemberListSheet(
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MemberListMember(
|
fun MemberListMemberUser(
|
||||||
member: Member,
|
|
||||||
user: User,
|
user: User,
|
||||||
serverId: String,
|
member: Member?,
|
||||||
|
serverId: String?,
|
||||||
onSelectUser: (String) -> Unit
|
onSelectUser: (String) -> Unit
|
||||||
) {
|
) {
|
||||||
val highestColourRole = Roles.resolveHighestRole(serverId, member.id!!.user, true)
|
val highestColourRole = serverId?.let {
|
||||||
|
Roles.resolveHighestRole(
|
||||||
|
it,
|
||||||
|
user.id!!,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
}
|
||||||
val colour = highestColourRole?.colour?.let { WebCompat.parseColour(it) }
|
val colour = highestColourRole?.colour?.let { WebCompat.parseColour(it) }
|
||||||
?: Brush.solidColor(LocalContentColor.current)
|
?: Brush.solidColor(LocalContentColor.current)
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.clickable {
|
.clickable {
|
||||||
onSelectUser(member.id.user)
|
onSelectUser(user.id!!)
|
||||||
}
|
}
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(horizontal = 12.dp, vertical = 8.dp),
|
.padding(horizontal = 12.dp, vertical = 8.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
UserAvatar(
|
UserAvatar(
|
||||||
username = member.nickname
|
username = member?.nickname
|
||||||
?: user.displayName
|
?: user.displayName
|
||||||
?: user.username
|
?: user.username
|
||||||
?: user.id!!,
|
?: user.id!!,
|
||||||
avatar = user.avatar,
|
avatar = user.avatar,
|
||||||
rawUrl = member.avatar?.let { "$REVOLT_FILES/avatars/${it.id}?max_side=256" },
|
rawUrl = member?.avatar?.let { "$REVOLT_FILES/avatars/${it.id}?max_side=256" },
|
||||||
userId = user.id!!,
|
userId = user.id!!,
|
||||||
presence = presenceFromStatus(
|
presence = presenceFromStatus(
|
||||||
user.status?.presence,
|
user.status?.presence,
|
||||||
|
|
@ -264,7 +347,7 @@ fun MemberListMember(
|
||||||
Spacer(modifier = Modifier.width(12.dp))
|
Spacer(modifier = Modifier.width(12.dp))
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
text = member.nickname
|
text = member?.nickname
|
||||||
?: user.displayName
|
?: user.displayName
|
||||||
?: user.username
|
?: user.username
|
||||||
?: user.id,
|
?: user.id,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue