Merge pull request #7913 from vector-im/feature/bma/threadListCrashes
Thread list crashes
This commit is contained in:
commit
ffb11d6455
|
@ -0,0 +1 @@
|
|||
Handle network error on API `rooms/{roomId}/threads`
|
|
@ -794,6 +794,7 @@
|
|||
<string name="thread_list_modal_my_threads_subtitle">Shows all threads you’ve participated in</string>
|
||||
<string name="thread_list_empty_title">Keep discussions organized with threads</string>
|
||||
<string name="thread_list_empty_subtitle">Threads help keep your conversations on-topic and easy to track.</string>
|
||||
<string name="thread_list_not_available">You\'re homeserver does not support listing threads yet.</string>
|
||||
<!-- Parameter %s will be replaced by the value of string reply_in_thread -->
|
||||
<string name="thread_list_empty_notice">Tip: Long tap a message and use “%s”.</string>
|
||||
<string name="search_thread_from_a_thread">From a Thread</string>
|
||||
|
|
|
@ -25,6 +25,9 @@ import java.io.IOException
|
|||
import java.net.UnknownHostException
|
||||
import javax.net.ssl.HttpsURLConnection
|
||||
|
||||
fun Throwable.is400() = this is Failure.ServerError &&
|
||||
httpCode == HttpsURLConnection.HTTP_BAD_REQUEST
|
||||
|
||||
fun Throwable.is401() = this is Failure.ServerError &&
|
||||
httpCode == HttpsURLConnection.HTTP_UNAUTHORIZED && /* 401 */
|
||||
error.code == MatrixError.M_UNAUTHORIZED
|
||||
|
|
|
@ -19,5 +19,4 @@ package org.matrix.android.sdk.api.session.room.threads
|
|||
sealed class FetchThreadsResult {
|
||||
data class ShouldFetchMore(val nextBatch: String) : FetchThreadsResult()
|
||||
object ReachedEnd : FetchThreadsResult()
|
||||
object Failed : FetchThreadsResult()
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ import org.matrix.android.sdk.api.session.room.threads.model.ThreadSummary
|
|||
* This interface defines methods to interact with thread related features.
|
||||
* It's the dynamic threads implementation and the homeserver must return
|
||||
* a capability entry for threads. If the server do not support m.thread
|
||||
* then [ThreadsLocalService] should be used instead
|
||||
* then [org.matrix.android.sdk.api.session.room.threads.local.ThreadsLocalService] should be used instead
|
||||
*/
|
||||
interface ThreadsService {
|
||||
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* 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 im.vector.app.features.home.room.threads.list.viewmodel
|
||||
|
||||
import im.vector.app.core.platform.VectorViewModelAction
|
||||
|
||||
sealed interface ThreadListViewActions : VectorViewModelAction {
|
||||
object TryAgain : ThreadListViewActions
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* 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 im.vector.app.features.home.room.threads.list.viewmodel
|
||||
|
||||
import im.vector.app.core.platform.VectorViewEvents
|
||||
|
||||
sealed interface ThreadListViewEvents : VectorViewEvents {
|
||||
data class ShowError(val throwable: Throwable) : ThreadListViewEvents
|
||||
}
|
|
@ -27,8 +27,6 @@ import com.airbnb.mvrx.ViewModelContext
|
|||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import im.vector.app.core.platform.EmptyAction
|
||||
import im.vector.app.core.platform.EmptyViewEvents
|
||||
import im.vector.app.core.platform.VectorViewModel
|
||||
import im.vector.app.features.analytics.AnalyticsTracker
|
||||
import im.vector.app.features.analytics.extensions.toAnalyticsInteraction
|
||||
|
@ -52,7 +50,7 @@ class ThreadListViewModel @AssistedInject constructor(
|
|||
@Assisted val initialState: ThreadListViewState,
|
||||
private val analyticsTracker: AnalyticsTracker,
|
||||
private val session: Session,
|
||||
) : VectorViewModel<ThreadListViewState, EmptyAction, EmptyViewEvents>(initialState) {
|
||||
) : VectorViewModel<ThreadListViewState, ThreadListViewActions, ThreadListViewEvents>(initialState) {
|
||||
|
||||
private val room = session.getRoom(initialState.roomId)
|
||||
|
||||
|
@ -93,7 +91,17 @@ class ThreadListViewModel @AssistedInject constructor(
|
|||
fetchAndObserveThreads()
|
||||
}
|
||||
|
||||
override fun handle(action: EmptyAction) {}
|
||||
override fun handle(action: ThreadListViewActions) {
|
||||
when (action) {
|
||||
ThreadListViewActions.TryAgain -> handleTryAgain()
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleTryAgain() {
|
||||
viewModelScope.launch {
|
||||
fetchNextPage()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Observing thread list with respect to homeserver capabilities.
|
||||
|
@ -181,21 +189,23 @@ class ThreadListViewModel @AssistedInject constructor(
|
|||
true -> ThreadFilter.PARTICIPATED
|
||||
false -> ThreadFilter.ALL
|
||||
}
|
||||
room?.threadsService()?.fetchThreadList(
|
||||
nextBatchId = nextBatchId,
|
||||
limit = defaultPagedListConfig.pageSize,
|
||||
filter = filter,
|
||||
).let { result ->
|
||||
when (result) {
|
||||
is FetchThreadsResult.ReachedEnd -> {
|
||||
hasReachedEnd = true
|
||||
}
|
||||
is FetchThreadsResult.ShouldFetchMore -> {
|
||||
nextBatchId = result.nextBatch
|
||||
}
|
||||
else -> {
|
||||
try {
|
||||
room?.threadsService()?.fetchThreadList(
|
||||
nextBatchId = nextBatchId,
|
||||
limit = defaultPagedListConfig.pageSize,
|
||||
filter = filter,
|
||||
)?.let { result ->
|
||||
when (result) {
|
||||
is FetchThreadsResult.ReachedEnd -> {
|
||||
hasReachedEnd = true
|
||||
}
|
||||
is FetchThreadsResult.ShouldFetchMore -> {
|
||||
nextBatchId = result.nextBatch
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (throwable: Throwable) {
|
||||
_viewEvents.post(ThreadListViewEvents.ShowError(throwable))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ import androidx.core.view.isVisible
|
|||
import com.airbnb.mvrx.args
|
||||
import com.airbnb.mvrx.fragmentViewModel
|
||||
import com.airbnb.mvrx.withState
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.extensions.cleanup
|
||||
|
@ -41,10 +42,14 @@ import im.vector.app.features.home.room.threads.arguments.ThreadListArgs
|
|||
import im.vector.app.features.home.room.threads.arguments.ThreadTimelineArgs
|
||||
import im.vector.app.features.home.room.threads.list.viewmodel.ThreadListController
|
||||
import im.vector.app.features.home.room.threads.list.viewmodel.ThreadListPagedController
|
||||
import im.vector.app.features.home.room.threads.list.viewmodel.ThreadListViewActions
|
||||
import im.vector.app.features.home.room.threads.list.viewmodel.ThreadListViewEvents
|
||||
import im.vector.app.features.home.room.threads.list.viewmodel.ThreadListViewModel
|
||||
import im.vector.app.features.home.room.threads.list.viewmodel.ThreadListViewState
|
||||
import im.vector.app.features.rageshake.BugReporter
|
||||
import im.vector.app.features.rageshake.ReportType
|
||||
import org.matrix.android.sdk.api.failure.is400
|
||||
import org.matrix.android.sdk.api.failure.is404
|
||||
import org.matrix.android.sdk.api.session.room.threads.model.ThreadSummary
|
||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||
import org.matrix.android.sdk.api.util.MatrixItem
|
||||
|
@ -126,11 +131,45 @@ class ThreadListFragment :
|
|||
views.threadListRecyclerView.configureWith(legacyThreadListController, TimelineItemAnimator(), hasFixedSize = false)
|
||||
legacyThreadListController.listener = this
|
||||
}
|
||||
observeViewEvents()
|
||||
}
|
||||
|
||||
private fun observeViewEvents() {
|
||||
threadListViewModel.observeViewEvents {
|
||||
when (it) {
|
||||
is ThreadListViewEvents.ShowError -> handleShowError(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleShowError(event: ThreadListViewEvents.ShowError) {
|
||||
val error = event.throwable
|
||||
MaterialAlertDialogBuilder(requireActivity())
|
||||
.setTitle(R.string.dialog_title_error)
|
||||
.also {
|
||||
if (error.is400() || error.is404()) {
|
||||
// Outdated Homeserver
|
||||
it.setMessage(R.string.thread_list_not_available)
|
||||
it.setPositiveButton(R.string.ok) { _, _ ->
|
||||
requireActivity().finish()
|
||||
}
|
||||
} else {
|
||||
// Other error, can retry
|
||||
// (Can happen on first request or on pagination request)
|
||||
it.setMessage(errorFormatter.toHumanReadable(error))
|
||||
it.setPositiveButton(R.string.ok, null)
|
||||
it.setNegativeButton(R.string.global_retry) { _, _ ->
|
||||
threadListViewModel.handle(ThreadListViewActions.TryAgain)
|
||||
}
|
||||
}
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
views.threadListRecyclerView.cleanup()
|
||||
threadListController.listener = null
|
||||
legacyThreadListController.listener = null
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue