Merge branch 'vector-im:develop' into develop

This commit is contained in:
Freezlex 2023-05-22 10:26:58 +02:00 committed by GitHub
commit cb778da9d0
115 changed files with 1076 additions and 409 deletions

View file

@ -45,7 +45,6 @@ import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TopAppBar
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun PreferenceView(
title: String,

View file

@ -14,8 +14,6 @@
* limitations under the License.
*/
@file:OptIn(ExperimentalMaterial3Api::class)
package io.element.android.libraries.designsystem.theme.components
import androidx.compose.foundation.layout.RowScope

View file

@ -14,8 +14,6 @@
* limitations under the License.
*/
@file:OptIn(ExperimentalMaterial3Api::class)
package io.element.android.libraries.designsystem.theme.components
import androidx.compose.foundation.layout.RowScope
@ -57,6 +55,7 @@ fun MediumTopAppBar(
internal fun MediumTopAppBarPreview() =
ElementThemedPreview { ContentToPreview() }
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun ContentToPreview() {
MediumTopAppBar(title = { Text(text = "Title") })

View file

@ -14,8 +14,6 @@
* limitations under the License.
*/
@file:OptIn(ExperimentalMaterialApi::class, ExperimentalMaterialApi::class)
package io.element.android.libraries.designsystem.theme.components
import androidx.compose.foundation.background
@ -107,6 +105,7 @@ internal fun ModalBottomSheetLayoutLightPreview() =
internal fun ModalBottomSheetLayoutDarkPreview() =
ElementPreviewDark { ContentToPreview() }
@OptIn(ExperimentalMaterialApi::class)
@Composable
private fun ContentToPreview() {
ModalBottomSheetLayout(

View file

@ -23,10 +23,9 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.OutlinedTextFieldDefaults
import androidx.compose.material3.TextFieldColors
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.ExperimentalComposeUiApi
@ -49,7 +48,6 @@ import io.element.android.libraries.designsystem.preview.PreviewGroup
import io.element.android.libraries.designsystem.utils.allBooleans
import io.element.android.libraries.designsystem.utils.asInt
@OptIn(ExperimentalMaterial3Api::class, ExperimentalComposeUiApi::class)
@Composable
fun OutlinedTextField(
value: String,
@ -70,8 +68,8 @@ fun OutlinedTextField(
singleLine: Boolean = false,
maxLines: Int = Int.MAX_VALUE,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
shape: Shape = TextFieldDefaults.outlinedShape,
colors: TextFieldColors = TextFieldDefaults.outlinedTextFieldColors()
shape: Shape = OutlinedTextFieldDefaults.shape,
colors: TextFieldColors = OutlinedTextFieldDefaults.colors()
) {
androidx.compose.material3.OutlinedTextField(
value = value,

View file

@ -18,7 +18,6 @@ package io.element.android.libraries.designsystem.theme.components
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FabPosition
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ScaffoldDefaults
@ -27,7 +26,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun Scaffold(
modifier: Modifier = Modifier,

View file

@ -14,8 +14,6 @@
* limitations under the License.
*/
@file:OptIn(ExperimentalMaterial3Api::class)
package io.element.android.libraries.designsystem.theme.components
import androidx.compose.foundation.interaction.MutableInteractionSource
@ -75,8 +73,9 @@ fun SearchBar(
@Preview(group = PreviewGroup.Search)
@Composable
internal fun DockedSearchBarPreview() = ElementThemedPreview { ContentToPreview() }
internal fun SearchBarPreview() = ElementThemedPreview { ContentToPreview() }
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun ContentToPreview() {
SearchBar(

View file

@ -23,7 +23,6 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.TextFieldColors
import androidx.compose.material3.TextFieldDefaults
@ -50,7 +49,6 @@ import io.element.android.libraries.designsystem.preview.PreviewGroup
import io.element.android.libraries.designsystem.utils.allBooleans
import io.element.android.libraries.designsystem.utils.asInt
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TextField(
value: String,
@ -71,8 +69,8 @@ fun TextField(
singleLine: Boolean = false,
maxLines: Int = Int.MAX_VALUE,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
shape: Shape = TextFieldDefaults.filledShape,
colors: TextFieldColors = TextFieldDefaults.textFieldColors()
shape: Shape = TextFieldDefaults.shape,
colors: TextFieldColors = TextFieldDefaults.colors()
) {
androidx.compose.material3.TextField(
value = value,

View file

@ -14,8 +14,6 @@
* limitations under the License.
*/
@file:OptIn(ExperimentalMaterial3Api::class)
package io.element.android.libraries.designsystem.theme.components
import androidx.compose.foundation.layout.RowScope

View file

@ -42,6 +42,7 @@ interface MatrixClient : Closeable {
suspend fun createRoom(createRoomParams: CreateRoomParameters): Result<RoomId>
suspend fun createDM(userId: UserId): Result<RoomId>
suspend fun getProfile(userId: UserId): Result<MatrixUser>
suspend fun searchUsers(searchTerm: String, limit: Long): Result<MatrixSearchUserResults>
fun startSync()
fun stopSync()
fun mediaResolver(): MediaResolver
@ -58,9 +59,10 @@ interface MatrixClient : Closeable {
height: Long
): Result<ByteArray>
suspend fun uploadMedia(mimeType: String, data: ByteArray): Result<String>
fun onSlidingSyncUpdate()
fun roomMembershipObserver(): RoomMembershipObserver
suspend fun searchUsers(searchTerm: String, limit: Long): Result<MatrixSearchUserResults>
}

View file

@ -17,7 +17,6 @@
package io.element.android.libraries.matrix.api.room
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.user.MatrixUser
data class RoomMember(
val userId: UserId,
@ -30,12 +29,6 @@ data class RoomMember(
val isIgnored: Boolean,
)
fun RoomMember.toMatrixUser() = MatrixUser(
userId = userId,
displayName = displayName,
avatarUrl = avatarUrl,
)
enum class RoomMembershipState {
BAN, INVITE, JOIN, KNOCK, LEAVE
}

View file

@ -14,8 +14,6 @@
* limitations under the License.
*/
@file:OptIn(ExperimentalCoroutinesApi::class)
package io.element.android.libraries.matrix.impl
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
@ -46,7 +44,6 @@ import io.element.android.libraries.matrix.impl.verification.RustSessionVerifica
import io.element.android.libraries.sessionstorage.api.SessionStore
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.filter
@ -282,6 +279,13 @@ class RustMatrixClient constructor(
}
}
override suspend fun searchUsers(searchTerm: String, limit: Long): Result<MatrixSearchUserResults> =
withContext(dispatchers.io) {
runCatching {
client.searchUsers(searchTerm, limit.toULong()).let(UserSearchResultMapper::map)
}
}
override fun mediaResolver(): MediaResolver = mediaResolver
override fun sessionVerificationService(): SessionVerificationService = verificationService
@ -369,6 +373,13 @@ class RustMatrixClient constructor(
}
}
@OptIn(ExperimentalUnsignedTypes::class)
override suspend fun uploadMedia(mimeType: String, data: ByteArray): Result<String> = withContext(dispatchers.io) {
runCatching {
client.uploadMedia(mimeType, data.toUByteArray().toList())
}
}
override fun onSlidingSyncUpdate() {
if (!verificationService.isReady.value) {
try {
@ -381,13 +392,6 @@ class RustMatrixClient constructor(
override fun roomMembershipObserver(): RoomMembershipObserver = roomMembershipObserver
override suspend fun searchUsers(searchTerm: String, limit: Long): Result<MatrixSearchUserResults> =
withContext(dispatchers.io) {
runCatching {
client.searchUsers(searchTerm, limit.toULong()).let(UserSearchResultMapper::map)
}
}
private fun File.deleteSessionDirectory(userID: String): Boolean {
// Rust sanitises the user ID replacing invalid characters with an _
val sanitisedUserID = userID.replace(":", "_")

View file

@ -16,6 +16,7 @@
package io.element.android.libraries.matrix.impl.notification
import io.element.android.libraries.core.bool.orFalse
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.UserId
@ -36,7 +37,7 @@ class NotificationMapper @Inject constructor() {
senderDisplayName = it.senderDisplayName,
roomAvatarUrl = it.roomAvatarUrl,
isDirect = it.isDirect,
isEncrypted = it.isEncrypted,
isEncrypted = it.isEncrypted.orFalse(),
isNoisy = it.isNoisy
)
}

View file

@ -91,7 +91,6 @@ internal class RustRoomSummaryDataSource(
coroutineScope.cancel()
}
//@OptIn(FlowPreview::class)
override fun roomSummaries(): StateFlow<List<RoomSummary>> {
return roomSummaries
}

View file

@ -59,6 +59,7 @@ class FakeMatrixClient(
private val getRoomResults = mutableMapOf<RoomId, MatrixRoom>()
private val searchUserResults = mutableMapOf<String, Result<MatrixSearchUserResults>>()
private val getProfileResults = mutableMapOf<UserId, Result<MatrixUser>>()
private var uploadMediaResult: Result<String> = Result.success(AN_AVATAR_URL)
override fun getRoom(roomId: RoomId): MatrixRoom? {
return getRoomResults[roomId]
@ -91,6 +92,10 @@ class FakeMatrixClient(
return getProfileResults[userId] ?: Result.failure(IllegalStateException("No profile found for $userId"))
}
override suspend fun searchUsers(searchTerm: String, limit: Long): Result<MatrixSearchUserResults> {
return searchUserResults[searchTerm] ?: Result.failure(IllegalStateException("No response defined for $searchTerm"))
}
override fun startSync() = Unit
override fun stopSync() = Unit
@ -122,6 +127,10 @@ class FakeMatrixClient(
return Result.success(ByteArray(0))
}
override suspend fun uploadMedia(mimeType: String, data: ByteArray): Result<String> {
return uploadMediaResult
}
override fun sessionVerificationService(): SessionVerificationService = sessionVerificationService
override fun pushersService(): PushersService = pushersService
@ -134,10 +143,6 @@ class FakeMatrixClient(
return RoomMembershipObserver()
}
override suspend fun searchUsers(searchTerm: String, limit: Long): Result<MatrixSearchUserResults> {
return searchUserResults[searchTerm] ?: Result.failure(IllegalStateException("No response defined for $searchTerm"))
}
// Mocks
fun givenLogoutError(failure: Throwable?) {
@ -179,4 +184,8 @@ class FakeMatrixClient(
fun givenGetProfileResult(userId: UserId, result: Result<MatrixUser>) {
getProfileResults[userId] = result
}
fun givenUploadMediaResult(result: Result<String>) {
uploadMediaResult = result
}
}

View file

@ -27,6 +27,11 @@ interface PickerProvider {
onResult: (uri: Uri?, mimeType: String?) -> Unit
): PickerLauncher<PickVisualMediaRequest, Uri?>
@Composable
fun registerGalleryImagePicker(
onResult: (Uri?) -> Unit
): PickerLauncher<PickVisualMediaRequest, Uri?>
@Composable
fun registerFilePicker(
mimeType: String,
@ -38,5 +43,4 @@ interface PickerProvider {
@Composable
fun registerCameraVideoPicker(onResult: (Uri?) -> Unit): PickerLauncher<Uri, Boolean>
}

View file

@ -26,6 +26,13 @@ sealed interface PickerType<Input, Output> {
fun getContract(): ActivityResultContract<Input, Output>
fun getDefaultRequest(): Input
object Image : PickerType<PickVisualMediaRequest, Uri?> {
override fun getContract() = ActivityResultContracts.PickVisualMedia()
override fun getDefaultRequest(): PickVisualMediaRequest {
return PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly)
}
}
object ImageAndVideo : PickerType<PickVisualMediaRequest, Uri?> {
override fun getContract() = ActivityResultContracts.PickVisualMedia()
override fun getDefaultRequest(): PickVisualMediaRequest {

View file

@ -59,6 +59,20 @@ class PickerProviderImpl constructor(private val isInTest: Boolean) : PickerProv
}
}
/**
* Remembers and returns a [PickerLauncher] for a gallery picture.
* [onResult] will be called with either the selected file's [Uri] or `null` if nothing was selected.
*/
@Composable
override fun registerGalleryImagePicker(onResult: (Uri?) -> Unit): PickerLauncher<PickVisualMediaRequest, Uri?> {
// Tests and UI preview can't handle Contexts, so we might as well disable the whole picker
return if (LocalInspectionMode.current || isInTest) {
NoOpPickerLauncher { onResult(null) }
} else {
rememberPickerLauncher(type = PickerType.Image) { uri -> onResult(uri) }
}
}
/**
* Remembers and returns a [PickerLauncher] for a gallery item, either a picture or a video.
* [onResult] will be called with either the selected file's [Uri] or `null` if nothing was selected.

View file

@ -33,6 +33,11 @@ class FakePickerProvider : PickerProvider {
return NoOpPickerLauncher { onResult(result, mimeType) }
}
@Composable
override fun registerGalleryImagePicker(onResult: (uri: Uri?) -> Unit): PickerLauncher<PickVisualMediaRequest, Uri?> {
return NoOpPickerLauncher { onResult(result) }
}
@Composable
override fun registerFilePicker(mimeType: String, onResult: (Uri?) -> Unit): PickerLauncher<String, Uri?> {
return NoOpPickerLauncher { onResult(result) }

View file

@ -63,6 +63,7 @@
<string name="common_developer_options">"Developer options"</string>
<string name="common_edited_suffix">"(edited)"</string>
<string name="common_editing">"Editing"</string>
<string name="common_emote">"* %1$s %2$s"</string>
<string name="common_encryption_enabled">"Encryption enabled"</string>
<string name="common_error">"Error"</string>
<string name="common_file">"File"</string>
@ -84,12 +85,14 @@
<string name="common_report_a_bug">"Report a bug"</string>
<string name="common_report_submitted">"Report submitted"</string>
<string name="common_search_for_someone">"Search for someone"</string>
<string name="common_search_results">"Search results"</string>
<string name="common_security">"Security"</string>
<string name="common_select_your_server">"Select your server"</string>
<string name="common_sending">"Sending…"</string>
<string name="common_server_not_supported">"Server not supported"</string>
<string name="common_server_url">"Server URL"</string>
<string name="common_settings">"Settings"</string>
<string name="common_starting_chat">"Starting chat…"</string>
<string name="common_sticker">"Sticker"</string>
<string name="common_success">"Success"</string>
<string name="common_suggestions">"Suggestions"</string>
@ -159,4 +162,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>