Add mapping on FocusEventException.
Extract FocusRequestState to its own file and add preview.
This commit is contained in:
parent
d3d5081084
commit
a4c6e6c281
10 changed files with 219 additions and 47 deletions
|
|
@ -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
|
|||
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,20 +72,20 @@ class TimelineController @Inject constructor(
|
|||
}
|
||||
|
||||
suspend fun focusOnEvent(eventId: EventId): Result<Unit> {
|
||||
return try {
|
||||
val newDetachedTimeline = room.timelineFocusedOnEvent(eventId)
|
||||
detachedTimeline.getAndUpdate { current ->
|
||||
if (current.isPresent) {
|
||||
current.get().close()
|
||||
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)
|
||||
}
|
||||
Optional.of(newDetachedTimeline)
|
||||
}
|
||||
Result.success(Unit)
|
||||
} catch (cancellation: CancellationException) {
|
||||
throw cancellation
|
||||
} catch (exception: Exception) {
|
||||
Result.failure(exception)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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
|
|||
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(
|
|||
}
|
||||
}
|
||||
|
||||
@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,
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
)
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -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 = {},
|
||||
)
|
||||
}
|
||||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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,13 +170,17 @@ class RustMatrixRoom(
|
|||
|
||||
override suspend fun unsubscribeFromSync() = roomSyncSubscriber.unsubscribe(roomId)
|
||||
|
||||
override suspend fun timelineFocusedOnEvent(eventId: EventId): Timeline {
|
||||
return innerRoom.timelineFocusedOnEvent(
|
||||
eventId = eventId.value,
|
||||
numContextEvents = 50u,
|
||||
internalIdPrefix = "focus_$eventId",
|
||||
).let { inner ->
|
||||
createTimeline(inner, isLive = false)
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -442,7 +447,7 @@ class RustMatrixRoom(
|
|||
}
|
||||
|
||||
override suspend fun toggleReaction(emoji: String, eventId: EventId): Result<Unit> {
|
||||
return liveTimeline.toggleReaction(emoji, eventId)
|
||||
return liveTimeline.toggleReaction(emoji, eventId)
|
||||
}
|
||||
|
||||
override suspend fun forwardEvent(eventId: EventId, roomIds: List<RoomId>): Result<Unit> {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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…
Add table
Add a link
Reference in a new issue