mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2025-02-17 04:20:00 +03:00
Basic support to browse space directory
This commit is contained in:
parent
802853d205
commit
06a84d985a
20 changed files with 361 additions and 90 deletions
|
@ -28,5 +28,6 @@ data class SpaceChildInfo(
|
|||
val order: String?,
|
||||
val activeMemberCount: Int?,
|
||||
val autoJoin: Boolean,
|
||||
val viaServers: List<String>
|
||||
val viaServers: List<String>,
|
||||
val parentRoomId: String?
|
||||
)
|
||||
|
|
|
@ -54,7 +54,8 @@ interface SpaceService {
|
|||
/**
|
||||
* Get's information of a space by querying the server
|
||||
*/
|
||||
suspend fun querySpaceChildren(spaceId: String): Pair<RoomSummary, List<SpaceChildInfo>>
|
||||
suspend fun querySpaceChildren(spaceId: String, suggestedOnly: Boolean? = null, autoJoinedOnly: Boolean? = null)
|
||||
: Pair<RoomSummary, List<SpaceChildInfo>>
|
||||
|
||||
/**
|
||||
* Get a live list of space summaries. This list is refreshed as soon as the data changes.
|
||||
|
|
|
@ -86,7 +86,8 @@ internal class RoomSummaryMapper @Inject constructor(private val timelineEventMa
|
|||
activeMemberCount = it.childSummaryEntity?.joinedMembersCount,
|
||||
order = it.order,
|
||||
autoJoin = it.autoJoin ?: false,
|
||||
viaServers = it.viaServers.toList()
|
||||
viaServers = it.viaServers.toList(),
|
||||
parentRoomId = roomSummaryEntity.roomId
|
||||
)
|
||||
}
|
||||
)
|
||||
|
|
|
@ -44,7 +44,6 @@ import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource
|
|||
import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryDataSource
|
||||
import org.matrix.android.sdk.internal.session.space.peeking.PeekSpaceTask
|
||||
import org.matrix.android.sdk.internal.session.space.peeking.SpacePeekResult
|
||||
import java.lang.IllegalArgumentException
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class DefaultSpaceService @Inject constructor(
|
||||
|
@ -90,8 +89,8 @@ internal class DefaultSpaceService @Inject constructor(
|
|||
return peekSpaceTask.execute(PeekSpaceTask.Params(spaceId))
|
||||
}
|
||||
|
||||
override suspend fun querySpaceChildren(spaceId: String): Pair<RoomSummary, List<SpaceChildInfo>> {
|
||||
return resolveSpaceInfoTask.execute(ResolveSpaceInfoTask.Params.withId(spaceId)).let { response ->
|
||||
override suspend fun querySpaceChildren(spaceId: String, suggestedOnly: Boolean?, autoJoinedOnly: Boolean?): Pair<RoomSummary, List<SpaceChildInfo>> {
|
||||
return resolveSpaceInfoTask.execute(ResolveSpaceInfoTask.Params.withId(spaceId, suggestedOnly, autoJoinedOnly)).let { response ->
|
||||
val spaceDesc = response.rooms?.firstOrNull { it.roomId == spaceId }
|
||||
Pair(
|
||||
first = RoomSummary(
|
||||
|
@ -111,7 +110,7 @@ internal class DefaultSpaceService @Inject constructor(
|
|||
?.map { childSummary ->
|
||||
val childStateEv = response.events
|
||||
?.firstOrNull { it.stateKey == childSummary.roomId && it.type == EventType.STATE_SPACE_CHILD }
|
||||
?.content.toModel<SpaceChildContent>()
|
||||
val childStateEvContent = childStateEv?.content.toModel<SpaceChildContent>()
|
||||
SpaceChildInfo(
|
||||
childRoomId = childSummary.roomId,
|
||||
isKnown = true,
|
||||
|
@ -119,10 +118,11 @@ internal class DefaultSpaceService @Inject constructor(
|
|||
name = childSummary.name,
|
||||
topic = childSummary.topic,
|
||||
avatarUrl = childSummary.avatarUrl,
|
||||
order = childStateEv?.order,
|
||||
autoJoin = childStateEv?.autoJoin ?: false,
|
||||
viaServers = childStateEv?.via ?: emptyList(),
|
||||
activeMemberCount = childSummary.numJoinedMembers
|
||||
order = childStateEvContent?.order,
|
||||
autoJoin = childStateEvContent?.autoJoin ?: false,
|
||||
viaServers = childStateEvContent?.via ?: emptyList(),
|
||||
activeMemberCount = childSummary.numJoinedMembers,
|
||||
parentRoomId = childStateEv?.roomId
|
||||
)
|
||||
} ?: emptyList()
|
||||
)
|
||||
|
|
|
@ -23,12 +23,15 @@ import javax.inject.Inject
|
|||
internal interface ResolveSpaceInfoTask : Task<ResolveSpaceInfoTask.Params, SpacesResponse> {
|
||||
data class Params(
|
||||
val spaceId: String,
|
||||
val maxRoomPerSpace: Int,
|
||||
val maxRoomPerSpace: Int?,
|
||||
val limit: Int,
|
||||
val batchToken: String?
|
||||
val batchToken: String?,
|
||||
val suggestedOnly: Boolean?,
|
||||
val autoJoinOnly: Boolean?
|
||||
) {
|
||||
companion object {
|
||||
fun withId(spaceId: String) = Params(spaceId, 10, 20, null)
|
||||
fun withId(spaceId: String, suggestedOnly: Boolean?, autoJoinOnly: Boolean?) =
|
||||
Params(spaceId, 10, 20, null, suggestedOnly, autoJoinOnly)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -37,7 +40,13 @@ internal class DefaultResolveSpaceInfoTask @Inject constructor(
|
|||
private val spaceApi: SpaceApi
|
||||
) : ResolveSpaceInfoTask {
|
||||
override suspend fun execute(params: ResolveSpaceInfoTask.Params): SpacesResponse {
|
||||
val body = SpaceSummaryParams(maxRoomPerSpace = params.maxRoomPerSpace, limit = params.limit, batch = params.batchToken ?: "")
|
||||
val body = SpaceSummaryParams(
|
||||
maxRoomPerSpace = params.maxRoomPerSpace,
|
||||
limit = params.limit,
|
||||
batch = params.batchToken ?: "",
|
||||
autoJoinedOnly = params.autoJoinOnly,
|
||||
suggestedOnly = params.suggestedOnly
|
||||
)
|
||||
return executeRequest<SpacesResponse>(null) {
|
||||
apiCall = spaceApi.getSpaces(params.spaceId, body)
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ internal interface SpaceApi {
|
|||
* POST /_matrix/client/r0/rooms/{roomID}/spaces
|
||||
* {
|
||||
* "max_rooms_per_space": 5, // The maximum number of rooms/subspaces to return for a given space, if negative unbounded. default: -1.
|
||||
* "auto_join_only": true, // If true, only return m.space.child events with auto_join:true, default: false, which returns all events.
|
||||
* "limit": 100, // The maximum number of rooms/subspaces to return, server can override this, default: 100.
|
||||
* "batch": "opaque_string" // A token to use if this is a subsequent HTTP hit, default: "".
|
||||
* }
|
||||
|
|
|
@ -22,12 +22,14 @@ import com.squareup.moshi.JsonClass
|
|||
@JsonClass(generateAdapter = true)
|
||||
internal data class SpaceSummaryParams(
|
||||
/** The maximum number of rooms/subspaces to return for a given space, if negative unbounded. default: -1*/
|
||||
@Json(name = "max_rooms_per_space") val maxRoomPerSpace: Int = 100,
|
||||
@Json(name = "max_rooms_per_space") val maxRoomPerSpace: Int?,
|
||||
/** The maximum number of rooms/subspaces to return, server can override this, default: 100 */
|
||||
@Json(name = "limit") val limit: Int = 100,
|
||||
@Json(name = "limit") val limit: Int?,
|
||||
/** A token to use if this is a subsequent HTTP hit, default: "".*/
|
||||
@Json(name = "batch") val batch: String = "",
|
||||
/** whether we should only return children with the "suggested" flag set.*/
|
||||
@Json(name = "suggested_only") val suggestedOnly: Boolean = false
|
||||
@Json(name = "suggested_only") val suggestedOnly: Boolean?,
|
||||
/** whether we should only return children with the "suggested" flag set.*/
|
||||
@Json(name = "auto_join_only") val autoJoinedOnly: Boolean?
|
||||
|
||||
)
|
||||
|
|
|
@ -123,6 +123,7 @@ import im.vector.app.features.spaces.SpaceListFragment
|
|||
import im.vector.app.features.spaces.create.ChooseSpaceTypeFragment
|
||||
import im.vector.app.features.spaces.create.CreateSpaceDefaultRoomsFragment
|
||||
import im.vector.app.features.spaces.create.CreateSpaceDetailsFragment
|
||||
import im.vector.app.features.spaces.explore.SpaceDirectoryFragment
|
||||
import im.vector.app.features.spaces.preview.SpacePreviewFragment
|
||||
import im.vector.app.features.terms.ReviewTermsFragment
|
||||
import im.vector.app.features.usercode.ShowUserCodeFragment
|
||||
|
@ -666,4 +667,9 @@ interface FragmentModule {
|
|||
@IntoMap
|
||||
@FragmentKey(MatrixToRoomSpaceFragment::class)
|
||||
fun bindMatrixToRoomSpaceFragment(fragment: MatrixToRoomSpaceFragment): Fragment
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FragmentKey(SpaceDirectoryFragment::class)
|
||||
fun bindSpaceDirectoryFragment(fragment: SpaceDirectoryFragment): Fragment
|
||||
}
|
||||
|
|
|
@ -79,6 +79,7 @@ import im.vector.app.features.share.IncomingShareActivity
|
|||
import im.vector.app.features.signout.soft.SoftLogoutActivity
|
||||
import im.vector.app.features.spaces.ShareSpaceBottomSheet
|
||||
import im.vector.app.features.spaces.SpaceCreationActivity
|
||||
import im.vector.app.features.spaces.SpaceExploreActivity
|
||||
import im.vector.app.features.terms.ReviewTermsActivity
|
||||
import im.vector.app.features.ui.UiStateRepository
|
||||
import im.vector.app.features.usercode.UserCodeActivity
|
||||
|
@ -154,6 +155,7 @@ interface ScreenComponent {
|
|||
fun inject(activity: ReAuthActivity)
|
||||
fun inject(activity: RoomDevToolActivity)
|
||||
fun inject(activity: SpaceCreationActivity)
|
||||
fun inject(activity: SpaceExploreActivity)
|
||||
|
||||
/* ==========================================================================================
|
||||
* BottomSheets
|
||||
|
|
|
@ -57,12 +57,13 @@ class RoomSummaryItemFactory @Inject constructor(private val displayableEventFor
|
|||
|
||||
fun createSuggestion(spaceChildInfo: SpaceChildInfo,
|
||||
suggestedRoomJoiningStates: Map<String, Async<Unit>>,
|
||||
onJoinClick: View.OnClickListener) : VectorEpoxyModel<*> {
|
||||
return SuggestedRoomItem_()
|
||||
onJoinClick: View.OnClickListener): VectorEpoxyModel<*> {
|
||||
return SpaceChildInfoItem_()
|
||||
.id("sug_${spaceChildInfo.childRoomId}")
|
||||
.matrixItem(MatrixItem.RoomItem(spaceChildInfo.childRoomId, spaceChildInfo.name, spaceChildInfo.avatarUrl))
|
||||
.avatarRenderer(avatarRenderer)
|
||||
.topic(spaceChildInfo.topic)
|
||||
.buttonLabel(stringProvider.getString(R.string.join))
|
||||
.loading(suggestedRoomJoiningStates[spaceChildInfo.childRoomId] is Loading)
|
||||
.memberCount(spaceChildInfo.activeMemberCount ?: 0)
|
||||
.buttonClickListener(onJoinClick)
|
||||
|
|
|
@ -38,7 +38,7 @@ import me.gujun.android.span.span
|
|||
import org.matrix.android.sdk.api.util.MatrixItem
|
||||
|
||||
@EpoxyModelClass(layout = R.layout.item_suggested_room)
|
||||
abstract class SuggestedRoomItem : VectorEpoxyModel<SuggestedRoomItem.Holder>() {
|
||||
abstract class SpaceChildInfoItem : VectorEpoxyModel<SpaceChildInfoItem.Holder>() {
|
||||
|
||||
@EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer
|
||||
@EpoxyAttribute lateinit var matrixItem: MatrixItem
|
||||
|
@ -48,6 +48,9 @@ abstract class SuggestedRoomItem : VectorEpoxyModel<SuggestedRoomItem.Holder>()
|
|||
|
||||
@EpoxyAttribute var memberCount: Int = 0
|
||||
@EpoxyAttribute var loading: Boolean = false
|
||||
@EpoxyAttribute var space: Boolean = false
|
||||
|
||||
@EpoxyAttribute var buttonLabel: String? = null
|
||||
|
||||
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var itemLongClickListener: View.OnLongClickListener? = null
|
||||
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var itemClickListener: View.OnClickListener? = null
|
||||
|
@ -61,13 +64,17 @@ abstract class SuggestedRoomItem : VectorEpoxyModel<SuggestedRoomItem.Holder>()
|
|||
itemLongClickListener?.onLongClick(it) ?: false
|
||||
}
|
||||
holder.titleView.text = matrixItem.getBestName()
|
||||
avatarRenderer.render(matrixItem, holder.avatarImageView)
|
||||
if (space) {
|
||||
avatarRenderer.renderSpace(matrixItem, holder.avatarImageView)
|
||||
} else {
|
||||
avatarRenderer.render(matrixItem, holder.avatarImageView)
|
||||
}
|
||||
|
||||
holder.descriptionText.text = span {
|
||||
span {
|
||||
apply {
|
||||
val tintColor = ThemeUtils.getColor(holder.view.context, R.attr.riotx_text_secondary)
|
||||
ContextCompat.getDrawable(holder.view.context, R.drawable.ic_room_profile_member_list)
|
||||
ContextCompat.getDrawable(holder.view.context, R.drawable.ic_member_small)
|
||||
?.apply {
|
||||
ThemeUtils.tintDrawableWithColor(this, tintColor)
|
||||
}?.let {
|
||||
|
@ -83,6 +90,8 @@ abstract class SuggestedRoomItem : VectorEpoxyModel<SuggestedRoomItem.Holder>()
|
|||
}
|
||||
}
|
||||
|
||||
holder.joinButton.text = buttonLabel
|
||||
|
||||
if (loading) {
|
||||
holder.joinButtonLoading.isVisible = true
|
||||
holder.joinButton.isInvisible = true
|
||||
|
@ -93,8 +102,8 @@ abstract class SuggestedRoomItem : VectorEpoxyModel<SuggestedRoomItem.Holder>()
|
|||
|
||||
holder.joinButton.setOnClickListener {
|
||||
// local echo
|
||||
holder.joinButtonLoading.isVisible = true
|
||||
holder.joinButton.isInvisible = true
|
||||
holder.joinButton.isEnabled = false
|
||||
holder.view.postDelayed({ holder.joinButton.isEnabled = true }, 400)
|
||||
buttonClickListener?.onClick(it)
|
||||
}
|
||||
}
|
|
@ -71,6 +71,7 @@ import im.vector.app.features.roomprofile.RoomProfileActivity
|
|||
import im.vector.app.features.settings.VectorPreferences
|
||||
import im.vector.app.features.settings.VectorSettingsActivity
|
||||
import im.vector.app.features.share.SharedData
|
||||
import im.vector.app.features.spaces.SpaceExploreActivity
|
||||
import im.vector.app.features.spaces.SpacePreviewActivity
|
||||
import im.vector.app.features.terms.ReviewTermsActivity
|
||||
import im.vector.app.features.widgets.WidgetActivity
|
||||
|
@ -250,8 +251,17 @@ class DefaultNavigator @Inject constructor(
|
|||
}
|
||||
|
||||
override fun openRoomDirectory(context: Context, initialFilter: String) {
|
||||
val intent = RoomDirectoryActivity.getIntent(context, initialFilter)
|
||||
context.startActivity(intent)
|
||||
val selectedSpace = selectedSpaceDataSource.currentValue?.orNull()?.let {
|
||||
sessionHolder.getSafeActiveSession()?.getRoomSummary(it.roomId)
|
||||
}
|
||||
if (selectedSpace == null) {
|
||||
val intent = RoomDirectoryActivity.getIntent(context, initialFilter)
|
||||
context.startActivity(intent)
|
||||
} else {
|
||||
SpaceExploreActivity.newIntent(context, selectedSpace.roomId).let {
|
||||
context.startActivity(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun openCreateRoom(context: Context, initialName: String) {
|
||||
|
|
|
@ -20,47 +20,60 @@ import android.content.Context
|
|||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import com.airbnb.mvrx.MvRx
|
||||
import com.airbnb.mvrx.viewModel
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.di.ScreenComponent
|
||||
import im.vector.app.core.extensions.commitTransaction
|
||||
import im.vector.app.core.platform.VectorBaseActivity
|
||||
import im.vector.app.databinding.ActivitySimpleBinding
|
||||
import im.vector.app.features.spaces.explore.SpaceDirectoryArgs
|
||||
import im.vector.app.features.spaces.explore.SpaceDirectoryFragment
|
||||
import im.vector.app.features.spaces.preview.SpacePreviewArgs
|
||||
import im.vector.app.features.spaces.preview.SpacePreviewFragment
|
||||
import im.vector.app.features.spaces.explore.SpaceDirectoryState
|
||||
import im.vector.app.features.spaces.explore.SpaceDirectoryViewEvents
|
||||
import im.vector.app.features.spaces.explore.SpaceDirectoryViewModel
|
||||
import javax.inject.Inject
|
||||
|
||||
class SpaceExploreActivity : VectorBaseActivity<ActivitySimpleBinding>() {
|
||||
class SpaceExploreActivity : VectorBaseActivity<ActivitySimpleBinding>(), SpaceDirectoryViewModel.Factory {
|
||||
|
||||
@Inject lateinit var spaceDirectoryViewModelFactory: SpaceDirectoryViewModel.Factory
|
||||
|
||||
override fun injectWith(injector: ScreenComponent) {
|
||||
injector.inject(this)
|
||||
}
|
||||
|
||||
override fun getBinding(): ActivitySimpleBinding = ActivitySimpleBinding.inflate(layoutInflater)
|
||||
// lateinit var sharedActionViewModel: SpacePreviewSharedActionViewModel
|
||||
|
||||
override fun getTitleRes(): Int = R.string.space_explore_activity_title
|
||||
|
||||
val sharedViewModel: SpaceDirectoryViewModel by viewModel()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
// sharedActionViewModel = viewModelProvider.get(SpacePreviewSharedActionViewModel::class.java)
|
||||
// sharedActionViewModel
|
||||
// .observe()
|
||||
// .subscribe { action ->
|
||||
// when (action) {
|
||||
// SpacePreviewSharedAction.DismissAction -> finish()
|
||||
// SpacePreviewSharedAction.ShowModalLoading -> showWaitingView()
|
||||
// SpacePreviewSharedAction.HideModalLoading -> hideWaitingView()
|
||||
// is SpacePreviewSharedAction.ShowErrorMessage -> action.error?.let { showSnackbar(it) }
|
||||
// }
|
||||
// }.disposeOnDestroy()
|
||||
|
||||
if (isFirstCreation()) {
|
||||
val simpleName = SpaceDirectoryFragment::class.java.simpleName
|
||||
val args = intent?.getParcelableExtra<SpacePreviewArgs>(MvRx.KEY_ARG)
|
||||
val args = intent?.getParcelableExtra<SpaceDirectoryArgs>(MvRx.KEY_ARG)
|
||||
if (supportFragmentManager.findFragmentByTag(simpleName) == null) {
|
||||
supportFragmentManager.commitTransaction {
|
||||
replace(R.id.simpleFragmentContainer,
|
||||
SpacePreviewFragment::class.java,
|
||||
SpaceDirectoryFragment::class.java,
|
||||
Bundle().apply { this.putParcelable(MvRx.KEY_ARG, args) },
|
||||
simpleName
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sharedViewModel.observeViewEvents {
|
||||
when (it) {
|
||||
SpaceDirectoryViewEvents.Dismiss -> {
|
||||
finish()
|
||||
}
|
||||
is SpaceDirectoryViewEvents.NavigateToRoom -> {
|
||||
navigator.openRoom(this, it.roomId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@ -70,4 +83,7 @@ class SpaceExploreActivity : VectorBaseActivity<ActivitySimpleBinding>() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun create(initialState: SpaceDirectoryState): SpaceDirectoryViewModel =
|
||||
spaceDirectoryViewModelFactory.create(initialState)
|
||||
}
|
||||
|
|
|
@ -16,38 +16,77 @@
|
|||
|
||||
package im.vector.app.features.spaces.explore
|
||||
|
||||
import android.view.View
|
||||
import com.airbnb.epoxy.TypedEpoxyController
|
||||
import com.airbnb.mvrx.Fail
|
||||
import com.airbnb.mvrx.Incomplete
|
||||
import com.airbnb.mvrx.Success
|
||||
import im.vector.app.core.epoxy.errorWithRetryItem
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.epoxy.loadingItem
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
import im.vector.app.core.ui.list.genericFooterItem
|
||||
import im.vector.app.features.home.AvatarRenderer
|
||||
import im.vector.app.features.home.room.list.spaceChildInfoItem
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomType
|
||||
import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo
|
||||
import org.matrix.android.sdk.api.util.MatrixItem
|
||||
import javax.inject.Inject
|
||||
|
||||
class SpaceDirectoryController : TypedEpoxyController<SpaceDirectoryState>() {
|
||||
class SpaceDirectoryController @Inject constructor(
|
||||
private val avatarRenderer: AvatarRenderer,
|
||||
private val stringProvider: StringProvider
|
||||
) : TypedEpoxyController<SpaceDirectoryState>() {
|
||||
|
||||
interface InteractionListener {
|
||||
fun onButtonClick(spaceChildInfo: SpaceChildInfo)
|
||||
fun onSpaceChildClick(spaceChildInfo: SpaceChildInfo)
|
||||
}
|
||||
|
||||
var listener: InteractionListener? = null
|
||||
|
||||
override fun buildModels(data: SpaceDirectoryState?) {
|
||||
when (data?.summary) {
|
||||
is Success -> {
|
||||
// val directories = roomDirectoryListCreator.computeDirectories(asyncThirdPartyProtocol())
|
||||
//
|
||||
// directories.forEach {
|
||||
// buildDirectory(it)
|
||||
// }
|
||||
val results = data?.spaceSummaryApiResult
|
||||
|
||||
if (results is Incomplete) {
|
||||
loadingItem {
|
||||
id("loading")
|
||||
}
|
||||
is Incomplete -> {
|
||||
loadingItem {
|
||||
id("loading")
|
||||
} else {
|
||||
val flattenChildInfo = results?.invoke()
|
||||
?.filter {
|
||||
it.parentRoomId == (data.hierarchyStack.lastOrNull() ?: data.spaceId)
|
||||
}
|
||||
?: emptyList()
|
||||
|
||||
if (flattenChildInfo.isEmpty()) {
|
||||
genericFooterItem {
|
||||
id("empty_footer")
|
||||
stringProvider.getString(R.string.no_result_placeholder)
|
||||
}
|
||||
}
|
||||
is Fail -> {
|
||||
errorWithRetryItem {
|
||||
id("error")
|
||||
// text(errorFormatter.toHumanReadable(asyncThirdPartyProtocol.error))
|
||||
// listener { callback?.retry() }
|
||||
} else {
|
||||
flattenChildInfo.forEach { info ->
|
||||
val isSpace = info.roomType == RoomType.SPACE
|
||||
val isJoined = data?.joinedRoomsIds?.contains(info.childRoomId) == true
|
||||
val isLoading = data?.changeMembershipStates?.get(info.childRoomId)?.isInProgress() ?: false
|
||||
spaceChildInfoItem {
|
||||
id(info.childRoomId)
|
||||
matrixItem(MatrixItem.RoomItem(info.childRoomId, info.name, info.avatarUrl))
|
||||
avatarRenderer(avatarRenderer)
|
||||
topic(info.topic)
|
||||
memberCount(info.activeMemberCount ?: 0)
|
||||
space(isSpace)
|
||||
loading(isLoading)
|
||||
buttonLabel(
|
||||
if (isJoined) stringProvider.getString(R.string.action_open)
|
||||
else stringProvider.getString(R.string.join)
|
||||
)
|
||||
apply {
|
||||
if (isSpace) {
|
||||
itemClickListener(View.OnClickListener { listener?.onSpaceChildClick(info) })
|
||||
}
|
||||
}
|
||||
buttonClickListener(View.OnClickListener { listener?.onButtonClick(info) })
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,20 +16,74 @@
|
|||
|
||||
package im.vector.app.features.spaces.explore
|
||||
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.airbnb.mvrx.activityViewModel
|
||||
import com.airbnb.mvrx.withState
|
||||
import im.vector.app.core.extensions.cleanup
|
||||
import im.vector.app.core.extensions.configureWith
|
||||
import im.vector.app.core.platform.OnBackPressed
|
||||
import im.vector.app.core.platform.VectorBaseFragment
|
||||
import im.vector.app.databinding.FragmentRoomDirectoryPickerBinding
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomType
|
||||
import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo
|
||||
import javax.inject.Inject
|
||||
|
||||
@Parcelize
|
||||
data class SpaceDirectoryArgs(
|
||||
val spaceId: String
|
||||
) : Parcelable
|
||||
|
||||
class SpaceDirectoryFragment : VectorBaseFragment<FragmentRoomDirectoryPickerBinding>() {
|
||||
class SpaceDirectoryFragment @Inject constructor(
|
||||
private val epoxyController: SpaceDirectoryController
|
||||
) : VectorBaseFragment<FragmentRoomDirectoryPickerBinding>(),
|
||||
SpaceDirectoryController.InteractionListener,
|
||||
OnBackPressed {
|
||||
|
||||
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?) =
|
||||
FragmentRoomDirectoryPickerBinding.inflate(layoutInflater, container, false)
|
||||
|
||||
private val viewModel by activityViewModel(SpaceDirectoryViewModel::class)
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
vectorBaseActivity.setSupportActionBar(views.toolbar)
|
||||
|
||||
vectorBaseActivity.supportActionBar?.let {
|
||||
it.setDisplayShowHomeEnabled(true)
|
||||
it.setDisplayHomeAsUpEnabled(true)
|
||||
}
|
||||
epoxyController.listener = this
|
||||
views.roomDirectoryPickerList.configureWith(epoxyController)
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
epoxyController.listener = null
|
||||
views.roomDirectoryPickerList.cleanup()
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
override fun invalidate() = withState(viewModel) {
|
||||
epoxyController.setData(it)
|
||||
}
|
||||
|
||||
override fun onButtonClick(spaceChildInfo: SpaceChildInfo) {
|
||||
viewModel.handle(SpaceDirectoryViewAction.JoinOrOpen(spaceChildInfo))
|
||||
}
|
||||
|
||||
override fun onSpaceChildClick(spaceChildInfo: SpaceChildInfo) {
|
||||
if (spaceChildInfo.roomType == RoomType.SPACE) {
|
||||
viewModel.handle(SpaceDirectoryViewAction.ExploreSubSpace(spaceChildInfo))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBackPressed(toolbarButton: Boolean): Boolean {
|
||||
viewModel.handle(SpaceDirectoryViewAction.HandleBack)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,9 +19,12 @@ package im.vector.app.features.spaces.explore
|
|||
import androidx.lifecycle.viewModelScope
|
||||
import com.airbnb.mvrx.ActivityViewModelContext
|
||||
import com.airbnb.mvrx.Async
|
||||
import com.airbnb.mvrx.Fail
|
||||
import com.airbnb.mvrx.FragmentViewModelContext
|
||||
import com.airbnb.mvrx.Loading
|
||||
import com.airbnb.mvrx.MvRxState
|
||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
||||
import com.airbnb.mvrx.Success
|
||||
import com.airbnb.mvrx.Uninitialized
|
||||
import com.airbnb.mvrx.ViewModelContext
|
||||
import dagger.assisted.Assisted
|
||||
|
@ -30,38 +33,54 @@ import dagger.assisted.AssistedInject
|
|||
import im.vector.app.core.platform.VectorViewEvents
|
||||
import im.vector.app.core.platform.VectorViewModel
|
||||
import im.vector.app.core.platform.VectorViewModelAction
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.matrix.android.sdk.api.query.QueryStringValue
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
|
||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomType
|
||||
import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo
|
||||
import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams
|
||||
import org.matrix.android.sdk.api.util.Optional
|
||||
import org.matrix.android.sdk.internal.util.awaitCallback
|
||||
import org.matrix.android.sdk.rx.rx
|
||||
import org.matrix.android.sdk.rx.unwrap
|
||||
import timber.log.Timber
|
||||
|
||||
data class SpaceDirectoryState(
|
||||
// The current filter
|
||||
val spaceId: String,
|
||||
val currentFilter: String = "",
|
||||
val summary: Async<RoomSummary> = Uninitialized,
|
||||
val spaceSummary: Async<RoomSummary> = Uninitialized,
|
||||
val spaceSummaryApiResult: Async<List<SpaceChildInfo>> = Uninitialized,
|
||||
val childList: List<SpaceChildInfo> = emptyList(),
|
||||
val hierarchyStack: List<String> = emptyList(),
|
||||
// True if more result are available server side
|
||||
val hasMore: Boolean = false,
|
||||
// Set of joined roomId / spaces,
|
||||
val joinedRoomsIds: Set<String> = emptySet()
|
||||
val joinedRoomsIds: Set<String> = emptySet(),
|
||||
// keys are room alias or roomId
|
||||
val changeMembershipStates: Map<String, ChangeMembershipState> = emptyMap()
|
||||
) : MvRxState {
|
||||
constructor(args: SpaceDirectoryArgs) : this(spaceId = args.spaceId)
|
||||
constructor(args: SpaceDirectoryArgs) : this(
|
||||
spaceId = args.spaceId
|
||||
)
|
||||
}
|
||||
|
||||
sealed class SpaceDirectoryViewAction : VectorViewModelAction
|
||||
sealed class SpaceDirectoryViewAction : VectorViewModelAction {
|
||||
data class ExploreSubSpace(val spaceChildInfo: SpaceChildInfo) : SpaceDirectoryViewAction()
|
||||
data class JoinOrOpen(val spaceChildInfo: SpaceChildInfo) : SpaceDirectoryViewAction()
|
||||
object HandleBack : SpaceDirectoryViewAction()
|
||||
}
|
||||
|
||||
sealed class SpaceDirectoryViewEvents : VectorViewEvents
|
||||
sealed class SpaceDirectoryViewEvents : VectorViewEvents {
|
||||
object Dismiss : SpaceDirectoryViewEvents()
|
||||
data class NavigateToRoom(val roomId: String) : SpaceDirectoryViewEvents()
|
||||
}
|
||||
|
||||
class SpaceDirectoryViewModel @AssistedInject constructor(
|
||||
@Assisted initialState: SpaceDirectoryState,
|
||||
private val session: Session
|
||||
) : VectorViewModel<SpaceDirectoryState, VectorViewModelAction, SpaceDirectoryViewEvents>(initialState) {
|
||||
) : VectorViewModel<SpaceDirectoryState, SpaceDirectoryViewAction, SpaceDirectoryViewEvents>(initialState) {
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
|
@ -79,24 +98,113 @@ class SpaceDirectoryViewModel @AssistedInject constructor(
|
|||
}
|
||||
|
||||
init {
|
||||
val queryParams = roomSummaryQueryParams {
|
||||
roomId = QueryStringValue.Equals(initialState.spaceId)
|
||||
|
||||
val spaceSum = session.getRoomSummary(initialState.spaceId)
|
||||
setState {
|
||||
copy(
|
||||
childList = spaceSum?.children ?: emptyList(),
|
||||
spaceSummaryApiResult = Loading()
|
||||
)
|
||||
}
|
||||
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
session
|
||||
.rx()
|
||||
.liveSpaceSummaries(queryParams)
|
||||
.observeOn(Schedulers.computation())
|
||||
.map { sum -> Optional.from(sum.firstOrNull()) }
|
||||
.unwrap()
|
||||
.execute { async ->
|
||||
copy(summary = async)
|
||||
try {
|
||||
val query = session.spaceService().querySpaceChildren(initialState.spaceId)
|
||||
setState {
|
||||
copy(
|
||||
spaceSummaryApiResult = Success(query.second)
|
||||
)
|
||||
}
|
||||
} catch (failure: Throwable) {
|
||||
setState {
|
||||
copy(
|
||||
spaceSummaryApiResult = Fail(failure)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
observeJoinedRooms()
|
||||
observeMembershipChanges()
|
||||
}
|
||||
|
||||
private fun observeJoinedRooms() {
|
||||
val queryParams = roomSummaryQueryParams {
|
||||
memberships = listOf(Membership.JOIN)
|
||||
}
|
||||
session
|
||||
.rx()
|
||||
.liveRoomSummaries(queryParams)
|
||||
.subscribe { list ->
|
||||
val joinedRoomIds = list
|
||||
?.map { it.roomId }
|
||||
?.toSet()
|
||||
?: emptySet()
|
||||
|
||||
setState {
|
||||
copy(joinedRoomsIds = joinedRoomIds)
|
||||
}
|
||||
}
|
||||
.disposeOnClear()
|
||||
}
|
||||
|
||||
private fun observeMembershipChanges() {
|
||||
session.rx()
|
||||
.liveRoomChangeMembershipState()
|
||||
.subscribe {
|
||||
setState { copy(changeMembershipStates = it) }
|
||||
}
|
||||
.disposeOnClear()
|
||||
}
|
||||
|
||||
override fun handle(action: SpaceDirectoryViewAction) {
|
||||
when (action) {
|
||||
is SpaceDirectoryViewAction.ExploreSubSpace -> {
|
||||
setState {
|
||||
copy(hierarchyStack = hierarchyStack + listOf(action.spaceChildInfo.childRoomId))
|
||||
}
|
||||
}
|
||||
SpaceDirectoryViewAction.HandleBack -> {
|
||||
withState {
|
||||
if (it.hierarchyStack.isEmpty()) {
|
||||
_viewEvents.post(SpaceDirectoryViewEvents.Dismiss)
|
||||
} else {
|
||||
setState {
|
||||
copy(
|
||||
hierarchyStack = hierarchyStack.dropLast(1)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
is SpaceDirectoryViewAction.JoinOrOpen -> {
|
||||
handleJoinOrOpen(action.spaceChildInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun handle(action: VectorViewModelAction) {
|
||||
TODO("Not yet implemented")
|
||||
private fun handleJoinOrOpen(spaceChildInfo: SpaceChildInfo) = withState { state ->
|
||||
val isSpace = spaceChildInfo.roomType == RoomType.SPACE
|
||||
if (state.joinedRoomsIds.contains(spaceChildInfo.childRoomId)) {
|
||||
if (isSpace) {
|
||||
handle(SpaceDirectoryViewAction.ExploreSubSpace(spaceChildInfo))
|
||||
} else {
|
||||
_viewEvents.post(SpaceDirectoryViewEvents.NavigateToRoom(spaceChildInfo.childRoomId))
|
||||
}
|
||||
} else {
|
||||
// join
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
if (isSpace) {
|
||||
session.spaceService().joinSpace(spaceChildInfo.childRoomId, null, spaceChildInfo.viaServers)
|
||||
} else {
|
||||
awaitCallback<Unit> {
|
||||
session.joinRoom(spaceChildInfo.childRoomId, null, spaceChildInfo.viaServers, it)
|
||||
}
|
||||
}
|
||||
} catch (failure: Throwable) {
|
||||
Timber.e(failure, "## Space: Failed to join room or subsapce")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
10
vector/src/main/res/drawable/ic_member_small.xml
Normal file
10
vector/src/main/res/drawable/ic_member_small.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="16dp"
|
||||
android:height="16dp"
|
||||
android:viewportWidth="16"
|
||||
android:viewportHeight="16">
|
||||
<path
|
||||
android:pathData="M16,8C16,12.4183 12.4183,16 8,16C3.5817,16 0,12.4183 0,8C0,3.5817 3.5817,0 8,0C12.4183,0 16,3.5817 16,8ZM7.3333,6.1667C7.3333,7.3633 6.4379,8.3333 5.3333,8.3333C4.2288,8.3333 3.3333,7.3633 3.3333,6.1667C3.3333,4.97 4.2288,4 5.3333,4C6.4379,4 7.3333,4.97 7.3333,6.1667ZM13.2564,7.2315C13.2564,8.2798 12.472,9.1296 11.5043,9.1296C10.5366,9.1296 9.7522,8.2798 9.7522,7.2315C9.7522,6.1832 10.5366,5.3333 11.5043,5.3333C12.472,5.3333 13.2564,6.1832 13.2564,7.2315ZM8.6666,12.5969C8.6666,11.0353 7.5698,9.7298 6.1045,9.4089C5.855,9.3593 5.5971,9.3333 5.3333,9.3333C3.9862,9.3333 2.7946,10.0107 2.0698,11.0489C3.1767,13.1974 5.4167,14.6667 8,14.6667C8.225,14.6667 8.4474,14.6555 8.6666,14.6338L8.6666,12.5969ZM13.9475,11.015C13.1422,12.6005 11.7216,13.8208 10,14.3615L10,12.5969C10,11.8856 9.8385,11.2121 9.5501,10.611C10.1084,10.2288 10.7807,10.0057 11.5043,10.0057C12.4545,10.0057 13.3163,10.3904 13.9475,11.015Z"
|
||||
android:fillColor="#737D8C"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
|
@ -32,5 +32,4 @@
|
|||
tools:listitem="@layout/item_room_directory" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
|
@ -5,6 +5,7 @@
|
|||
android:id="@+id/itemRoomLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:foreground="?attr/selectableItemBackground"
|
||||
android:background="?riotx_background">
|
||||
|
||||
<FrameLayout
|
||||
|
|
|
@ -3294,4 +3294,5 @@
|
|||
|
||||
<string name="suggested_rooms_pills_on_empty_text">You’re not in any rooms yet. Below are some suggested rooms, but you can see more with the green button bottom right.</string>
|
||||
|
||||
<string name="space_explore_activity_title">Explore rooms</string>
|
||||
</resources>
|
||||
|
|
Loading…
Add table
Reference in a new issue