-
Notifications
You must be signed in to change notification settings - Fork 232
Improve room setting edition #2849
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
Changes from 15 commits
cf0da56
4b720f9
176c7c8
0b9724a
12ad232
a4c4d6d
e6badb1
ef28cf3
04537bc
cf654fd
1710671
193081a
4278c24
7f5a3b7
1c9d046
c3caff0
5ab96c3
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 |
---|---|---|
|
@@ -37,6 +37,9 @@ import io.element.android.libraries.matrix.api.room.MatrixRoom | |
import io.element.android.libraries.matrix.api.room.StateEventType | ||
import io.element.android.libraries.matrix.api.room.powerlevels.canSendState | ||
import io.element.android.libraries.matrix.ui.media.AvatarAction | ||
import io.element.android.libraries.matrix.ui.room.avatarUrl | ||
import io.element.android.libraries.matrix.ui.room.rawName | ||
import io.element.android.libraries.matrix.ui.room.topic | ||
import io.element.android.libraries.mediapickers.api.PickerProvider | ||
import io.element.android.libraries.mediaupload.api.MediaPreProcessor | ||
import io.element.android.libraries.permissions.api.PermissionsEvents | ||
|
@@ -61,41 +64,56 @@ class RoomDetailsEditPresenter @Inject constructor( | |
val cameraPermissionState = cameraPermissionPresenter.present() | ||
val roomSyncUpdateFlow = room.syncUpdateFlow.collectAsState() | ||
|
||
// Since there is no way to obtain the new avatar uri after uploading a new avatar, | ||
// just erase the local value when the room field has changed | ||
var roomAvatarUri by rememberSaveable(room.avatarUrl) { mutableStateOf(room.avatarUrl?.toUri()) } | ||
val roomAvatarUri = room.avatarUrl()?.toUri() | ||
var roomAvatarUriEdited by rememberSaveable { mutableStateOf<Uri?>(null) } | ||
LaunchedEffect(roomAvatarUri) { | ||
// Every time the roomAvatar change (from sync), we can set the new avatar. | ||
roomAvatarUriEdited = roomAvatarUri | ||
} | ||
|
||
var roomName by rememberSaveable { mutableStateOf(room.displayName.trim()) } | ||
var roomTopic by rememberSaveable { mutableStateOf(room.topic?.trim()) } | ||
val roomRawNameTrimmed = room.rawName().orEmpty().trim() | ||
var roomRawNameEdited by rememberSaveable { mutableStateOf("") } | ||
LaunchedEffect(roomRawNameTrimmed) { | ||
// Every time the rawName change (from sync), we can set the new name. | ||
roomRawNameEdited = roomRawNameTrimmed | ||
} | ||
val roomTopicTrimmed = room.topic().orEmpty().trim() | ||
var roomTopicEdited by rememberSaveable { mutableStateOf("") } | ||
LaunchedEffect(roomTopicTrimmed) { | ||
// Every time the topic change (from sync), we can set the new topic. | ||
roomTopicEdited = roomTopicTrimmed | ||
} | ||
|
||
val saveButtonEnabled by remember( | ||
roomSyncUpdateFlow.value, | ||
roomName, | ||
roomTopic, | ||
roomRawNameTrimmed, | ||
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. This is a bit weird to use 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. Yes, I tried to remove the keys, but this does not work as expected, for instance 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. Because 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. OK, so I have a mix in c3caff0. I think it's still relevant to use de |
||
roomRawNameEdited, | ||
roomTopicTrimmed, | ||
roomTopicEdited, | ||
roomAvatarUri, | ||
roomAvatarUriEdited, | ||
) { | ||
derivedStateOf { | ||
roomAvatarUri?.toString()?.trim() != room.avatarUrl?.toUri()?.toString()?.trim() || | ||
roomName.trim() != room.displayName.trim() || | ||
roomTopic.orEmpty().trim() != room.topic.orEmpty().trim() | ||
roomRawNameTrimmed != roomRawNameEdited.trim() || | ||
roomTopicTrimmed != roomTopicEdited.trim() || | ||
roomAvatarUri != roomAvatarUriEdited | ||
} | ||
} | ||
|
||
var canChangeName by remember { mutableStateOf(false) } | ||
var canChangeTopic by remember { mutableStateOf(false) } | ||
var canChangeAvatar by remember { mutableStateOf(false) } | ||
|
||
LaunchedEffect(Unit) { | ||
LaunchedEffect(roomSyncUpdateFlow.value) { | ||
canChangeName = room.canSendState(StateEventType.ROOM_NAME).getOrElse { false } | ||
canChangeTopic = room.canSendState(StateEventType.ROOM_TOPIC).getOrElse { false } | ||
canChangeAvatar = room.canSendState(StateEventType.ROOM_AVATAR).getOrElse { false } | ||
} | ||
|
||
val cameraPhotoPicker = mediaPickerProvider.registerCameraPhotoPicker( | ||
onResult = { uri -> if (uri != null) roomAvatarUri = uri } | ||
onResult = { uri -> if (uri != null) roomAvatarUriEdited = uri } | ||
) | ||
val galleryImagePicker = mediaPickerProvider.registerGalleryImagePicker( | ||
onResult = { uri -> if (uri != null) roomAvatarUri = uri } | ||
onResult = { uri -> if (uri != null) roomAvatarUriEdited = uri } | ||
) | ||
|
||
LaunchedEffect(cameraPermissionState.permissionGranted) { | ||
|
@@ -105,12 +123,12 @@ class RoomDetailsEditPresenter @Inject constructor( | |
} | ||
} | ||
|
||
val avatarActions by remember(roomAvatarUri) { | ||
val avatarActions by remember(roomAvatarUriEdited) { | ||
derivedStateOf { | ||
listOfNotNull( | ||
AvatarAction.TakePhoto, | ||
AvatarAction.ChoosePhoto, | ||
AvatarAction.Remove.takeIf { roomAvatarUri != null }, | ||
AvatarAction.Remove.takeIf { roomAvatarUriEdited != null }, | ||
).toImmutableList() | ||
} | ||
} | ||
|
@@ -119,7 +137,15 @@ class RoomDetailsEditPresenter @Inject constructor( | |
val localCoroutineScope = rememberCoroutineScope() | ||
fun handleEvents(event: RoomDetailsEditEvents) { | ||
when (event) { | ||
is RoomDetailsEditEvents.Save -> localCoroutineScope.saveChanges(roomName, roomTopic, roomAvatarUri, saveAction) | ||
is RoomDetailsEditEvents.Save -> localCoroutineScope.saveChanges( | ||
currentNameTrimmed = roomRawNameTrimmed, | ||
newNameTrimmed = roomRawNameEdited.trim(), | ||
currentTopicTrimmed = roomTopicTrimmed, | ||
newTopicTrimmed = roomTopicEdited.trim(), | ||
currentAvatar = roomAvatarUri, | ||
newAvatarUri = roomAvatarUriEdited, | ||
action = saveAction, | ||
) | ||
is RoomDetailsEditEvents.HandleAvatarAction -> { | ||
when (event.action) { | ||
AvatarAction.ChoosePhoto -> galleryImagePicker.launch() | ||
|
@@ -129,23 +155,23 @@ class RoomDetailsEditPresenter @Inject constructor( | |
pendingPermissionRequest = true | ||
cameraPermissionState.eventSink(PermissionsEvents.RequestPermissions) | ||
} | ||
AvatarAction.Remove -> roomAvatarUri = null | ||
AvatarAction.Remove -> roomAvatarUriEdited = null | ||
} | ||
} | ||
|
||
is RoomDetailsEditEvents.UpdateRoomName -> roomName = event.name | ||
is RoomDetailsEditEvents.UpdateRoomTopic -> roomTopic = event.topic.takeUnless { it.isEmpty() } | ||
is RoomDetailsEditEvents.UpdateRoomName -> roomRawNameEdited = event.name | ||
is RoomDetailsEditEvents.UpdateRoomTopic -> roomTopicEdited = event.topic | ||
RoomDetailsEditEvents.CancelSaveChanges -> saveAction.value = AsyncAction.Uninitialized | ||
} | ||
} | ||
|
||
return RoomDetailsEditState( | ||
roomId = room.roomId.value, | ||
roomName = roomName, | ||
roomId = room.roomId, | ||
roomRawName = roomRawNameEdited, | ||
canChangeName = canChangeName, | ||
roomTopic = roomTopic.orEmpty(), | ||
roomTopic = roomTopicEdited, | ||
canChangeTopic = canChangeTopic, | ||
roomAvatarUrl = roomAvatarUri, | ||
roomAvatarUrl = roomAvatarUriEdited, | ||
canChangeAvatar = canChangeAvatar, | ||
avatarActions = avatarActions, | ||
saveButtonEnabled = saveButtonEnabled, | ||
|
@@ -156,25 +182,28 @@ class RoomDetailsEditPresenter @Inject constructor( | |
} | ||
|
||
private fun CoroutineScope.saveChanges( | ||
name: String, | ||
topic: String?, | ||
avatarUri: Uri?, | ||
currentNameTrimmed: String, | ||
newNameTrimmed: String, | ||
currentTopicTrimmed: String, | ||
newTopicTrimmed: String, | ||
currentAvatar: Uri?, | ||
newAvatarUri: Uri?, | ||
action: MutableState<AsyncAction<Unit>>, | ||
) = launch { | ||
val results = mutableListOf<Result<Unit>>() | ||
suspend { | ||
if (topic.orEmpty().trim() != room.topic.orEmpty().trim()) { | ||
results.add(room.setTopic(topic.orEmpty()).onFailure { | ||
if (newTopicTrimmed != currentTopicTrimmed) { | ||
results.add(room.setTopic(newTopicTrimmed).onFailure { | ||
Timber.e(it, "Failed to set room topic") | ||
}) | ||
} | ||
if (name.isNotEmpty() && name.trim() != room.displayName.trim()) { | ||
results.add(room.setName(name).onFailure { | ||
if (newNameTrimmed.isNotEmpty() && newNameTrimmed != currentNameTrimmed) { | ||
results.add(room.setName(newNameTrimmed).onFailure { | ||
Timber.e(it, "Failed to set room name") | ||
}) | ||
} | ||
if (avatarUri?.toString()?.trim() != room.avatarUrl?.trim()) { | ||
results.add(updateAvatar(avatarUri).onFailure { | ||
if (newAvatarUri != currentAvatar) { | ||
results.add(updateAvatar(newAvatarUri).onFailure { | ||
Timber.e(it, "Failed to update avatar") | ||
}) | ||
} | ||
|
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.
Do we really want to have live values here? I'd expect to be static
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.
Yes, I think it's fine, having the value change during edition should not happen a lot, just during our cheeky tests :)