Browse Source

Add mapping on FocusEventException.

Extract FocusRequestState to its own file and add preview.
pull/2759/head
Benoit Marty 5 months ago committed by Benoit Marty
parent
commit
a4c6e6c281
  1. 16
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineController.kt
  2. 24
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt
  3. 45
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/focus/FocusRequestStateProvider.kt
  4. 67
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/focus/FocusRequestStateView.kt
  5. 2
      libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt
  6. 34
      libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/errors/FocusEventException.kt
  7. 42
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/FocusEventException.kt
  8. 9
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt
  9. 4
      libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt
  10. 1
      libraries/ui-strings/src/main/res/values/localazy.xml

16
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineController.kt

@ -24,6 +24,7 @@ import io.element.android.libraries.matrix.api.room.MatrixRoom @@ -24,6 +24,7 @@ import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
import io.element.android.libraries.matrix.api.timeline.Timeline
import io.element.android.libraries.matrix.api.timeline.TimelineProvider
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.SupervisorJob
@ -40,7 +41,6 @@ import kotlinx.coroutines.flow.stateIn @@ -40,7 +41,6 @@ import kotlinx.coroutines.flow.stateIn
import java.io.Closeable
import java.util.Optional
import javax.inject.Inject
import kotlin.coroutines.cancellation.CancellationException
/**
* This controller is responsible of using the right timeline to display messages and make associated actions.
@ -72,19 +72,19 @@ class TimelineController @Inject constructor( @@ -72,19 +72,19 @@ class TimelineController @Inject constructor(
}
suspend fun focusOnEvent(eventId: EventId): Result<Unit> {
return try {
val newDetachedTimeline = room.timelineFocusedOnEvent(eventId)
return room.timelineFocusedOnEvent(eventId)
.onFailure {
if (it is CancellationException) {
throw it
}
}
.map { newDetachedTimeline ->
detachedTimeline.getAndUpdate { current ->
if (current.isPresent) {
current.get().close()
}
Optional.of(newDetachedTimeline)
}
Result.success(Unit)
} catch (cancellation: CancellationException) {
throw cancellation
} catch (exception: Exception) {
Result.failure(exception)
}
}

24
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt

@ -57,6 +57,7 @@ import io.element.android.compound.tokens.generated.CompoundIcons @@ -57,6 +57,7 @@ import io.element.android.compound.tokens.generated.CompoundIcons
import io.element.android.features.messages.impl.timeline.components.TimelineItemRow
import io.element.android.features.messages.impl.timeline.di.LocalTimelineItemPresenterFactories
import io.element.android.features.messages.impl.timeline.di.aFakeTimelineItemPresenterFactories
import io.element.android.features.messages.impl.timeline.focus.FocusRequestStateView
import io.element.android.features.messages.impl.timeline.model.NewEventState
import io.element.android.features.messages.impl.timeline.model.TimelineItem
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent
@ -64,8 +65,6 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt @@ -64,8 +65,6 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
import io.element.android.features.messages.impl.typing.TypingNotificationState
import io.element.android.features.messages.impl.typing.TypingNotificationView
import io.element.android.features.messages.impl.typing.aTypingNotificationState
import io.element.android.libraries.designsystem.components.ProgressDialog
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.FloatingActionButton
@ -176,27 +175,6 @@ fun TimelineView( @@ -176,27 +175,6 @@ fun TimelineView(
}
}
@Composable
private fun FocusRequestStateView(
focusRequestState: FocusRequestState,
onClearFocusRequestState: () -> Unit,
modifier: Modifier = Modifier,
) {
when (focusRequestState) {
is FocusRequestState.Failure -> {
ErrorDialog(
content = stringResource(id = CommonStrings.common_failed),
onDismiss = onClearFocusRequestState,
modifier = modifier,
)
}
FocusRequestState.Fetching -> {
ProgressDialog(modifier = modifier, onDismissRequest = onClearFocusRequestState)
}
else -> Unit
}
}
@Composable
private fun BoxScope.TimelineScrollHelper(
hasAnyEvent: Boolean,

45
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/focus/FocusRequestStateProvider.kt

@ -0,0 +1,45 @@ @@ -0,0 +1,45 @@
/*
* Copyright (c) 2024 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.features.messages.impl.timeline.focus
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.features.messages.impl.timeline.FocusRequestState
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.room.errors.FocusEventException
open class FocusRequestStateProvider : PreviewParameterProvider<FocusRequestState> {
override val values: Sequence<FocusRequestState>
get() = sequenceOf(
FocusRequestState.Fetching,
FocusRequestState.Failure(
FocusEventException.EventNotFound(
eventId = EventId("\$anEventId"),
)
),
FocusRequestState.Failure(
FocusEventException.InvalidEventId(
eventId = "invalid",
err = "An error"
)
),
FocusRequestState.Failure(
FocusEventException.Other(
msg = "An error"
)
),
)
}

67
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/focus/FocusRequestStateView.kt

@ -0,0 +1,67 @@ @@ -0,0 +1,67 @@
/*
* Copyright (c) 2024 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.features.messages.impl.timeline.focus
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.PreviewParameter
import io.element.android.features.messages.impl.timeline.FocusRequestState
import io.element.android.libraries.designsystem.components.ProgressDialog
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.matrix.api.room.errors.FocusEventException
import io.element.android.libraries.ui.strings.CommonStrings
@Composable
fun FocusRequestStateView(
focusRequestState: FocusRequestState,
onClearFocusRequestState: () -> Unit,
modifier: Modifier = Modifier,
) {
when (focusRequestState) {
is FocusRequestState.Failure -> {
val errorMessage = when (focusRequestState.throwable) {
is FocusEventException.EventNotFound,
is FocusEventException.InvalidEventId -> stringResource(id = CommonStrings.error_message_not_found)
is FocusEventException.Other -> stringResource(id = CommonStrings.error_unknown)
else -> stringResource(id = CommonStrings.error_unknown)
}
ErrorDialog(
content = errorMessage,
onDismiss = onClearFocusRequestState,
modifier = modifier,
)
}
FocusRequestState.Fetching -> {
ProgressDialog(modifier = modifier, onDismissRequest = onClearFocusRequestState)
}
else -> Unit
}
}
@PreviewsDayNight
@Composable
internal fun FocusRequestStateViewPreview(
@PreviewParameter(FocusRequestStateProvider::class) state: FocusRequestState,
) = ElementPreview {
FocusRequestStateView(
focusRequestState = state,
onClearFocusRequestState = {},
)
}

2
libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt

@ -100,7 +100,7 @@ interface MatrixRoom : Closeable { @@ -100,7 +100,7 @@ interface MatrixRoom : Closeable {
val liveTimeline: Timeline
suspend fun timelineFocusedOnEvent(eventId: EventId): Timeline
suspend fun timelineFocusedOnEvent(eventId: EventId): Result<Timeline>
fun destroy()

34
libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/errors/FocusEventException.kt

@ -0,0 +1,34 @@ @@ -0,0 +1,34 @@
/*
* Copyright (c) 2024 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.matrix.api.room.errors
import io.element.android.libraries.matrix.api.core.EventId
sealed class FocusEventException : Exception() {
data class InvalidEventId(
val eventId: String,
val err: String
) : FocusEventException()
data class EventNotFound(
val eventId: EventId
) : FocusEventException()
data class Other(
val msg: String
) : FocusEventException()
}

42
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/FocusEventException.kt

@ -0,0 +1,42 @@ @@ -0,0 +1,42 @@
/*
* Copyright (c) 2024 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.matrix.impl.room
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.room.errors.FocusEventException
import org.matrix.rustcomponents.sdk.FocusEventException as RustFocusEventException
fun Throwable.toFocusEventException(): Throwable {
return when (this) {
is RustFocusEventException -> {
when (this) {
is RustFocusEventException.InvalidEventId -> {
FocusEventException.InvalidEventId(eventId, err)
}
is RustFocusEventException.EventNotFound -> {
FocusEventException.EventNotFound(EventId(eventId))
}
is RustFocusEventException.Other -> {
FocusEventException.Other(msg)
}
}
}
else -> {
this
}
}
}

9
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt

@ -18,6 +18,7 @@ package io.element.android.libraries.matrix.impl.room @@ -18,6 +18,7 @@ package io.element.android.libraries.matrix.impl.room
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.core.coroutine.childScope
import io.element.android.libraries.core.extensions.mapFailure
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.ProgressCallback
import io.element.android.libraries.matrix.api.core.RoomAlias
@ -169,14 +170,18 @@ class RustMatrixRoom( @@ -169,14 +170,18 @@ class RustMatrixRoom(
override suspend fun unsubscribeFromSync() = roomSyncSubscriber.unsubscribe(roomId)
override suspend fun timelineFocusedOnEvent(eventId: EventId): Timeline {
return innerRoom.timelineFocusedOnEvent(
override suspend fun timelineFocusedOnEvent(eventId: EventId): Result<Timeline> {
return runCatching {
innerRoom.timelineFocusedOnEvent(
eventId = eventId.value,
numContextEvents = 50u,
internalIdPrefix = "focus_$eventId",
).let { inner ->
createTimeline(inner, isLive = false)
}
}.mapFailure {
it.toFocusEventException()
}
}
override fun destroy() {

4
libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt

@ -215,8 +215,8 @@ class FakeMatrixRoom( @@ -215,8 +215,8 @@ class FakeMatrixRoom(
override val syncUpdateFlow: StateFlow<Long> = MutableStateFlow(0L)
override suspend fun timelineFocusedOnEvent(eventId: EventId): Timeline {
return FakeTimeline()
override suspend fun timelineFocusedOnEvent(eventId: EventId): Result<Timeline> {
return Result.success(FakeTimeline())
}
override suspend fun subscribeToSync() = Unit

1
libraries/ui-strings/src/main/res/values/localazy.xml

@ -240,6 +240,7 @@ @@ -240,6 +240,7 @@
<string name="error_failed_loading_messages">"Failed loading messages"</string>
<string name="error_failed_locating_user">"%1$s could not access your location. Please try again later."</string>
<string name="error_failed_uploading_voice_message">"Failed to upload your voice message."</string>
<string name="error_message_not_found">"Message not found"</string>
<string name="error_missing_location_auth_android">"%1$s does not have permission to access your location. You can enable access in Settings."</string>
<string name="error_missing_location_rationale_android">"%1$s does not have permission to access your location. Enable access below."</string>
<string name="error_missing_microphone_voice_rationale_android">"%1$s does not have permission to access your microphone. Enable access to record a voice message."</string>

Loading…
Cancel
Save