Invite users to existing rooms (#441)
Invite users to existing rooms Scope: - Allow inviting from the room detail screen and the member list - Invite option is only shown if the user has the correct power level - Search flow the same as creating a new room, allowing multi-select - Existing room members/invitees are disabled with a custom caption - Sending is asynchronous, an error dialog will appear wherever the user is if necessary Closes #245
This commit is contained in:
parent
6825d8ac2b
commit
198d6d4c56
85 changed files with 1668 additions and 69 deletions
|
|
@ -57,6 +57,7 @@ fun <T> SearchBar(
|
|||
placeHolderTitle: String,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
showBackButton: Boolean = true,
|
||||
resultState: SearchBarResultState<T> = SearchBarResultState.NotSearching(),
|
||||
shape: Shape = SearchBarDefaults.inputFieldShape,
|
||||
tonalElevation: Dp = SearchBarDefaults.Elevation,
|
||||
|
|
@ -87,7 +88,7 @@ fun <T> SearchBar(
|
|||
modifier = Modifier.alpha(0.4f), // FIXME align on Design system theme (removing alpha should be fine)
|
||||
)
|
||||
},
|
||||
leadingIcon = if (active) {
|
||||
leadingIcon = if (showBackButton && active) {
|
||||
{ BackButton(onClick = { onActiveChange(false) }) }
|
||||
} else {
|
||||
null
|
||||
|
|
@ -179,6 +180,16 @@ internal fun SearchBarPreviewActiveWithQuery() = ElementThemedPreview {
|
|||
)
|
||||
}
|
||||
|
||||
@Preview(group = PreviewGroup.Search)
|
||||
@Composable
|
||||
internal fun SearchBarPreviewActiveWithQueryNoBackButton() = ElementThemedPreview {
|
||||
ContentToPreview(
|
||||
query = "search term",
|
||||
active = true,
|
||||
showBackButton = false,
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(group = PreviewGroup.Search)
|
||||
@Composable
|
||||
internal fun SearchBarPreviewActiveWithNoResults() = ElementThemedPreview {
|
||||
|
|
@ -212,6 +223,7 @@ internal fun SearchBarPreviewActiveWithContent() = ElementThemedPreview {
|
|||
private fun ContentToPreview(
|
||||
query: String = "",
|
||||
active: Boolean = false,
|
||||
showBackButton: Boolean = true,
|
||||
resultState: SearchBarResultState<String> = SearchBarResultState.NotSearching(),
|
||||
contentPrefix: @Composable ColumnScope.() -> Unit = {},
|
||||
contentSuffix: @Composable ColumnScope.() -> Unit = {},
|
||||
|
|
@ -221,6 +233,7 @@ private fun ContentToPreview(
|
|||
query = query,
|
||||
active = active,
|
||||
resultState = resultState,
|
||||
showBackButton = showBackButton,
|
||||
onQueryChange = {},
|
||||
onActiveChange = {},
|
||||
placeHolderTitle = "Search for things",
|
||||
|
|
|
|||
|
|
@ -85,4 +85,8 @@ interface MatrixRoom : Closeable {
|
|||
suspend fun acceptInvitation(): Result<Unit>
|
||||
|
||||
suspend fun rejectInvitation(): Result<Unit>
|
||||
|
||||
suspend fun inviteUserById(id: UserId): Result<Unit>
|
||||
|
||||
suspend fun canInvite(): Result<Boolean>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ import kotlinx.coroutines.flow.map
|
|||
import kotlinx.coroutines.flow.onStart
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.matrix.rustcomponents.sdk.Room
|
||||
import org.matrix.rustcomponents.sdk.RoomMember
|
||||
import org.matrix.rustcomponents.sdk.SlidingSyncRoom
|
||||
import org.matrix.rustcomponents.sdk.UpdateSummary
|
||||
import org.matrix.rustcomponents.sdk.genTransactionId
|
||||
|
|
@ -209,6 +210,18 @@ class RustMatrixRoom(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun inviteUserById(id: UserId): Result<Unit> = withContext(coroutineDispatchers.io) {
|
||||
runCatching {
|
||||
innerRoom.inviteUserById(id.value)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun canInvite(): Result<Boolean> = withContext(coroutineDispatchers.io) {
|
||||
runCatching {
|
||||
innerRoom.member(sessionId.value).use(RoomMember::canInvite)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun sendImage(file: File, thumbnailFile: File, imageInfo: ImageInfo): Result<Unit> {
|
||||
return runCatching {
|
||||
innerRoom.sendImage(file.path, thumbnailFile.path, imageInfo.map())
|
||||
|
|
|
|||
|
|
@ -152,6 +152,7 @@ class RustMatrixTimeline(
|
|||
RequiredState(key = "m.room.canonical_alias", value = ""),
|
||||
RequiredState(key = "m.room.topic", value = ""),
|
||||
RequiredState(key = "m.room.join_rules", value = ""),
|
||||
RequiredState(key = "m.room.power_levels", value = ""),
|
||||
),
|
||||
timelineLimit = null
|
||||
)
|
||||
|
|
|
|||
|
|
@ -59,6 +59,8 @@ class FakeMatrixRoom(
|
|||
private var updateMembersResult: Result<Unit> = Result.success(Unit)
|
||||
private var acceptInviteResult = Result.success(Unit)
|
||||
private var rejectInviteResult = Result.success(Unit)
|
||||
private var inviteUserResult = Result.success(Unit)
|
||||
private var canInviteResult = Result.success(true)
|
||||
private var sendMediaResult = Result.success(Unit)
|
||||
var sendMediaCount = 0
|
||||
private set
|
||||
|
|
@ -69,6 +71,9 @@ class FakeMatrixRoom(
|
|||
var isInviteRejected: Boolean = false
|
||||
private set
|
||||
|
||||
var invitedUserId: UserId? = null
|
||||
private set
|
||||
|
||||
private var leaveRoomError: Throwable? = null
|
||||
|
||||
override val membersStateFlow: MutableStateFlow<MatrixRoomMembersState> = MutableStateFlow(MatrixRoomMembersState.Unknown)
|
||||
|
|
@ -136,6 +141,15 @@ class FakeMatrixRoom(
|
|||
return rejectInviteResult
|
||||
}
|
||||
|
||||
override suspend fun inviteUserById(id: UserId): Result<Unit> {
|
||||
invitedUserId = id
|
||||
return inviteUserResult
|
||||
}
|
||||
|
||||
override suspend fun canInvite(): Result<Boolean> {
|
||||
return canInviteResult
|
||||
}
|
||||
|
||||
override suspend fun sendImage(file: File, thumbnailFile: File, imageInfo: ImageInfo): Result<Unit> = sendMediaResult.also { sendMediaCount++ }
|
||||
|
||||
override suspend fun sendVideo(file: File, thumbnailFile: File, videoInfo: VideoInfo): Result<Unit> = sendMediaResult.also { sendMediaCount++ }
|
||||
|
|
@ -174,6 +188,14 @@ class FakeMatrixRoom(
|
|||
rejectInviteResult = result
|
||||
}
|
||||
|
||||
fun givenInviteUserResult(result: Result<Unit>) {
|
||||
inviteUserResult = result
|
||||
}
|
||||
|
||||
fun givenCanInviteResult(result: Result<Boolean>) {
|
||||
canInviteResult = result
|
||||
}
|
||||
|
||||
fun givenIgnoreResult(result: Result<Unit>) {
|
||||
ignoreResult = result
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@
|
|||
<string name="common_file">"File"</string>
|
||||
<string name="common_gif">"GIF"</string>
|
||||
<string name="common_image">"Image"</string>
|
||||
<string name="common_leaving_room">"Leaving room"</string>
|
||||
<string name="common_link_copied_to_clipboard">"Link copied to clipboard"</string>
|
||||
<string name="common_loading">"Loading…"</string>
|
||||
<string name="common_message">"Message"</string>
|
||||
|
|
@ -98,6 +99,8 @@
|
|||
<string name="common_suggestions">"Suggestions"</string>
|
||||
<string name="common_topic">"Topic"</string>
|
||||
<string name="common_unable_to_decrypt">"Unable to decrypt"</string>
|
||||
<string name="common_unable_to_invite_message">"We were unable to successfully send invites to one or more users."</string>
|
||||
<string name="common_unable_to_invite_title">"Unable to send invite(s)"</string>
|
||||
<string name="common_unsupported_event">"Unsupported event"</string>
|
||||
<string name="common_username">"Username"</string>
|
||||
<string name="common_verification_cancelled">"Verification cancelled"</string>
|
||||
|
|
@ -162,4 +165,4 @@
|
|||
<string name="screen_analytics_settings_read_terms">"You can read all our terms %1$s."</string>
|
||||
<string name="screen_analytics_settings_read_terms_content_link">"here"</string>
|
||||
<string name="screen_report_content_block_user">"Block user"</string>
|
||||
</resources>
|
||||
</resources>
|
||||
Loading…
Add table
Add a link
Reference in a new issue