Scope and error manager

This commit is contained in:
Benoit Marty 2019-05-24 22:27:26 +02:00
parent cd5e808bb6
commit 128dea2677
12 changed files with 198 additions and 66 deletions

View File

@ -30,6 +30,7 @@ import im.vector.riotredesign.core.di.AppModule
import im.vector.riotredesign.features.home.HomeModule
import im.vector.riotredesign.features.rageshake.VectorFileLogger
import im.vector.riotredesign.features.rageshake.VectorUncaughtExceptionHandler
import im.vector.riotredesign.features.roomdirectory.RoomDirectoryModule
import org.koin.log.EmptyLogger
import org.koin.standalone.StandAloneContext.startKoin
import timber.log.Timber
@ -56,7 +57,8 @@ class VectorApplication : Application() {
EpoxyController.defaultModelBuildingHandler = EpoxyAsyncUtil.getAsyncBackgroundHandler()
val appModule = AppModule(applicationContext).definition
val homeModule = HomeModule().definition
startKoin(listOf(appModule, homeModule), logger = EmptyLogger())
val roomDirectoryModule = RoomDirectoryModule().definition
startKoin(listOf(appModule, homeModule, roomDirectoryModule), logger = EmptyLogger())

Matrix.getInstance().setApplicationFlavor(BuildConfig.FLAVOR_DESCRIPTION)
}

View File

@ -19,6 +19,7 @@ package im.vector.riotredesign.core.di
import android.content.Context
import android.content.Context.MODE_PRIVATE
import im.vector.matrix.android.api.Matrix
import im.vector.riotredesign.core.error.ErrorFormatter
import im.vector.riotredesign.core.resources.LocaleProvider
import im.vector.riotredesign.core.resources.StringArrayProvider
import im.vector.riotredesign.core.resources.StringProvider
@ -65,6 +66,10 @@ class AppModule(private val context: Context) {
RoomSummaryComparator()
}

single {
ErrorFormatter(get())
}

single {
NotificationDrawerManager(context)
}

View File

@ -0,0 +1,39 @@
/*
* Copyright 2019 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.riotredesign.core.error

import im.vector.matrix.android.api.failure.Failure
import im.vector.riotredesign.R
import im.vector.riotredesign.core.resources.StringProvider

class ErrorFormatter(val stringProvider: StringProvider) {


fun toHumanReadable(failure: Failure): String {
// Default
return failure.localizedMessage
}

fun toHumanReadable(throwable: Throwable): String {

return when (throwable) {
is Failure.NetworkConnection -> stringProvider.getString(R.string.error_no_network)
else -> throwable.localizedMessage
}

}
}

View File

@ -27,7 +27,6 @@ import kotlinx.android.synthetic.main.view_state.view.*
class StateView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0)
: FrameLayout(context, attrs, defStyle) {

// TODO Use Async from MvRx?
sealed class State {
object Content : State()
object Loading : State()

View File

@ -26,9 +26,11 @@ import im.vector.riotredesign.R
import im.vector.riotredesign.core.epoxy.errorWithRetryItem
import im.vector.riotredesign.core.epoxy.loadingItem
import im.vector.riotredesign.core.epoxy.noResultItem
import im.vector.riotredesign.core.error.ErrorFormatter
import im.vector.riotredesign.core.resources.StringProvider

class PublicRoomsController(private val stringProvider: StringProvider) : TypedEpoxyController<PublicRoomsViewState>() {
class PublicRoomsController(private val stringProvider: StringProvider,
private val errorFormatter: ErrorFormatter) : TypedEpoxyController<PublicRoomsViewState>() {

var callback: Callback? = null

@ -68,7 +70,7 @@ class PublicRoomsController(private val stringProvider: StringProvider) : TypedE
if (viewState.asyncPublicRoomsRequest is Fail) {
errorWithRetryItem {
id("error")
text(viewState.asyncPublicRoomsRequest.error.localizedMessage)
text(errorFormatter.toHumanReadable(viewState.asyncPublicRoomsRequest.error))
listener { callback?.loadMore() }
}
}

View File

@ -27,12 +27,15 @@ import com.google.android.material.snackbar.Snackbar
import com.jakewharton.rxbinding2.widget.RxTextView
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoom
import im.vector.riotredesign.R
import im.vector.riotredesign.core.error.ErrorFormatter
import im.vector.riotredesign.core.extensions.addFragmentToBackstack
import im.vector.riotredesign.core.platform.VectorBaseFragment
import im.vector.riotredesign.features.roomdirectory.picker.RoomDirectoryPickerFragment
import io.reactivex.rxkotlin.subscribeBy
import kotlinx.android.synthetic.main.fragment_public_rooms.*
import org.koin.android.ext.android.get
import org.koin.android.ext.android.inject
import org.koin.android.scope.ext.android.bindScope
import org.koin.android.scope.ext.android.getOrCreateScope
import timber.log.Timber
import java.util.concurrent.TimeUnit

@ -52,8 +55,8 @@ import java.util.concurrent.TimeUnit
class PublicRoomsFragment : VectorBaseFragment(), PublicRoomsController.Callback {

private val viewModel: RoomDirectoryViewModel by activityViewModel()

private val publicRoomsController = PublicRoomsController(get())
private val publicRoomsController: PublicRoomsController by inject()
private val errorFormatter: ErrorFormatter by inject()

override fun getLayoutResId() = R.layout.fragment_public_rooms

@ -84,7 +87,7 @@ class PublicRoomsFragment : VectorBaseFragment(), PublicRoomsController.Callback

viewModel.joinRoomErrorLiveData.observe(this, Observer {
it.getContentIfNotHandled()?.let { throwable ->
Snackbar.make(publicRoomsCoordinator, throwable.localizedMessage, Snackbar.LENGTH_SHORT)
Snackbar.make(publicRoomsCoordinator, errorFormatter.toHumanReadable(throwable), Snackbar.LENGTH_SHORT)
.show()
}
})
@ -92,6 +95,7 @@ class PublicRoomsFragment : VectorBaseFragment(), PublicRoomsController.Callback

override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
bindScope(getOrCreateScope(RoomDirectoryModule.ROOM_DIRECTORY_SCOPE))

setupRecyclerView()
}

View File

@ -16,15 +16,24 @@

package im.vector.riotredesign.features.roomdirectory

import android.os.Bundle
import im.vector.riotredesign.R
import im.vector.riotredesign.core.extensions.addFragment
import im.vector.riotredesign.core.platform.VectorBaseActivity
import org.koin.android.scope.ext.android.bindScope
import org.koin.android.scope.ext.android.getOrCreateScope

class RoomDirectoryActivity : VectorBaseActivity() {


override fun getLayoutRes() = R.layout.activity_simple

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

bindScope(getOrCreateScope(RoomDirectoryModule.ROOM_DIRECTORY_SCOPE))
}

override fun initUiAndData() {
if (isFirstCreation()) {
addFragment(PublicRoomsFragment(), R.id.simpleFragmentContainer)

View File

@ -0,0 +1,45 @@
/*
* Copyright 2019 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.riotredesign.features.roomdirectory

import im.vector.matrix.android.api.session.Session
import im.vector.riotredesign.features.roomdirectory.picker.RoomDirectoryListCreator
import im.vector.riotredesign.features.roomdirectory.picker.RoomDirectoryPickerController
import org.koin.dsl.module.module

// TODO Ganfra: When do we create a new module?
class RoomDirectoryModule {

companion object {
const val ROOM_DIRECTORY_SCOPE = "ROOM_DIRECTORY_SCOPE"
}

val definition = module(override = true) {

scope(ROOM_DIRECTORY_SCOPE) {
RoomDirectoryPickerController(get(), get(), get())
}

scope(ROOM_DIRECTORY_SCOPE) {
RoomDirectoryListCreator(get(), get<Session>().sessionParams.credentials)
}

scope(ROOM_DIRECTORY_SCOPE) {
PublicRoomsController(get(), get())
}
}
}

View File

@ -0,0 +1,70 @@
/*
* Copyright 2019 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.riotredesign.features.roomdirectory.picker

import im.vector.matrix.android.api.auth.data.Credentials
import im.vector.matrix.android.api.session.room.model.thirdparty.RoomDirectoryData
import im.vector.matrix.android.api.session.room.model.thirdparty.ThirdPartyProtocol
import im.vector.riotredesign.R
import im.vector.riotredesign.core.resources.StringArrayProvider

class RoomDirectoryListCreator(private val stringArrayProvider: StringArrayProvider,
private val credentials: Credentials) {

fun computeDirectories(thirdPartyProtocolData: Map<String, ThirdPartyProtocol>): List<RoomDirectoryData> {
val result = ArrayList<RoomDirectoryData>()

// Add user homeserver name
val userHsName = credentials.userId.substring(credentials.userId.indexOf(":") + 1)

result.add(RoomDirectoryData(
displayName = userHsName,
includeAllNetworks = true
))

// Add user's HS but for Matrix public rooms only
result.add(RoomDirectoryData())

// Add custom directory servers
val hsNamesList = stringArrayProvider.getStringArray(R.array.room_directory_servers)
hsNamesList.forEach {
if (it != userHsName) {
// Use the server name as a default display name
result.add(RoomDirectoryData(
displayName = it,
includeAllNetworks = true
))
}
}

// Add result of the request
thirdPartyProtocolData.forEach {
it.value.instances?.forEach { thirdPartyProtocolInstance ->
result.add(RoomDirectoryData(
homeServer = null,
displayName = thirdPartyProtocolInstance.desc ?: "",
thirdPartyInstanceId = thirdPartyProtocolInstance.instanceId,
includeAllNetworks = false,
// Default to protocol icon
avatarUrl = thirdPartyProtocolInstance.icon ?: it.value.icon
))
}
}

return result
}
}

View File

@ -20,18 +20,16 @@ import com.airbnb.epoxy.TypedEpoxyController
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Incomplete
import com.airbnb.mvrx.Success
import im.vector.matrix.android.api.auth.data.Credentials
import im.vector.matrix.android.api.session.room.model.thirdparty.RoomDirectoryData
import im.vector.matrix.android.api.session.room.model.thirdparty.ThirdPartyProtocol
import im.vector.riotredesign.R
import im.vector.riotredesign.core.epoxy.errorWithRetryItem
import im.vector.riotredesign.core.epoxy.loadingItem
import im.vector.riotredesign.core.resources.StringArrayProvider
import im.vector.riotredesign.core.error.ErrorFormatter
import im.vector.riotredesign.core.resources.StringProvider

class RoomDirectoryPickerController(private val stringProvider: StringProvider,
private val stringArrayProvider: StringArrayProvider,
private val credentials: Credentials
private val errorFormatter: ErrorFormatter,
private val roomDirectoryListCreator: RoomDirectoryListCreator
) : TypedEpoxyController<RoomDirectoryPickerViewState>() {

var callback: Callback? = null
@ -43,7 +41,7 @@ class RoomDirectoryPickerController(private val stringProvider: StringProvider,

when (asyncThirdPartyProtocol) {
is Success -> {
val directories = computeDirectories(asyncThirdPartyProtocol.invoke())
val directories = roomDirectoryListCreator.computeDirectories(asyncThirdPartyProtocol.invoke())

directories.forEach {
buildDirectory(it)
@ -57,59 +55,14 @@ class RoomDirectoryPickerController(private val stringProvider: StringProvider,
is Fail -> {
errorWithRetryItem {
id("error")
text(asyncThirdPartyProtocol.error.localizedMessage)
text(errorFormatter.toHumanReadable(asyncThirdPartyProtocol.error))
listener { callback?.retry() }
}
}
}
}

private fun computeDirectories(thirdPartyProtocolData: Map<String, ThirdPartyProtocol>): List<RoomDirectoryData> {
val result = ArrayList<RoomDirectoryData>()

// Add user homeserver name
val userHsName = credentials.userId.substring(credentials.userId.indexOf(":") + 1)

result.add(RoomDirectoryData(
displayName = userHsName,
includeAllNetworks = true
))

// Add user's HS but for Matrix public rooms only
result.add(RoomDirectoryData())

// Add custom directory servers
val hsNamesList = stringArrayProvider.getStringArray(R.array.room_directory_servers)
hsNamesList.forEach {
if (it != userHsName) {
// Use the server name as a default display name
result.add(RoomDirectoryData(
displayName = it,
includeAllNetworks = true
))
}
}

// Add result of the request
thirdPartyProtocolData.forEach {
it.value.instances?.forEach { thirdPartyProtocolInstance ->
result.add(RoomDirectoryData(
homeServer = null,
displayName = thirdPartyProtocolInstance.desc ?: "",
thirdPartyInstanceId = thirdPartyProtocolInstance.instanceId,
includeAllNetworks = false,
// Default to protocol icon
avatarUrl = thirdPartyProtocolInstance.icon ?: it.value.icon
))
}
}

return result
}

private fun buildDirectory(roomDirectoryData: RoomDirectoryData) {

// TODO
roomDirectoryItem {
id(index++)


View File

@ -23,24 +23,25 @@ import androidx.recyclerview.widget.LinearLayoutManager
import com.airbnb.mvrx.activityViewModel
import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.room.model.thirdparty.RoomDirectoryData
import im.vector.riotredesign.R
import im.vector.riotredesign.core.platform.VectorBaseFragment
import im.vector.riotredesign.features.roomdirectory.RoomDirectoryModule
import im.vector.riotredesign.features.roomdirectory.RoomDirectoryViewModel
import kotlinx.android.synthetic.main.fragment_public_rooms.toolbar
import kotlinx.android.synthetic.main.fragment_room_directory_picker.*
import org.koin.android.ext.android.get
import org.koin.android.ext.android.inject
import org.koin.android.scope.ext.android.bindScope
import org.koin.android.scope.ext.android.getOrCreateScope
import timber.log.Timber

// TODO Set title to R.string.select_room_directory
// TODO Menu to add custom room directory (not done in RiotWeb so far...)
class RoomDirectoryPickerFragment : VectorBaseFragment(), RoomDirectoryPickerController.Callback {

private val viewModel: RoomDirectoryViewModel by activityViewModel()

private val pickerViewModel: RoomDirectoryPickerViewModel by fragmentViewModel()

private val roomDirectoryPickerController = RoomDirectoryPickerController(get(), get(), get<Session>().sessionParams.credentials)
private val roomDirectoryPickerController: RoomDirectoryPickerController by inject()

override fun getLayoutResId() = R.layout.fragment_room_directory_picker

@ -69,6 +70,7 @@ class RoomDirectoryPickerFragment : VectorBaseFragment(), RoomDirectoryPickerCon

override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
bindScope(getOrCreateScope(RoomDirectoryModule.ROOM_DIRECTORY_SCOPE))

setupRecyclerView()
}
@ -87,7 +89,7 @@ class RoomDirectoryPickerFragment : VectorBaseFragment(), RoomDirectoryPickerCon
Timber.v("onRoomDirectoryClicked: $roomDirectoryData")
viewModel.setRoomDirectoryData(roomDirectoryData)

// TODO Not the bast way to manage Fragment Backstack...
// TODO Not the best way to manage Fragment Backstack...
vectorBaseActivity.onBackPressed()
}


View File

@ -20,4 +20,6 @@

<string name="create_new_room">Create New Room</string>

<string name="error_no_network">No network. Please check your Internet connection.</string>

</resources>