Merge branch 'develop' into feature/bma/removeOldResources
This commit is contained in:
commit
aadc6d68d8
85 changed files with 562 additions and 321 deletions
|
|
@ -26,4 +26,8 @@ dependencies {
|
|||
api(libs.dagger)
|
||||
api(libs.appyx.core)
|
||||
api(libs.androidx.lifecycle.runtime)
|
||||
|
||||
testImplementation(libs.test.junit)
|
||||
testImplementation(libs.coroutines.test)
|
||||
testImplementation(libs.test.truth)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,51 +18,152 @@ package io.element.android.libraries.architecture
|
|||
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.Stable
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.InvocationKind
|
||||
import kotlin.contracts.contract
|
||||
|
||||
/**
|
||||
* Sealed type that allows to model an asynchronous operation.
|
||||
*/
|
||||
@Stable
|
||||
sealed interface Async<out T> {
|
||||
|
||||
/**
|
||||
* Represents a failed operation.
|
||||
*
|
||||
* @param T the type of data returned by the operation.
|
||||
* @property error the error that caused the operation to fail.
|
||||
* @property prevData the data returned by a previous successful run of the operation if any.
|
||||
*/
|
||||
data class Failure<out T>(
|
||||
val error: Throwable,
|
||||
val prevData: T? = null,
|
||||
) : Async<T>
|
||||
|
||||
/**
|
||||
* Represents an operation that is currently ongoing.
|
||||
*
|
||||
* @param T the type of data returned by the operation.
|
||||
* @property prevData the data returned by a previous successful run of the operation if any.
|
||||
*/
|
||||
data class Loading<out T>(
|
||||
val prevData: T? = null,
|
||||
) : Async<T>
|
||||
|
||||
/**
|
||||
* Represents a successful operation.
|
||||
*
|
||||
* @param T the type of data returned by the operation.
|
||||
* @property data the data returned by the operation.
|
||||
*/
|
||||
data class Success<out T>(
|
||||
val data: T,
|
||||
) : Async<T>
|
||||
|
||||
/**
|
||||
* Represents an uninitialized operation (i.e. yet to be run).
|
||||
*/
|
||||
object Uninitialized : Async<Nothing>
|
||||
data class Loading<out T>(val prevState: T? = null) : Async<T>
|
||||
data class Failure<out T>(val error: Throwable, val prevState: T? = null) : Async<T>
|
||||
data class Success<out T>(val state: T) : Async<T>
|
||||
|
||||
fun dataOrNull(): T? {
|
||||
return when (this) {
|
||||
is Failure -> prevState
|
||||
is Loading -> prevState
|
||||
is Success -> state
|
||||
Uninitialized -> null
|
||||
/**
|
||||
* Returns the data returned by the operation, or null otherwise.
|
||||
*
|
||||
* Please note this method may return stale data if the operation is not [Success].
|
||||
*/
|
||||
fun dataOrNull(): T? = when (this) {
|
||||
is Failure -> prevData
|
||||
is Loading -> prevData
|
||||
is Success -> data
|
||||
Uninitialized -> null
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the error that caused the operation to fail, or null otherwise.
|
||||
*/
|
||||
fun errorOrNull(): Throwable? = when (this) {
|
||||
is Failure -> error
|
||||
else -> null
|
||||
}
|
||||
|
||||
fun isFailure(): Boolean = this is Failure<T>
|
||||
|
||||
fun isLoading(): Boolean = this is Loading<T>
|
||||
|
||||
fun isSuccess(): Boolean = this is Success<T>
|
||||
|
||||
fun isUninitialized(): Boolean = this == Uninitialized
|
||||
}
|
||||
|
||||
suspend inline fun <T> MutableState<Async<T>>.runCatchingUpdatingState(
|
||||
errorTransform: (Throwable) -> Throwable = { it },
|
||||
block: () -> T,
|
||||
): Result<T> = runUpdatingState(
|
||||
state = this,
|
||||
errorTransform = errorTransform,
|
||||
resultBlock = {
|
||||
runCatching {
|
||||
block()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
suspend inline fun <T> (suspend () -> T).execute(
|
||||
suspend inline fun <T> (suspend () -> T).runCatchingUpdatingState(
|
||||
state: MutableState<Async<T>>,
|
||||
errorMapping: ((Throwable) -> Throwable) = { it },
|
||||
) {
|
||||
try {
|
||||
state.value = Async.Loading()
|
||||
val result = this()
|
||||
state.value = Async.Success(result)
|
||||
} catch (error: Throwable) {
|
||||
state.value = Async.Failure(errorMapping.invoke(error))
|
||||
}
|
||||
}
|
||||
errorTransform: (Throwable) -> Throwable = { it },
|
||||
): Result<T> = runUpdatingState(
|
||||
state = state,
|
||||
errorTransform = errorTransform,
|
||||
resultBlock = {
|
||||
runCatching {
|
||||
this()
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
suspend inline fun <T> (suspend () -> Result<T>).executeResult(state: MutableState<Async<T>>) {
|
||||
if (state.value !is Async.Success) {
|
||||
state.value = Async.Loading()
|
||||
suspend inline fun <T> MutableState<Async<T>>.runUpdatingState(
|
||||
errorTransform: (Throwable) -> Throwable = { it },
|
||||
resultBlock: () -> Result<T>,
|
||||
): Result<T> = runUpdatingState(
|
||||
state = this,
|
||||
errorTransform = errorTransform,
|
||||
resultBlock = resultBlock,
|
||||
)
|
||||
|
||||
/**
|
||||
* Calls the specified [Result]-returning function [resultBlock]
|
||||
* encapsulating its progress and return value into an [Async] while
|
||||
* posting its updates to the MutableState [state].
|
||||
*
|
||||
* @param T the type of data returned by the operation.
|
||||
* @param state the [MutableState] to post updates to.
|
||||
* @param errorTransform a function to transform the error before posting it.
|
||||
* @param resultBlock a suspending function that returns a [Result].
|
||||
* @return the [Result] returned by [resultBlock].
|
||||
*/
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
@Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE")
|
||||
suspend inline fun <T> runUpdatingState(
|
||||
state: MutableState<Async<T>>,
|
||||
errorTransform: (Throwable) -> Throwable = { it },
|
||||
resultBlock: suspend () -> Result<T>,
|
||||
): Result<T> {
|
||||
contract {
|
||||
callsInPlace(resultBlock, InvocationKind.EXACTLY_ONCE)
|
||||
}
|
||||
this().fold(
|
||||
val prevData = state.value.dataOrNull()
|
||||
state.value = Async.Loading(prevData = prevData)
|
||||
return resultBlock().fold(
|
||||
onSuccess = {
|
||||
state.value = Async.Success(it)
|
||||
Result.success(it)
|
||||
},
|
||||
onFailure = {
|
||||
state.value = Async.Failure(it)
|
||||
val error = errorTransform(it)
|
||||
state.value = Async.Failure(
|
||||
error = error,
|
||||
prevData = prevData,
|
||||
)
|
||||
Result.failure(error)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun <T> Async<T>.isLoading(): Boolean {
|
||||
return this is Async.Loading<T>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.architecture
|
||||
|
||||
import androidx.compose.runtime.MutableState
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
||||
class AsyncKtTest {
|
||||
@Test
|
||||
fun `updates state when block returns success`() = runTest {
|
||||
val state = TestableMutableState<Async<Int>>(Async.Uninitialized)
|
||||
|
||||
val result = runUpdatingState(state) {
|
||||
delay(1)
|
||||
Result.success(1)
|
||||
}
|
||||
|
||||
assertThat(result.isSuccess).isTrue()
|
||||
assertThat(result.getOrNull()).isEqualTo(1)
|
||||
|
||||
assertThat(state.popFirst()).isEqualTo(Async.Uninitialized)
|
||||
assertThat(state.popFirst()).isEqualTo(Async.Loading(null))
|
||||
assertThat(state.popFirst()).isEqualTo(Async.Success(1))
|
||||
state.assertNoMoreValues()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `updates state when block returns failure`() = runTest {
|
||||
val state = TestableMutableState<Async<Int>>(Async.Uninitialized)
|
||||
|
||||
val result = runUpdatingState(state) {
|
||||
delay(1)
|
||||
Result.failure(MyThrowable("hello"))
|
||||
}
|
||||
|
||||
assertThat(result.isFailure).isTrue()
|
||||
assertThat(result.exceptionOrNull()).isEqualTo(MyThrowable("hello"))
|
||||
|
||||
assertThat(state.popFirst()).isEqualTo(Async.Uninitialized)
|
||||
assertThat(state.popFirst()).isEqualTo(Async.Loading(null))
|
||||
assertThat(state.popFirst()).isEqualTo(Async.Failure<Int>(MyThrowable("hello")))
|
||||
state.assertNoMoreValues()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `updates state when block returns failure transforming the error`() = runTest {
|
||||
val state = TestableMutableState<Async<Int>>(Async.Uninitialized)
|
||||
|
||||
val result = runUpdatingState(state, { MyThrowable(it.message + " world") }) {
|
||||
delay(1)
|
||||
Result.failure(MyThrowable("hello"))
|
||||
}
|
||||
|
||||
assertThat(result.isFailure).isTrue()
|
||||
assertThat(result.exceptionOrNull()).isEqualTo(MyThrowable("hello world"))
|
||||
|
||||
assertThat(state.popFirst()).isEqualTo(Async.Uninitialized)
|
||||
assertThat(state.popFirst()).isEqualTo(Async.Loading(null))
|
||||
assertThat(state.popFirst()).isEqualTo(Async.Failure<Int>(MyThrowable("hello world")))
|
||||
state.assertNoMoreValues()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A fake [MutableState] that allows to record all the states that were set.
|
||||
*/
|
||||
private class TestableMutableState<T>(
|
||||
value: T
|
||||
) : MutableState<T> {
|
||||
|
||||
private val _deque = ArrayDeque<T>(listOf(value))
|
||||
|
||||
override var value: T
|
||||
get() = _deque.last()
|
||||
set(value) {
|
||||
_deque.addLast(value)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the states that were set in the order they were set.
|
||||
*/
|
||||
fun popFirst(): T = _deque.removeFirst()
|
||||
|
||||
fun assertNoMoreValues() {
|
||||
assertThat(_deque).isEmpty()
|
||||
}
|
||||
|
||||
override operator fun component1(): T = value
|
||||
|
||||
override operator fun component2(): (T) -> Unit = { value = it }
|
||||
}
|
||||
|
||||
/**
|
||||
* An exception that is also a data class so we can compare it using equals.
|
||||
*/
|
||||
private data class MyThrowable(val myMessage: String) : Throwable(myMessage)
|
||||
|
|
@ -31,7 +31,7 @@ import io.element.android.libraries.designsystem.preview.ElementPreviewDark
|
|||
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
|
||||
import io.element.android.libraries.designsystem.theme.components.Button
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.ui.strings.R as StringR
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
||||
@Composable
|
||||
fun AsyncFailure(
|
||||
|
|
@ -45,11 +45,11 @@ fun AsyncFailure(
|
|||
.padding(vertical = 32.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Text(text = throwable.message ?: stringResource(id = StringR.string.error_unknown))
|
||||
Text(text = throwable.message ?: stringResource(id = CommonStrings.error_unknown))
|
||||
if (onRetry != null) {
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Button(onClick = onRetry) {
|
||||
Text(text = stringResource(id = StringR.string.action_retry))
|
||||
Text(text = stringResource(id = CommonStrings.action_retry))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,14 +28,14 @@ import io.element.android.libraries.designsystem.preview.ElementThemedPreview
|
|||
import io.element.android.libraries.designsystem.preview.PreviewGroup
|
||||
import io.element.android.libraries.designsystem.theme.components.Icon
|
||||
import io.element.android.libraries.designsystem.theme.components.IconButton
|
||||
import io.element.android.libraries.ui.strings.R as StringR
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
||||
@Composable
|
||||
fun BackButton(
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
imageVector: ImageVector = Icons.Default.ArrowBack,
|
||||
contentDescription: String = stringResource(StringR.string.action_back),
|
||||
contentDescription: String = stringResource(CommonStrings.action_back),
|
||||
enabled: Boolean = true,
|
||||
) {
|
||||
IconButton(
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ import androidx.compose.ui.unit.Dp
|
|||
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewGroup
|
||||
import io.element.android.libraries.designsystem.utils.BooleanProvider
|
||||
import io.element.android.libraries.ui.strings.R as StringR
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
|
|
@ -41,8 +41,8 @@ fun ConfirmationDialog(
|
|||
onDismiss: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
title: String? = null,
|
||||
submitText: String = stringResource(id = StringR.string.action_ok),
|
||||
cancelText: String = stringResource(id = StringR.string.action_cancel),
|
||||
submitText: String = stringResource(id = CommonStrings.action_ok),
|
||||
cancelText: String = stringResource(id = CommonStrings.action_cancel),
|
||||
thirdButtonText: String? = null,
|
||||
emphasizeSubmitButton: Boolean = false,
|
||||
onCancelClicked: () -> Unit = onDismiss,
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ import androidx.compose.ui.tooling.preview.Preview
|
|||
import androidx.compose.ui.unit.Dp
|
||||
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewGroup
|
||||
import io.element.android.libraries.ui.strings.R as StringR
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
|
|
@ -91,8 +91,8 @@ private fun ErrorDialogContent(
|
|||
}
|
||||
|
||||
object ErrorDialogDefaults {
|
||||
val title: String @Composable get() = stringResource(id = StringR.string.dialog_title_error)
|
||||
val submitText: String @Composable get() = stringResource(id = StringR.string.action_ok)
|
||||
val title: String @Composable get() = stringResource(id = CommonStrings.dialog_title_error)
|
||||
val submitText: String @Composable get() = stringResource(id = CommonStrings.action_ok)
|
||||
}
|
||||
|
||||
@Preview(group = PreviewGroup.Dialogs)
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ import androidx.compose.ui.unit.Dp
|
|||
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewGroup
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.ui.strings.R as StringR
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
||||
@Composable
|
||||
fun RetryDialog(
|
||||
|
|
@ -109,9 +109,9 @@ private fun RetryDialogContent(
|
|||
}
|
||||
|
||||
object RetryDialogDefaults {
|
||||
val title: String @Composable get() = stringResource(id = StringR.string.dialog_title_error)
|
||||
val retryText: String @Composable get() = stringResource(id = StringR.string.action_retry)
|
||||
val dismissText: String @Composable get() = stringResource(id = StringR.string.action_cancel)
|
||||
val title: String @Composable get() = stringResource(id = CommonStrings.dialog_title_error)
|
||||
val retryText: String @Composable get() = stringResource(id = CommonStrings.action_retry)
|
||||
val dismissText: String @Composable get() = stringResource(id = CommonStrings.action_cancel)
|
||||
}
|
||||
|
||||
@Preview(group = PreviewGroup.Dialogs)
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ import io.element.android.libraries.designsystem.components.button.BackButton
|
|||
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewGroup
|
||||
import io.element.android.libraries.designsystem.theme.LocalColors
|
||||
import io.element.android.libraries.ui.strings.R
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
|
|
@ -100,7 +100,7 @@ fun <T> SearchBar(
|
|||
IconButton(onClick = { onQueryChange("") }) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Close,
|
||||
contentDescription = stringResource(R.string.action_clear),
|
||||
contentDescription = stringResource(CommonStrings.action_clear),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -110,7 +110,7 @@ fun <T> SearchBar(
|
|||
{
|
||||
Icon(
|
||||
imageVector = Icons.Default.Search,
|
||||
contentDescription = stringResource(R.string.action_search),
|
||||
contentDescription = stringResource(CommonStrings.action_search),
|
||||
tint = MaterialTheme.colorScheme.tertiary,
|
||||
)
|
||||
}
|
||||
|
|
@ -135,7 +135,7 @@ fun <T> SearchBar(
|
|||
Spacer(Modifier.size(80.dp))
|
||||
|
||||
Text(
|
||||
text = stringResource(R.string.common_no_results),
|
||||
text = stringResource(CommonStrings.common_no_results),
|
||||
textAlign = TextAlign.Center,
|
||||
color = MaterialTheme.colorScheme.tertiary,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
|
|
|
|||
|
|
@ -47,9 +47,9 @@ import io.element.android.libraries.matrix.api.timeline.item.event.UnableToDecry
|
|||
import io.element.android.libraries.matrix.api.timeline.item.event.UnknownContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.UnknownMessageType
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.VideoMessageType
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import io.element.android.services.toolbox.api.strings.StringProvider
|
||||
import javax.inject.Inject
|
||||
import io.element.android.libraries.ui.strings.R as StringR
|
||||
|
||||
@ContributesBinding(SessionScope::class)
|
||||
class DefaultRoomLastMessageFormatter @Inject constructor(
|
||||
|
|
@ -66,7 +66,7 @@ class DefaultRoomLastMessageFormatter @Inject constructor(
|
|||
return when (val content = event.content) {
|
||||
is MessageContent -> processMessageContents(content, senderDisplayName, isDmRoom)
|
||||
RedactedContent -> {
|
||||
val message = sp.getString(StringR.string.common_message_removed)
|
||||
val message = sp.getString(CommonStrings.common_message_removed)
|
||||
if (!isDmRoom) {
|
||||
prefix(message, senderDisplayName)
|
||||
} else {
|
||||
|
|
@ -77,7 +77,7 @@ class DefaultRoomLastMessageFormatter @Inject constructor(
|
|||
content.body
|
||||
}
|
||||
is UnableToDecryptContent -> {
|
||||
val message = sp.getString(StringR.string.common_decryption_error)
|
||||
val message = sp.getString(CommonStrings.common_decryption_error)
|
||||
if (!isDmRoom) {
|
||||
prefix(message, senderDisplayName)
|
||||
} else {
|
||||
|
|
@ -94,7 +94,7 @@ class DefaultRoomLastMessageFormatter @Inject constructor(
|
|||
stateContentFormatter.format(content, senderDisplayName, isOutgoing, RenderingMode.RoomList)
|
||||
}
|
||||
is FailedToParseMessageLikeContent, is FailedToParseStateContent, is UnknownContent -> {
|
||||
prefixIfNeeded(sp.getString(StringR.string.common_unsupported_event), senderDisplayName, isDmRoom)
|
||||
prefixIfNeeded(sp.getString(CommonStrings.common_unsupported_event), senderDisplayName, isDmRoom)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -111,19 +111,19 @@ class DefaultRoomLastMessageFormatter @Inject constructor(
|
|||
messageType.body
|
||||
}
|
||||
is VideoMessageType -> {
|
||||
sp.getString(StringR.string.common_video)
|
||||
sp.getString(CommonStrings.common_video)
|
||||
}
|
||||
is ImageMessageType -> {
|
||||
sp.getString(StringR.string.common_image)
|
||||
sp.getString(CommonStrings.common_image)
|
||||
}
|
||||
is FileMessageType -> {
|
||||
sp.getString(StringR.string.common_file)
|
||||
sp.getString(CommonStrings.common_file)
|
||||
}
|
||||
is AudioMessageType -> {
|
||||
sp.getString(StringR.string.common_audio)
|
||||
sp.getString(CommonStrings.common_audio)
|
||||
}
|
||||
UnknownMessageType -> {
|
||||
sp.getString(StringR.string.common_unsupported_event)
|
||||
sp.getString(CommonStrings.common_unsupported_event)
|
||||
}
|
||||
is NoticeMessageType -> {
|
||||
messageType.body
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.StateContent
|
|||
import io.element.android.libraries.matrix.api.timeline.item.event.StickerContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.UnableToDecryptContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.UnknownContent
|
||||
import io.element.android.libraries.ui.strings.R
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import io.element.android.services.toolbox.api.strings.StringProvider
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
@ -71,7 +71,7 @@ class DefaultTimelineEventFormatter @Inject constructor(
|
|||
if (buildMeta.isDebuggable) {
|
||||
error("You should not use this formatter for this event: $event")
|
||||
}
|
||||
sp.getString(R.string.common_unsupported_event)
|
||||
sp.getString(CommonStrings.common_unsupported_event)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,10 +19,10 @@ package io.element.android.libraries.eventformatter.impl
|
|||
import io.element.android.libraries.eventformatter.impl.mode.RenderingMode
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.OtherState
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.StateContent
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import io.element.android.services.toolbox.api.strings.StringProvider
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import io.element.android.libraries.ui.strings.R as StringR
|
||||
|
||||
class StateContentFormatter @Inject constructor(
|
||||
private val sp: StringProvider,
|
||||
|
|
@ -50,7 +50,7 @@ class StateContentFormatter @Inject constructor(
|
|||
sp.getString(R.string.state_event_room_created, senderDisplayName)
|
||||
}
|
||||
}
|
||||
is OtherState.RoomEncryption -> sp.getString(StringR.string.common_encryption_enabled)
|
||||
is OtherState.RoomEncryption -> sp.getString(CommonStrings.common_encryption_enabled)
|
||||
is OtherState.RoomName -> {
|
||||
val hasRoomName = content.name != null
|
||||
when {
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ import io.element.android.libraries.designsystem.theme.components.Text
|
|||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.room.RoomSummaryDetails
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.ui.strings.R as StringR
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
||||
@Composable
|
||||
fun SelectedRoom(
|
||||
|
|
@ -84,7 +84,7 @@ fun SelectedRoom(
|
|||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Close,
|
||||
contentDescription = stringResource(id = StringR.string.action_remove),
|
||||
contentDescription = stringResource(id = CommonStrings.action_remove),
|
||||
tint = MaterialTheme.colorScheme.onPrimary,
|
||||
modifier = Modifier.padding(2.dp)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ import io.element.android.libraries.designsystem.theme.components.Text
|
|||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.matrix.ui.model.getAvatarData
|
||||
import io.element.android.libraries.matrix.ui.model.getBestName
|
||||
import io.element.android.libraries.ui.strings.R as StringR
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
||||
@Composable
|
||||
fun SelectedUser(
|
||||
|
|
@ -83,7 +83,7 @@ fun SelectedUser(
|
|||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Close,
|
||||
contentDescription = stringResource(id = StringR.string.action_remove),
|
||||
contentDescription = stringResource(id = CommonStrings.action_remove),
|
||||
tint = MaterialTheme.colorScheme.onPrimary,
|
||||
modifier = Modifier.padding(2.dp)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ import io.element.android.libraries.designsystem.theme.components.Icon
|
|||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.designsystem.theme.noFontPadding
|
||||
import io.element.android.libraries.matrix.ui.model.getAvatarData
|
||||
import io.element.android.libraries.ui.strings.R
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
||||
@Composable
|
||||
fun UnresolvedUserRow(
|
||||
|
|
@ -94,7 +94,7 @@ fun UnresolvedUserRow(
|
|||
)
|
||||
|
||||
Text(
|
||||
text = stringResource(R.string.common_invite_unknown_profile),
|
||||
text = stringResource(CommonStrings.common_invite_unknown_profile),
|
||||
color = MaterialTheme.colorScheme.secondary,
|
||||
fontSize = 12.sp,
|
||||
lineHeight = 16.sp,
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ import androidx.compose.material.icons.outlined.PhotoCamera
|
|||
import androidx.compose.material.icons.outlined.PhotoLibrary
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import io.element.android.libraries.ui.strings.R
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
||||
@Immutable
|
||||
sealed class AvatarAction(
|
||||
|
|
@ -31,7 +31,7 @@ sealed class AvatarAction(
|
|||
val icon: ImageVector,
|
||||
val destructive: Boolean = false,
|
||||
) {
|
||||
object TakePhoto : AvatarAction(titleResId = R.string.action_take_photo, icon = Icons.Outlined.PhotoCamera)
|
||||
object ChoosePhoto : AvatarAction(titleResId = R.string.action_choose_photo, icon = Icons.Outlined.PhotoLibrary)
|
||||
object Remove : AvatarAction(titleResId = R.string.action_remove, icon = Icons.Outlined.Delete, destructive = true)
|
||||
object TakePhoto : AvatarAction(titleResId = CommonStrings.action_take_photo, icon = Icons.Outlined.PhotoCamera)
|
||||
object ChoosePhoto : AvatarAction(titleResId = CommonStrings.action_choose_photo, icon = Icons.Outlined.PhotoLibrary)
|
||||
object Remove : AvatarAction(titleResId = CommonStrings.action_remove, icon = Icons.Outlined.Delete, destructive = true)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ import io.element.android.libraries.matrix.api.media.MediaSource
|
|||
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnail
|
||||
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailInfo
|
||||
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailType
|
||||
import io.element.android.libraries.ui.strings.R as StringR
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
|
|
@ -146,7 +146,7 @@ fun TextComposer(
|
|||
contentPadding = PaddingValues(top = 10.dp, bottom = 10.dp, start = 12.dp, end = 42.dp),
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
placeholder = {
|
||||
Text(stringResource(StringR.string.common_message), style = defaultTypography)
|
||||
Text(stringResource(CommonStrings.common_message), style = defaultTypography)
|
||||
},
|
||||
colors = TextFieldDefaults.colors(
|
||||
unfocusedTextColor = MaterialTheme.colorScheme.secondary,
|
||||
|
|
@ -225,7 +225,7 @@ private fun EditingModeView(
|
|||
)
|
||||
Icon(
|
||||
imageVector = Icons.Default.Close,
|
||||
contentDescription = stringResource(StringR.string.action_close),
|
||||
contentDescription = stringResource(CommonStrings.action_close),
|
||||
tint = MaterialTheme.colorScheme.secondary,
|
||||
modifier = Modifier
|
||||
.size(16.dp)
|
||||
|
|
@ -283,7 +283,7 @@ private fun ReplyToModeView(
|
|||
)
|
||||
Icon(
|
||||
imageVector = Icons.Default.Close,
|
||||
contentDescription = stringResource(StringR.string.action_close),
|
||||
contentDescription = stringResource(CommonStrings.action_close),
|
||||
tint = MaterialTheme.colorScheme.secondary,
|
||||
modifier = Modifier
|
||||
.size(16.dp)
|
||||
|
|
@ -367,8 +367,8 @@ private fun BoxScope.SendButton(
|
|||
else -> R.drawable.ic_send
|
||||
}
|
||||
val contentDescription = when (composerMode) {
|
||||
is MessageComposerMode.Edit -> stringResource(StringR.string.action_edit)
|
||||
else -> stringResource(StringR.string.action_send)
|
||||
is MessageComposerMode.Edit -> stringResource(CommonStrings.action_edit)
|
||||
else -> stringResource(CommonStrings.action_send)
|
||||
}
|
||||
Icon(
|
||||
modifier = Modifier.size(16.dp),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.ui.strings
|
||||
|
||||
typealias CommonPlurals = R.plurals
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.ui.strings
|
||||
|
||||
typealias CommonStrings = R.string
|
||||
Loading…
Add table
Add a link
Reference in a new issue