-
Notifications
You must be signed in to change notification settings - Fork 46
SES-4753 : Manage Members #1665
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from all commits
f3b8de3
ac4d7c2
65ec34e
edee85b
d3b7224
49d20bb
97ed879
32d2d7f
91830a2
417642a
06458f3
6d81de6
d93ad02
2927aa2
a6edb1a
73554cc
fd5d21b
3b6fb51
73f23ea
23ccf9c
b7386e6
5351084
5873753
5aebd8d
70df9fb
df3f53e
59d87e9
f8f4fb2
1db4656
8ed326e
fd4391a
360682b
676ad55
d315cb8
398af1c
65728c9
682b84c
850ff03
8a96d6a
e14871d
a445363
f0e66ae
db69eca
47256e1
8bdb2d9
f90d1db
97ec585
6731b93
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -89,6 +89,16 @@ abstract class BaseGroupMembersViewModel( | |
| ::filterContacts | ||
| ).stateIn(viewModelScope, SharingStarted.Lazily, emptyList()) | ||
|
|
||
| // Output: List of only NON-ADMINS | ||
| val nonAdminMembers: StateFlow<List<GroupMemberState>> = members | ||
| .map { list -> list.filter { !it.showAsAdmin } } | ||
| .stateIn(viewModelScope, SharingStarted.Lazily, emptyList()) | ||
|
|
||
| val hasNonAdminMembers: StateFlow<Boolean> = | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this needed? You already have
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added this flag since having 0 results when searching/filtering causes the empty list UI to show. |
||
| groupInfo | ||
| .map { pair -> pair?.second.orEmpty().any { !it.showAsAdmin } } | ||
| .stateIn(viewModelScope, SharingStarted.Lazily, false) | ||
|
|
||
| fun onSearchQueryChanged(query: String) { | ||
| mutableSearchQuery.value = query | ||
| } | ||
|
|
@@ -140,7 +150,7 @@ abstract class BaseGroupMembersViewModel( | |
| showProBadge = proStatus.shouldShowProBadge(), | ||
| avatarUIData = avatarUtils.getUIDataFromAccountId(memberAccountId.hexString), | ||
| clickable = !isMyself, | ||
| statusLabel = getMemberLabel(status, context, amIAdmin), | ||
| statusLabel = getMemberLabel(status, context, amIAdmin) | ||
| ) | ||
| } | ||
|
|
||
|
|
@@ -172,16 +182,35 @@ abstract class BaseGroupMembersViewModel( | |
| } | ||
| } | ||
|
|
||
| // Refer to notion doc for the sorting logic | ||
| // Refer to manage members/admin PRD for the sorting logic | ||
| private fun sortMembers(members: List<GroupMemberState>, currentUserId: AccountId) = | ||
| members.sortedWith( | ||
| compareBy<GroupMemberState>{ it.accountId != currentUserId } // Current user comes first | ||
| .thenBy { !it.showAsAdmin } // Admins come first | ||
| .thenComparing(compareBy(String.CASE_INSENSITIVE_ORDER) { it.name }) // Sort by name (case insensitive) | ||
| .thenBy { it.accountId } // Last resort: sort by account ID | ||
| compareBy<GroupMemberState> { stateOrder(it.status) } | ||
| .thenBy { it.accountId != currentUserId } | ||
| .thenComparing(compareBy(String.CASE_INSENSITIVE_ORDER) { it.name }) | ||
| .thenBy { it.accountId } | ||
| ) | ||
| } | ||
|
|
||
| private fun stateOrder(status: GroupMember.Status?): Int = when (status) { | ||
| // 1. Invite failed | ||
| GroupMember.Status.INVITE_FAILED -> 0 | ||
| // 2. Invite not sent | ||
| GroupMember.Status.INVITE_NOT_SENT -> 1 | ||
| // 3. Sending invite | ||
| GroupMember.Status.INVITE_SENDING -> 2 | ||
| // 4. Invite sent | ||
| GroupMember.Status.INVITE_SENT -> 3 | ||
| // 5. Invite status unknown | ||
| GroupMember.Status.INVITE_UNKNOWN -> 4 | ||
| // 6. Pending removal | ||
| GroupMember.Status.REMOVED, | ||
| GroupMember.Status.REMOVED_UNKNOWN, | ||
| GroupMember.Status.REMOVED_INCLUDING_MESSAGES -> 5 | ||
| // 7. Member (everything else) | ||
| else -> 6 | ||
| } | ||
|
|
||
| data class GroupMemberState( | ||
| val accountId: AccountId, | ||
| val avatarUIData: AvatarUIData, | ||
|
|
@@ -195,7 +224,7 @@ data class GroupMemberState( | |
| val canRemove: Boolean, | ||
| val canPromote: Boolean, | ||
| val clickable: Boolean, | ||
| val statusLabel: String, | ||
| val statusLabel: String | ||
| ) { | ||
| val canEdit: Boolean get() = canRemove || canPromote || canResendInvite || canResendPromotion | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might not be safe adding a property directly into an existing job. A job is persisted into db and when it's deserialized, funny thing could happen but i'm not very sure what the behavior here will be. You can try this by inviting someone to the group, using code prior to this PR, then quickly kill the app before the invite completes. Then you install the code from this PR and see whether you'll have trouble reintroduce the job. It could also be fine though, just need some testing