RoomDirectoryPicker WIP

This commit is contained in:
Benoit Marty
2019-05-24 15:00:43 +02:00
parent 877de1f597
commit 2404eeadf0
32 changed files with 1044 additions and 108 deletions

View File

@ -20,6 +20,7 @@ import android.content.Context
import android.content.Context.MODE_PRIVATE
import im.vector.matrix.android.api.Matrix
import im.vector.riotredesign.core.resources.LocaleProvider
import im.vector.riotredesign.core.resources.StringArrayProvider
import im.vector.riotredesign.core.resources.StringProvider
import im.vector.riotredesign.features.home.group.SelectedGroupStore
import im.vector.riotredesign.features.home.room.VisibleRoomStore
@ -40,6 +41,10 @@ class AppModule(private val context: Context) {
StringProvider(context.resources)
}
single {
StringArrayProvider(context.resources)
}
single {
context.getSharedPreferences("im.vector.riot", MODE_PRIVATE)
}

View File

@ -18,26 +18,26 @@ package im.vector.riotredesign.core.extensions
import androidx.fragment.app.Fragment
fun androidx.fragment.app.Fragment.addFragment(fragment: Fragment, frameId: Int) {
fun Fragment.addFragment(fragment: Fragment, frameId: Int) {
fragmentManager?.inTransaction { add(frameId, fragment) }
}
fun androidx.fragment.app.Fragment.replaceFragment(fragment: Fragment, frameId: Int) {
fun Fragment.replaceFragment(fragment: Fragment, frameId: Int) {
fragmentManager?.inTransaction { replace(frameId, fragment) }
}
fun androidx.fragment.app.Fragment.addFragmentToBackstack(fragment: Fragment, frameId: Int, tag: String? = null) {
fun Fragment.addFragmentToBackstack(fragment: Fragment, frameId: Int, tag: String? = null) {
fragmentManager?.inTransaction { replace(frameId, fragment).addToBackStack(tag) }
}
fun androidx.fragment.app.Fragment.addChildFragment(fragment: Fragment, frameId: Int) {
fun Fragment.addChildFragment(fragment: Fragment, frameId: Int) {
childFragmentManager.inTransaction { add(frameId, fragment) }
}
fun androidx.fragment.app.Fragment.replaceChildFragment(fragment: Fragment, frameId: Int) {
fun Fragment.replaceChildFragment(fragment: Fragment, frameId: Int) {
childFragmentManager.inTransaction { replace(frameId, fragment) }
}
fun androidx.fragment.app.Fragment.addChildFragmentToBackstack(fragment: Fragment, frameId: Int, tag: String? = null) {
fun Fragment.addChildFragmentToBackstack(fragment: Fragment, frameId: Int, tag: String? = null) {
childFragmentManager.inTransaction { replace(frameId, fragment).addToBackStack(tag) }
}

View File

@ -0,0 +1,33 @@
/*
* 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.extensions
import android.widget.TextView
import androidx.core.view.isVisible
/**
* Set a text in the TextView, or set visibility to GONE it if the text is null
*/
fun TextView.setTextOrHide(newText: String?, hideWhenBlank: Boolean = true) {
if (newText == null
|| (newText.isBlank() && hideWhenBlank)) {
isVisible = false
} else {
this.text = newText
isVisible = true
}
}

View File

@ -100,7 +100,7 @@ abstract class VectorBaseFragment : BaseMvRxFragment(), OnBackPressed {
override fun invalidate() {
//no-ops by default
// TODO Remove default implementation?
Timber.w("invalidate() method has not been implemented")
}
protected fun setArguments(args: Parcelable? = null) {

View File

@ -0,0 +1,38 @@
/*
* 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.resources
import android.content.res.Resources
import androidx.annotation.ArrayRes
import androidx.annotation.NonNull
class StringArrayProvider(private val resources: Resources) {
/**
* Returns a localized string array from the application's package's
* default string array table.
*
* @param resId Resource id for the string array
* @return The string array associated with the resource, stripped of styled
* text information.
*/
@NonNull
fun getStringArray(@ArrayRes resId: Int): Array<String> {
return resources.getStringArray(resId)
}
}

View File

@ -29,10 +29,9 @@ import im.vector.riotredesign.core.epoxy.VectorEpoxyHolder
import im.vector.riotredesign.core.epoxy.VectorEpoxyModel
import im.vector.riotredesign.features.home.AvatarRenderer
@EpoxyModelClass(layout = R.layout.item_room_directory)
abstract class RoomDirectoryItem : VectorEpoxyModel<RoomDirectoryItem.Holder>() {
@EpoxyModelClass(layout = R.layout.item_public_room)
abstract class PublicRoomItem : VectorEpoxyModel<PublicRoomItem.Holder>() {
// TODO Manage join state waiting from the sync
enum class JoinState {
NOT_JOINED,
JOINING,
@ -82,15 +81,15 @@ abstract class RoomDirectoryItem : VectorEpoxyModel<RoomDirectoryItem.Holder>()
class Holder : VectorEpoxyHolder() {
val rootView by bind<ViewGroup>(R.id.itemRoomDirectoryLayout)
val rootView by bind<ViewGroup>(R.id.itemPublicRoomLayout)
val avatarView by bind<ImageView>(R.id.itemRoomDirectoryAvatar)
val nameView by bind<TextView>(R.id.itemRoomDirectoryName)
val counterView by bind<TextView>(R.id.itemRoomDirectoryMembersCount)
val avatarView by bind<ImageView>(R.id.itemPublicRoomAvatar)
val nameView by bind<TextView>(R.id.itemPublicRoomName)
val counterView by bind<TextView>(R.id.itemPublicRoomMembersCount)
val joinedView by bind<View>(R.id.itemRoomDirectoryJoined)
val joinButton by bind<View>(R.id.itemRoomDirectoryJoin)
val joiningView by bind<View>(R.id.itemRoomDirectoryJoining)
val joinedView by bind<View>(R.id.itemPublicRoomJoined)
val joinButton by bind<View>(R.id.itemPublicRoomJoin)
val joiningView by bind<View>(R.id.itemPublicRoomJoining)
}
}

View File

@ -28,11 +28,11 @@ import im.vector.riotredesign.core.epoxy.loadingItem
import im.vector.riotredesign.core.epoxy.noResultItem
import im.vector.riotredesign.core.resources.StringProvider
class RoomDirectoryController(private val stringProvider: StringProvider) : TypedEpoxyController<RoomDirectoryViewState>() {
class PublicRoomsController(private val stringProvider: StringProvider) : TypedEpoxyController<PublicRoomsViewState>() {
var callback: Callback? = null
override fun buildModels(viewState: RoomDirectoryViewState) {
override fun buildModels(viewState: PublicRoomsViewState) {
val publicRooms = viewState.publicRooms
if (publicRooms.isEmpty()
@ -74,17 +74,17 @@ class RoomDirectoryController(private val stringProvider: StringProvider) : Type
}
}
private fun buildPublicRoom(publicRoom: PublicRoom, viewState: RoomDirectoryViewState) {
roomDirectoryItem {
private fun buildPublicRoom(publicRoom: PublicRoom, viewState: PublicRoomsViewState) {
publicRoomItem {
id(publicRoom.roomId)
roomId(publicRoom.roomId)
avatarUrl(publicRoom.avatarUrl)
roomName(publicRoom.name)
nbOfMembers(publicRoom.numJoinedMembers)
when {
viewState.joinedRoomsIds.contains(publicRoom.roomId) -> joinState(RoomDirectoryItem.JoinState.JOINED)
viewState.joiningRoomsIds.contains(publicRoom.roomId) -> joinState(RoomDirectoryItem.JoinState.JOINING)
else -> joinState(RoomDirectoryItem.JoinState.NOT_JOINED)
viewState.joinedRoomsIds.contains(publicRoom.roomId) -> joinState(PublicRoomItem.JoinState.JOINED)
viewState.joiningRoomsIds.contains(publicRoom.roomId) -> joinState(PublicRoomItem.JoinState.JOINING)
else -> joinState(PublicRoomItem.JoinState.NOT_JOINED)
}
joinListener {
callback?.onPublicRoomJoin(publicRoom)

View File

@ -21,13 +21,15 @@ import android.text.Editable
import android.view.View
import androidx.recyclerview.widget.LinearLayoutManager
import com.airbnb.epoxy.EpoxyVisibilityTracker
import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.activityViewModel
import com.airbnb.mvrx.withState
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoom
import im.vector.riotredesign.R
import im.vector.riotredesign.core.extensions.addFragmentToBackstack
import im.vector.riotredesign.core.platform.SimpleTextWatcher
import im.vector.riotredesign.core.platform.VectorBaseFragment
import kotlinx.android.synthetic.main.fragment_room_directory.*
import im.vector.riotredesign.features.roomdirectory.picker.RoomDirectoryPickerFragment
import kotlinx.android.synthetic.main.fragment_public_rooms.*
import org.koin.android.ext.android.get
import timber.log.Timber
@ -38,19 +40,21 @@ import timber.log.Timber
*
* FIXME Rotate screen launch again the request
*
* For Nad:
* TODO For Nad:
* Display number of rooms?
* Picto size are not correct
* Where I put the room directory picker?
* World Readable badge
* Guest can join badge
*
*/
class RoomDirectoryFragment : VectorBaseFragment(), RoomDirectoryController.Callback {
class PublicRoomsFragment : VectorBaseFragment(), PublicRoomsController.Callback {
private val viewModel: RoomDirectoryViewModel by fragmentViewModel()
private val viewModel: RoomDirectoryViewModel by activityViewModel()
private val roomDirectoryController = RoomDirectoryController(get())
private val publicRoomsController = PublicRoomsController(get())
override fun getLayoutResId() = R.layout.fragment_room_directory
override fun getLayoutResId() = R.layout.fragment_public_rooms
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
@ -62,16 +66,20 @@ class RoomDirectoryFragment : VectorBaseFragment(), RoomDirectoryController.Call
it.setDisplayHomeAsUpEnabled(true)
}
roomDirectoryFilter.addTextChangedListener(object : SimpleTextWatcher() {
publicRoomsFilter.addTextChangedListener(object : SimpleTextWatcher() {
override fun afterTextChanged(s: Editable) {
// TODO Debounce
viewModel.filterWith(roomDirectoryFilter.text.toString())
viewModel.filterWith(publicRoomsFilter.text.toString())
}
})
createNewRoom.setOnClickListener {
publicRoomsCreateNewRoom.setOnClickListener {
vectorBaseActivity.notImplemented()
}
publicRoomsChangeDirectory.setOnClickListener {
vectorBaseActivity.addFragmentToBackstack(RoomDirectoryPickerFragment(), R.id.simpleFragmentContainer)
}
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
@ -82,14 +90,14 @@ class RoomDirectoryFragment : VectorBaseFragment(), RoomDirectoryController.Call
private fun setupRecyclerView() {
val epoxyVisibilityTracker = EpoxyVisibilityTracker()
epoxyVisibilityTracker.attach(roomDirectoryList)
epoxyVisibilityTracker.attach(publicRoomsList)
val layoutManager = LinearLayoutManager(context)
roomDirectoryList.layoutManager = layoutManager
roomDirectoryController.callback = this
publicRoomsList.layoutManager = layoutManager
publicRoomsController.callback = this
roomDirectoryList.setController(roomDirectoryController)
publicRoomsList.setController(publicRoomsController)
}
override fun onPublicRoomClicked(publicRoom: PublicRoom) {
@ -108,6 +116,9 @@ class RoomDirectoryFragment : VectorBaseFragment(), RoomDirectoryController.Call
override fun invalidate() = withState(viewModel) { state ->
// Populate list with Epoxy
roomDirectoryController.setData(state)
publicRoomsController.setData(state)
// Directory name
publicRoomsDirectoryName.text = state.roomDirectoryDisplayName
}
}

View File

@ -24,6 +24,7 @@ import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoom
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoomsFilter
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoomsParams
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoomsResponse
import im.vector.matrix.android.api.session.room.model.thirdparty.RoomDirectoryData
import im.vector.matrix.android.api.util.Cancelable
import im.vector.matrix.rx.rx
import im.vector.riotredesign.core.platform.VectorViewModel
@ -32,13 +33,13 @@ import timber.log.Timber
private const val PUBLIC_ROOMS_LIMIT = 20
class RoomDirectoryViewModel(initialState: RoomDirectoryViewState,
private val session: Session) : VectorViewModel<RoomDirectoryViewState>(initialState) {
class RoomDirectoryViewModel(initialState: PublicRoomsViewState,
private val session: Session) : VectorViewModel<PublicRoomsViewState>(initialState) {
companion object : MvRxViewModelFactory<RoomDirectoryViewModel, RoomDirectoryViewState> {
companion object : MvRxViewModelFactory<RoomDirectoryViewModel, PublicRoomsViewState> {
@JvmStatic
override fun create(viewModelContext: ViewModelContext, state: RoomDirectoryViewState): RoomDirectoryViewModel? {
override fun create(viewModelContext: ViewModelContext, state: PublicRoomsViewState): RoomDirectoryViewModel? {
val currentSession = viewModelContext.activity.get<Session>()
return RoomDirectoryViewModel(state, currentSession)
@ -52,10 +53,19 @@ class RoomDirectoryViewModel(initialState: RoomDirectoryViewState,
private var currentTask: Cancelable? = null
// Default RoomDirectoryData
private var roomDirectoryData = RoomDirectoryData()
init {
// Load with empty filter
load()
setState {
copy(
roomDirectoryDisplayName = roomDirectoryData.displayName
)
}
// Observe joined room (from the sync)
observeJoinedRooms()
}
@ -84,11 +94,33 @@ class RoomDirectoryViewModel(initialState: RoomDirectoryViewState,
}
}
fun setRoomDirectoryData(roomDirectoryData: RoomDirectoryData) {
if (this.roomDirectoryData == roomDirectoryData) {
return
}
this.roomDirectoryData = roomDirectoryData
reset()
load()
}
fun filterWith(filter: String) {
if (currentFilter == filter) {
return
}
currentTask?.cancel()
currentFilter = filter
reset()
load()
}
private fun reset() {
// Reset since token
since = null
@ -96,10 +128,10 @@ class RoomDirectoryViewModel(initialState: RoomDirectoryViewState,
copy(
publicRooms = emptyList(),
asyncPublicRoomsRequest = Loading(),
hasMore = false)
hasMore = false,
roomDirectoryDisplayName = roomDirectoryData.displayName
)
}
load()
}
fun loadMore() {
@ -115,13 +147,13 @@ class RoomDirectoryViewModel(initialState: RoomDirectoryViewState,
}
private fun load() {
currentTask = session.getPublicRooms(null, // TODO session.sessionParams.homeServerConnectionConfig.homeServerUri.toString(),
currentTask = session.getPublicRooms(roomDirectoryData.homeServer,
PublicRoomsParams(
limit = PUBLIC_ROOMS_LIMIT,
filter = PublicRoomsFilter(searchTerm = currentFilter),
includeAllNetworks = false, // TODO
includeAllNetworks = roomDirectoryData.includeAllNetworks,
since = since,
thirdPartyInstanceId = null // TODO
thirdPartyInstanceId = roomDirectoryData.thirdPartyInstanceId
),
object : MatrixCallback<PublicRoomsResponse> {
override fun onSuccess(data: PublicRoomsResponse) {

View File

@ -21,7 +21,7 @@ import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.Uninitialized
import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoom
data class RoomDirectoryViewState(
data class PublicRoomsViewState(
// Store cumul of pagination result
val publicRooms: List<PublicRoom> = emptyList(),
// Current pagination request
@ -31,5 +31,6 @@ data class RoomDirectoryViewState(
// List of roomIds that the user wants to join
val joiningRoomsIds: List<String> = emptyList(),
// List of joined roomId,
val joinedRoomsIds: List<String> = emptyList()
val joinedRoomsIds: List<String> = emptyList(),
val roomDirectoryDisplayName: String? = null
) : MvRxState

View File

@ -27,7 +27,7 @@ class RoomDirectoryActivity : VectorBaseActivity() {
override fun initUiAndData() {
if (isFirstCreation()) {
addFragment(RoomDirectoryFragment(), R.id.simpleFragmentContainer)
addFragment(PublicRoomsFragment(), R.id.simpleFragmentContainer)
}
}

View File

@ -0,0 +1,75 @@
/*
* 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 android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import im.vector.riotredesign.R
import im.vector.riotredesign.core.epoxy.VectorEpoxyHolder
import im.vector.riotredesign.core.epoxy.VectorEpoxyModel
import im.vector.riotredesign.core.extensions.setTextOrHide
import im.vector.riotredesign.core.glide.GlideApp
@EpoxyModelClass(layout = R.layout.item_room_directory)
abstract class RoomDirectoryItem : VectorEpoxyModel<RoomDirectoryItem.Holder>() {
@EpoxyAttribute
var directoryAvatarUrl: String? = null
@EpoxyAttribute
var directoryName: String? = null
@EpoxyAttribute
var directoryDescription: String? = null
@EpoxyAttribute
var includeAllNetworks: Boolean = false
@EpoxyAttribute
var globalListener: (() -> Unit)? = null
override fun bind(holder: Holder) {
holder.rootView.setOnClickListener { globalListener?.invoke() }
// Avatar
GlideApp.with(holder.avatarView)
.load(directoryAvatarUrl)
.apply {
if (!includeAllNetworks) {
placeholder(R.drawable.network_matrix)
}
}
.into(holder.avatarView)
holder.nameView.text = directoryName
holder.descritionView.setTextOrHide(directoryDescription)
}
class Holder : VectorEpoxyHolder() {
val rootView by bind<ViewGroup>(R.id.itemRoomDirectoryLayout)
val avatarView by bind<ImageView>(R.id.itemRoomDirectoryAvatar)
val nameView by bind<TextView>(R.id.itemRoomDirectoryName)
val descritionView by bind<TextView>(R.id.itemRoomDirectoryDescription)
}
}

View File

@ -0,0 +1,139 @@
/*
* 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 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.resources.StringProvider
class RoomDirectoryPickerController(private val stringProvider: StringProvider,
private val stringArrayProvider: StringArrayProvider,
private val credentials: Credentials
) : TypedEpoxyController<RoomDirectoryPickerViewState>() {
var callback: Callback? = null
var index = 0
override fun buildModels(viewState: RoomDirectoryPickerViewState) {
val asyncThirdPartyProtocol = viewState.asyncThirdPartyRequest
when (asyncThirdPartyProtocol) {
is Success -> {
val directories = computeDirectories(asyncThirdPartyProtocol.invoke())
directories.forEach {
buildDirectory(it)
}
}
is Incomplete -> {
loadingItem {
id("loading")
}
}
is Fail -> {
errorWithRetryItem {
id("error")
text(asyncThirdPartyProtocol.error.localizedMessage)
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++)
directoryName(roomDirectoryData.displayName)
val description = when {
roomDirectoryData.includeAllNetworks -> stringProvider.getString(R.string.directory_server_all_rooms_on_server, roomDirectoryData.displayName)
"Matrix" == roomDirectoryData.displayName -> stringProvider.getString(R.string.directory_server_native_rooms, roomDirectoryData.displayName)
else -> null
}
directoryDescription(description)
directoryAvatarUrl(roomDirectoryData.avatarUrl)
includeAllNetworks(roomDirectoryData.includeAllNetworks)
globalListener {
callback?.onRoomDirectoryClicked(roomDirectoryData)
}
}
}
interface Callback {
fun onRoomDirectoryClicked(roomDirectory: RoomDirectoryData)
fun retry()
}
}

View File

@ -0,0 +1,103 @@
/*
* 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 android.os.Bundle
import android.view.MenuItem
import android.view.View
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.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 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)
override fun getLayoutResId() = R.layout.fragment_room_directory_picker
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
vectorBaseActivity.setSupportActionBar(toolbar)
vectorBaseActivity.supportActionBar?.let {
it.setDisplayShowHomeEnabled(true)
it.setDisplayHomeAsUpEnabled(true)
}
}
override fun getMenuRes() = R.menu.menu_directory_server_picker
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == R.id.action_add_custom_hs) {
// TODO
vectorBaseActivity.notImplemented()
return true
}
return super.onOptionsItemSelected(item)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
setupRecyclerView()
}
private fun setupRecyclerView() {
val layoutManager = LinearLayoutManager(context)
roomDirectoryPickerList.layoutManager = layoutManager
roomDirectoryPickerController.callback = this
roomDirectoryPickerList.setController(roomDirectoryPickerController)
}
override fun onRoomDirectoryClicked(roomDirectoryData: RoomDirectoryData) {
Timber.v("onRoomDirectoryClicked: $roomDirectoryData")
viewModel.setRoomDirectoryData(roomDirectoryData)
// TODO Not the bast way to manage Fragment Backstack...
vectorBaseActivity.onBackPressed()
}
override fun retry() {
Timber.v("Retry")
pickerViewModel.load()
}
override fun invalidate() = withState(pickerViewModel) { state ->
// Populate list with Epoxy
roomDirectoryPickerController.setData(state)
}
}

View File

@ -0,0 +1,62 @@
/*
* 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 com.airbnb.mvrx.Fail
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.ViewModelContext
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.room.model.thirdparty.ThirdPartyProtocol
import im.vector.riotredesign.core.platform.VectorViewModel
import org.koin.android.ext.android.get
class RoomDirectoryPickerViewModel(initialState: RoomDirectoryPickerViewState,
private val session: Session) : VectorViewModel<RoomDirectoryPickerViewState>(initialState) {
companion object : MvRxViewModelFactory<RoomDirectoryPickerViewModel, RoomDirectoryPickerViewState> {
@JvmStatic
override fun create(viewModelContext: ViewModelContext, state: RoomDirectoryPickerViewState): RoomDirectoryPickerViewModel? {
val currentSession = viewModelContext.activity.get<Session>()
return RoomDirectoryPickerViewModel(state, currentSession)
}
}
init {
load()
}
fun load() {
session.getThirdPartyProtocol(object : MatrixCallback<Map<String, ThirdPartyProtocol>> {
override fun onSuccess(data: Map<String, ThirdPartyProtocol>) {
setState {
copy(asyncThirdPartyRequest = Success(data))
}
}
override fun onFailure(failure: Throwable) {
setState {
copy(asyncThirdPartyRequest = Fail(failure))
}
}
})
}
}

View File

@ -0,0 +1,26 @@
/*
* 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 com.airbnb.mvrx.Async
import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.Uninitialized
import im.vector.matrix.android.api.session.room.model.thirdparty.ThirdPartyProtocol
data class RoomDirectoryPickerViewState(
val asyncThirdPartyRequest: Async<Map<String, ThirdPartyProtocol>> = Uninitialized
) : MvRxState