mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2025-03-17 19:58:57 +03:00
Suggested Space support
This commit is contained in:
parent
0da9be327a
commit
802853d205
22 changed files with 430 additions and 21 deletions
|
@ -21,14 +21,17 @@ import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
|||
|
||||
interface Space {
|
||||
|
||||
fun asRoom() : Room
|
||||
fun asRoom(): Room
|
||||
|
||||
/**
|
||||
* A current snapshot of [RoomSummary] associated with the space
|
||||
*/
|
||||
fun spaceSummary(): RoomSummary?
|
||||
|
||||
suspend fun addChildren(roomId: String, viaServers: List<String>, order: String?, autoJoin: Boolean = false)
|
||||
suspend fun addChildren(roomId: String, viaServers: List<String>,
|
||||
order: String?,
|
||||
autoJoin: Boolean = false,
|
||||
suggested: Boolean? = false)
|
||||
|
||||
suspend fun removeRoom(roomId: String)
|
||||
|
||||
|
|
|
@ -46,7 +46,14 @@ data class SpaceChildContent(
|
|||
* be automatically joined by members of that space.
|
||||
* (This is not a force-join, which are descoped for a future MSC; the user can subsequently part these room if they desire.)
|
||||
*/
|
||||
@Json(name = "auto_join") val autoJoin: Boolean? = false
|
||||
@Json(name = "auto_join") val autoJoin: Boolean? = false,
|
||||
|
||||
/**
|
||||
* If `suggested` is set to `true`, that indicates that the child should be advertised to
|
||||
* members of the space by the client. This could be done by showing them eagerly
|
||||
* in the room list. This is should be ignored if `auto_join` is set to `true`.
|
||||
*/
|
||||
@Json(name = "suggested") val suggested: Boolean? = false
|
||||
) {
|
||||
/**
|
||||
* Orders which are not strings, or do not consist solely of ascii characters in the range \x20 (space) to \x7F (~),
|
||||
|
|
|
@ -36,14 +36,18 @@ internal class DefaultSpace(private val room: Room, private val spaceSummaryData
|
|||
return spaceSummaryDataSource.getSpaceSummary(asRoom().roomId)
|
||||
}
|
||||
|
||||
override suspend fun addChildren(roomId: String, viaServers: List<String>, order: String?, autoJoin: Boolean) {
|
||||
override suspend fun addChildren(roomId: String, viaServers: List<String>,
|
||||
order: String?,
|
||||
autoJoin: Boolean,
|
||||
suggested: Boolean?) {
|
||||
asRoom().sendStateEvent(
|
||||
eventType = EventType.STATE_SPACE_CHILD,
|
||||
stateKey = roomId,
|
||||
body = SpaceChildContent(
|
||||
via = viaServers,
|
||||
autoJoin = autoJoin,
|
||||
order = order
|
||||
order = order,
|
||||
suggested = suggested
|
||||
).toContent()
|
||||
)
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ internal interface SpaceApi {
|
|||
*
|
||||
* MSC 2946 https://github.com/matrix-org/matrix-doc/blob/kegan/spaces-summary/proposals/2946-spaces-summary.md
|
||||
*/
|
||||
@POST(NetworkConstants.URI_API_PREFIX_PATH_UNSTABLE + "rooms/{roomId}/spaces")
|
||||
@POST(NetworkConstants.URI_API_PREFIX_PATH_UNSTABLE + "org.matrix.msc2946/rooms/{roomId}/spaces")
|
||||
fun getSpaces(@Path("roomId") spaceId: String,
|
||||
@Body params: SpaceSummaryParams
|
||||
): Call<SpacesResponse>
|
||||
|
|
|
@ -26,5 +26,8 @@ internal data class SpaceSummaryParams(
|
|||
/** The maximum number of rooms/subspaces to return, server can override this, default: 100 */
|
||||
@Json(name = "limit") val limit: Int = 100,
|
||||
/** A token to use if this is a subsequent HTTP hit, default: "".*/
|
||||
@Json(name = "batch") val batch: String = ""
|
||||
@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
|
||||
|
||||
)
|
||||
|
|
|
@ -143,7 +143,7 @@ internal class ReadReceiptHandler @Inject constructor(
|
|||
@Suppress("UNCHECKED_CAST")
|
||||
val content = dataFromFile
|
||||
.events
|
||||
.firstOrNull { it.type == EventType.RECEIPT }
|
||||
?.firstOrNull { it.type == EventType.RECEIPT }
|
||||
?.content as? ReadReceiptContent
|
||||
|
||||
if (content == null) {
|
||||
|
|
|
@ -33,6 +33,17 @@ class AppStateHandler @Inject constructor() : LifecycleObserver {
|
|||
|
||||
private val compositeDisposable = CompositeDisposable()
|
||||
|
||||
init {
|
||||
// restore current space from ui state
|
||||
sessionDataSource.currentValue?.orNull()?.let { session ->
|
||||
uiStateRepository.getSelectedSpace(session.sessionId)?.let { selectedSpaceId ->
|
||||
session.getRoomSummary(selectedSpaceId)?.let {
|
||||
selectedSpaceDataSource.post(Option.just(it))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
|
||||
fun entersForeground() {
|
||||
}
|
||||
|
|
|
@ -37,6 +37,8 @@ import im.vector.app.features.crypto.verification.IncomingVerificationRequestHan
|
|||
import im.vector.app.features.grouplist.SelectedGroupDataSource
|
||||
import im.vector.app.features.grouplist.SelectedSpaceDataSource
|
||||
import im.vector.app.features.home.AvatarRenderer
|
||||
import im.vector.app.features.home.CurrentSpaceSuggestedRoomListDataSource
|
||||
import im.vector.app.features.home.HomeRoomListDataSource
|
||||
import im.vector.app.features.home.room.detail.RoomDetailPendingActionStore
|
||||
import im.vector.app.features.home.room.detail.timeline.helper.MatrixItemColorProvider
|
||||
import im.vector.app.features.home.room.detail.timeline.helper.RoomSummariesHolder
|
||||
|
@ -118,6 +120,8 @@ interface VectorComponent {
|
|||
|
||||
fun selectedSpaceStore(): SelectedSpaceDataSource
|
||||
|
||||
fun currentSpaceSuggestedRoomListDataSource(): CurrentSpaceSuggestedRoomListDataSource
|
||||
|
||||
fun roomDetailPendingActionStore(): RoomDetailPendingActionStore
|
||||
|
||||
fun activeSessionObservableStore(): ActiveSessionDataSource
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* 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.app.features.home
|
||||
|
||||
import im.vector.app.core.utils.BehaviorDataSource
|
||||
import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class CurrentSpaceSuggestedRoomListDataSource @Inject constructor() : BehaviorDataSource<List<SpaceChildInfo>>()
|
|
@ -58,7 +58,6 @@ import org.matrix.android.sdk.api.session.group.model.GroupSummary
|
|||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
import org.matrix.android.sdk.api.util.toMatrixItem
|
||||
import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
private const val INDEX_PEOPLE = 0
|
||||
|
@ -363,7 +362,7 @@ class HomeDetailFragment @Inject constructor(
|
|||
}
|
||||
|
||||
override fun invalidate() = withState(viewModel) {
|
||||
Timber.v(it.toString())
|
||||
// Timber.v(it.toString())
|
||||
views.bottomNavigationView.getOrCreateBadge(R.id.bottom_action_people).render(it.notificationCountPeople, it.notificationHighlightPeople)
|
||||
views.bottomNavigationView.getOrCreateBadge(R.id.bottom_action_rooms).render(it.notificationCountRooms, it.notificationHighlightRooms)
|
||||
views.bottomNavigationView.getOrCreateBadge(R.id.bottom_action_notification).render(it.notificationCountCatchup, it.notificationHighlightCatchup)
|
||||
|
|
|
@ -29,4 +29,5 @@ sealed class RoomListAction : VectorViewModelAction {
|
|||
data class ChangeRoomNotificationState(val roomId: String, val notificationState: RoomNotificationState) : RoomListAction()
|
||||
data class ToggleTag(val roomId: String, val tag: String) : RoomListAction()
|
||||
data class LeaveRoom(val roomId: String) : RoomListAction()
|
||||
data class JoinSuggestedRoom(val roomId: String, val viaServers: List<String>?) : RoomListAction()
|
||||
}
|
||||
|
|
|
@ -52,6 +52,7 @@ import im.vector.app.features.notifications.NotificationDrawerManager
|
|||
import kotlinx.parcelize.Parcelize
|
||||
import org.matrix.android.sdk.api.extensions.orTrue
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo
|
||||
import org.matrix.android.sdk.api.session.room.model.tag.RoomTag
|
||||
import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState
|
||||
import javax.inject.Inject
|
||||
|
@ -421,6 +422,10 @@ class RoomListFragment @Inject constructor(
|
|||
roomListViewModel.handle(RoomListAction.AcceptInvitation(room))
|
||||
}
|
||||
|
||||
override fun onJoinSuggestedRoom(room: SpaceChildInfo) {
|
||||
roomListViewModel.handle(RoomListAction.JoinSuggestedRoom(room.childRoomId, room.viaServers))
|
||||
}
|
||||
|
||||
override fun onRejectRoomInvitation(room: RoomSummary) {
|
||||
notificationDrawerManager.clearMemberShipNotificationForRoom(room.roomId)
|
||||
roomListViewModel.handle(RoomListAction.RejectInvitation(room))
|
||||
|
|
|
@ -18,8 +18,11 @@ package im.vector.app.features.home.room.list
|
|||
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.airbnb.mvrx.Fail
|
||||
import com.airbnb.mvrx.FragmentViewModelContext
|
||||
import com.airbnb.mvrx.Loading
|
||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
||||
import com.airbnb.mvrx.Success
|
||||
import com.airbnb.mvrx.ViewModelContext
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.extensions.exhaustive
|
||||
|
@ -166,6 +169,7 @@ class RoomListViewModel @Inject constructor(
|
|||
is RoomListAction.ChangeRoomNotificationState -> handleChangeNotificationMode(action)
|
||||
is RoomListAction.ToggleTag -> handleToggleTag(action)
|
||||
is RoomListAction.ToggleSection -> handleToggleSection(action.section)
|
||||
is RoomListAction.JoinSuggestedRoom -> handleJoinSuggestedRoom(action)
|
||||
}.exhaustive
|
||||
}
|
||||
|
||||
|
@ -316,6 +320,38 @@ class RoomListViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun handleJoinSuggestedRoom(action: RoomListAction.JoinSuggestedRoom) {
|
||||
setState {
|
||||
copy(
|
||||
suggestedRoomJoiningState = this.suggestedRoomJoiningState.toMutableMap().apply {
|
||||
this[action.roomId] = Loading()
|
||||
}.toMap()
|
||||
)
|
||||
}
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
awaitCallback<Unit> {
|
||||
session.joinRoom(action.roomId, null, action.viaServers ?: emptyList(), it)
|
||||
}
|
||||
setState {
|
||||
copy(
|
||||
suggestedRoomJoiningState = this.suggestedRoomJoiningState.toMutableMap().apply {
|
||||
this[action.roomId] = Success(Unit)
|
||||
}.toMap()
|
||||
)
|
||||
}
|
||||
} catch (failure: Throwable) {
|
||||
setState {
|
||||
copy(
|
||||
suggestedRoomJoiningState = this.suggestedRoomJoiningState.toMutableMap().apply {
|
||||
this[action.roomId] = Fail(failure)
|
||||
}.toMap()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleToggleTag(action: RoomListAction.ToggleTag) {
|
||||
session.getRoom(action.roomId)?.let { room ->
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
|
@ -342,7 +378,7 @@ class RoomListViewModel @Inject constructor(
|
|||
|
||||
private fun String.otherTag(): String? {
|
||||
return when (this) {
|
||||
RoomTag.ROOM_TAG_FAVOURITE -> RoomTag.ROOM_TAG_LOW_PRIORITY
|
||||
RoomTag.ROOM_TAG_FAVOURITE -> RoomTag.ROOM_TAG_LOW_PRIORITY
|
||||
RoomTag.ROOM_TAG_LOW_PRIORITY -> RoomTag.ROOM_TAG_FAVOURITE
|
||||
else -> null
|
||||
}
|
||||
|
|
|
@ -16,6 +16,9 @@
|
|||
|
||||
package im.vector.app.features.home.room.list
|
||||
|
||||
import android.view.View
|
||||
import com.airbnb.mvrx.Async
|
||||
import com.airbnb.mvrx.Loading
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.date.DateFormatKind
|
||||
import im.vector.app.core.date.VectorDateFormatter
|
||||
|
@ -28,6 +31,8 @@ import im.vector.app.features.home.room.typing.TypingHelper
|
|||
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.SpaceChildInfo
|
||||
import org.matrix.android.sdk.api.util.MatrixItem
|
||||
import org.matrix.android.sdk.api.util.toMatrixItem
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -50,6 +55,19 @@ class RoomSummaryItemFactory @Inject constructor(private val displayableEventFor
|
|||
}
|
||||
}
|
||||
|
||||
fun createSuggestion(spaceChildInfo: SpaceChildInfo,
|
||||
suggestedRoomJoiningStates: Map<String, Async<Unit>>,
|
||||
onJoinClick: View.OnClickListener) : VectorEpoxyModel<*> {
|
||||
return SuggestedRoomItem_()
|
||||
.id("sug_${spaceChildInfo.childRoomId}")
|
||||
.matrixItem(MatrixItem.RoomItem(spaceChildInfo.childRoomId, spaceChildInfo.name, spaceChildInfo.avatarUrl))
|
||||
.avatarRenderer(avatarRenderer)
|
||||
.topic(spaceChildInfo.topic)
|
||||
.loading(suggestedRoomJoiningStates[spaceChildInfo.childRoomId] is Loading)
|
||||
.memberCount(spaceChildInfo.activeMemberCount ?: 0)
|
||||
.buttonClickListener(onJoinClick)
|
||||
}
|
||||
|
||||
private fun createInvitationItem(roomSummary: RoomSummary,
|
||||
changeMembershipState: ChangeMembershipState,
|
||||
listener: RoomListListener?): VectorEpoxyModel<*> {
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* 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.app.features.home.room.list
|
||||
|
||||
import android.view.HapticFeedbackConstants
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Button
|
||||
import android.widget.ImageView
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.isInvisible
|
||||
import androidx.core.view.isVisible
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.epoxy.VectorEpoxyHolder
|
||||
import im.vector.app.core.epoxy.VectorEpoxyModel
|
||||
import im.vector.app.features.home.AvatarRenderer
|
||||
import im.vector.app.features.themes.ThemeUtils
|
||||
import me.gujun.android.span.image
|
||||
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>() {
|
||||
|
||||
@EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer
|
||||
@EpoxyAttribute lateinit var matrixItem: MatrixItem
|
||||
|
||||
// Used only for diff calculation
|
||||
@EpoxyAttribute var topic: String? = null
|
||||
|
||||
@EpoxyAttribute var memberCount: Int = 0
|
||||
@EpoxyAttribute var loading: Boolean = false
|
||||
|
||||
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var itemLongClickListener: View.OnLongClickListener? = null
|
||||
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var itemClickListener: View.OnClickListener? = null
|
||||
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var buttonClickListener: View.OnClickListener? = null
|
||||
|
||||
override fun bind(holder: Holder) {
|
||||
super.bind(holder)
|
||||
holder.rootView.setOnClickListener(itemClickListener)
|
||||
holder.rootView.setOnLongClickListener {
|
||||
it.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
|
||||
itemLongClickListener?.onLongClick(it) ?: false
|
||||
}
|
||||
holder.titleView.text = matrixItem.getBestName()
|
||||
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)
|
||||
?.apply {
|
||||
ThemeUtils.tintDrawableWithColor(this, tintColor)
|
||||
}?.let {
|
||||
image(it)
|
||||
}
|
||||
}
|
||||
+" $memberCount"
|
||||
apply {
|
||||
topic?.let {
|
||||
+" - $topic"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
holder.joinButtonLoading.isVisible = true
|
||||
holder.joinButton.isInvisible = true
|
||||
} else {
|
||||
holder.joinButtonLoading.isVisible = false
|
||||
holder.joinButton.isVisible = true
|
||||
}
|
||||
|
||||
holder.joinButton.setOnClickListener {
|
||||
// local echo
|
||||
holder.joinButtonLoading.isVisible = true
|
||||
holder.joinButton.isInvisible = true
|
||||
buttonClickListener?.onClick(it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun unbind(holder: Holder) {
|
||||
holder.rootView.setOnClickListener(null)
|
||||
holder.rootView.setOnLongClickListener(null)
|
||||
avatarRenderer.clear(holder.avatarImageView)
|
||||
super.unbind(holder)
|
||||
}
|
||||
|
||||
class Holder : VectorEpoxyHolder() {
|
||||
val titleView by bind<TextView>(R.id.roomNameView)
|
||||
val joinButton by bind<Button>(R.id.joinSuggestedRoomButton)
|
||||
val joinButtonLoading by bind<ProgressBar>(R.id.joinSuggestedLoading)
|
||||
val descriptionText by bind<TextView>(R.id.suggestedRoomDescription)
|
||||
val avatarImageView by bind<ImageView>(R.id.roomAvatarImageView)
|
||||
val rootView by bind<ViewGroup>(R.id.itemRoomLayout)
|
||||
}
|
||||
}
|
|
@ -36,6 +36,7 @@ import im.vector.app.features.createdirect.DirectRoomHelper
|
|||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||
import org.matrix.android.sdk.api.failure.Failure
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.session.permalinks.PermalinkData
|
||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||
|
@ -47,6 +48,7 @@ import org.matrix.android.sdk.api.util.Optional
|
|||
import org.matrix.android.sdk.api.util.toMatrixItem
|
||||
import org.matrix.android.sdk.internal.session.room.alias.RoomAliasDescription
|
||||
import org.matrix.android.sdk.internal.util.awaitCallback
|
||||
import javax.net.ssl.HttpsURLConnection
|
||||
|
||||
class MatrixToBottomSheetViewModel @AssistedInject constructor(
|
||||
@Assisted initialState: MatrixToBottomSheetState,
|
||||
|
@ -122,8 +124,8 @@ class MatrixToBottomSheetViewModel @AssistedInject constructor(
|
|||
} else {
|
||||
session.getRoom(permalinkData.roomIdOrAlias)
|
||||
}?.roomSummary()
|
||||
|
||||
if (knownRoom != null) {
|
||||
val forceRefresh = true
|
||||
if (!forceRefresh && knownRoom != null) {
|
||||
setState {
|
||||
copy(
|
||||
roomPeekResult = Success(
|
||||
|
@ -141,7 +143,7 @@ class MatrixToBottomSheetViewModel @AssistedInject constructor(
|
|||
)
|
||||
}
|
||||
} else {
|
||||
val result = when (val peekResult = tryOrNull { resolveRoom(permalinkData.roomIdOrAlias) }) {
|
||||
val result = when (val peekResult = tryOrNull { resolveSpace(permalinkData) }) {
|
||||
is PeekResult.Success -> {
|
||||
RoomInfoResult.FullInfo(
|
||||
roomItem = MatrixItem.RoomItem(peekResult.roomId, peekResult.name, peekResult.avatarUrl),
|
||||
|
@ -188,6 +190,30 @@ class MatrixToBottomSheetViewModel @AssistedInject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun resolveSpace(permalinkData: PermalinkData.RoomLink) : PeekResult {
|
||||
try {
|
||||
return session.spaceService().querySpaceChildren(permalinkData.roomIdOrAlias).let {
|
||||
val roomSummary = it.first
|
||||
PeekResult.Success(
|
||||
roomId = roomSummary.roomId,
|
||||
alias = roomSummary.canonicalAlias,
|
||||
avatarUrl = roomSummary.avatarUrl,
|
||||
name = roomSummary.name,
|
||||
topic = roomSummary.topic,
|
||||
numJoinedMembers = roomSummary.joinedMembersCount,
|
||||
roomType = roomSummary.roomType,
|
||||
viaServers = emptyList()
|
||||
)
|
||||
}
|
||||
} catch (failure: Throwable) {
|
||||
if (failure is Failure.OtherServerError && failure.httpCode == HttpsURLConnection.HTTP_NOT_FOUND) {
|
||||
return resolveRoom(permalinkData.roomIdOrAlias)
|
||||
} else {
|
||||
throw failure
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun resolveUser(userId: String): User {
|
||||
return tryOrNull { session.resolveUser(userId) }
|
||||
// Create raw user in case the user is not searchable
|
||||
|
|
|
@ -33,6 +33,7 @@ import im.vector.app.core.platform.VectorViewModel
|
|||
import im.vector.app.core.platform.VectorViewModelAction
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
import im.vector.app.features.grouplist.SelectedSpaceDataSource
|
||||
import im.vector.app.features.ui.UiStateRepository
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.functions.BiFunction
|
||||
import kotlinx.coroutines.launch
|
||||
|
@ -70,7 +71,8 @@ data class SpaceListViewState(
|
|||
class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: SpaceListViewState,
|
||||
private val selectedSpaceDataSource: SelectedSpaceDataSource,
|
||||
private val session: Session,
|
||||
private val stringProvider: StringProvider
|
||||
private val stringProvider: StringProvider,
|
||||
private val uiStateRepository: UiStateRepository
|
||||
) : VectorViewModel<SpaceListViewState, SpaceListAction, SpaceListViewEvents>(initialState) {
|
||||
|
||||
@AssistedFactory
|
||||
|
@ -152,6 +154,7 @@ class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: Sp
|
|||
// selectedSpaceDataSource.post(Option.just(state.selectedSpace))
|
||||
// }
|
||||
setState { copy(selectedSpace = action.spaceSummary) }
|
||||
uiStateRepository.storeSelectedSpace(action.spaceSummary.roomId, session.sessionId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@ package im.vector.app.features.spaces.create
|
|||
|
||||
import android.net.Uri
|
||||
import im.vector.app.core.platform.ViewModelTask
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.session.room.failure.CreateRoomFailure
|
||||
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
|
||||
|
@ -45,8 +44,8 @@ data class CreateSpaceTaskParams(
|
|||
)
|
||||
|
||||
class CreateSpaceViewModelTask @Inject constructor(
|
||||
private val session: Session,
|
||||
private val stringProvider: StringProvider
|
||||
private val session: Session
|
||||
// private val stringProvider: StringProvider
|
||||
) : ViewModelTask<CreateSpaceTaskParams, CreateSpaceTaskResult> {
|
||||
|
||||
override suspend fun execute(params: CreateSpaceTaskParams): CreateSpaceTaskResult {
|
||||
|
@ -77,10 +76,17 @@ class CreateSpaceViewModelTask @Inject constructor(
|
|||
timeout.roomID
|
||||
}
|
||||
val via = session.sessionParams.homeServerHost?.let { listOf(it) } ?: emptyList()
|
||||
createdSpace!!.addChildren(roomId, via, null, true)
|
||||
createdSpace!!.addChildren(roomId, via, null, autoJoin = false, suggested = true)
|
||||
// set canonical
|
||||
session.spaceService().setSpaceParent(
|
||||
roomId,
|
||||
createdSpace.asRoom().roomId,
|
||||
true,
|
||||
via
|
||||
)
|
||||
childIds.add(roomId)
|
||||
} catch (failure: Throwable) {
|
||||
Timber.d("Failed to create child room in $spaceID")
|
||||
Timber.d("Space: Failed to create child room in $spaceID")
|
||||
childErrors[roomName] = failure
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ class SharedPreferencesUiStateRepository @Inject constructor(
|
|||
override fun getDisplayMode(): RoomListDisplayMode {
|
||||
return when (sharedPreferences.getInt(KEY_DISPLAY_MODE, VALUE_DISPLAY_MODE_CATCHUP)) {
|
||||
VALUE_DISPLAY_MODE_PEOPLE -> RoomListDisplayMode.PEOPLE
|
||||
VALUE_DISPLAY_MODE_ROOMS -> RoomListDisplayMode.ROOMS
|
||||
VALUE_DISPLAY_MODE_ROOMS -> RoomListDisplayMode.ROOMS
|
||||
else -> if (vectorPreferences.labAddNotificationTab()) {
|
||||
RoomListDisplayMode.NOTIFICATIONS
|
||||
} else {
|
||||
|
@ -59,10 +59,22 @@ class SharedPreferencesUiStateRepository @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun storeSelectedSpace(roomId: String?, sessionId: String) {
|
||||
sharedPreferences.edit {
|
||||
putString("$KEY_SELECTED_SPACE@$sessionId", roomId)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getSelectedSpace(sessionId: String): String? {
|
||||
return sharedPreferences.getString("$KEY_SELECTED_SPACE@$sessionId", null)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val KEY_DISPLAY_MODE = "UI_STATE_DISPLAY_MODE"
|
||||
private const val VALUE_DISPLAY_MODE_CATCHUP = 0
|
||||
private const val VALUE_DISPLAY_MODE_PEOPLE = 1
|
||||
private const val VALUE_DISPLAY_MODE_ROOMS = 2
|
||||
|
||||
private const val KEY_SELECTED_SPACE = "UI_STATE_SELECTED_SPACE"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,4 +31,8 @@ interface UiStateRepository {
|
|||
fun getDisplayMode(): RoomListDisplayMode
|
||||
|
||||
fun storeDisplayMode(displayMode: RoomListDisplayMode)
|
||||
|
||||
fun storeSelectedSpace(roomId: String?, sessionId: String)
|
||||
|
||||
fun getSelectedSpace(sessionId: String): String?
|
||||
}
|
||||
|
|
123
vector/src/main/res/layout/item_suggested_room.xml
Normal file
123
vector/src/main/res/layout/item_suggested_room.xml
Normal file
|
@ -0,0 +1,123 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/itemRoomLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?riotx_background">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/roomAvatarContainer"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="12dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/roomAvatarImageView"
|
||||
android:layout_width="56dp"
|
||||
android:layout_height="56dp"
|
||||
android:contentDescription="@string/avatar"
|
||||
tools:src="@tools:sample/avatars" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
|
||||
<!-- Margin bottom does not work, so I use space -->
|
||||
<Space
|
||||
android:id="@+id/roomAvatarBottomSpace"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="12dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/roomAvatarContainer"
|
||||
tools:layout_marginStart="20dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/roomNameView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/layout_horizontal_margin"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:duplicateParentState="true"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textColor="?riotx_text_primary"
|
||||
android:textSize="15sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintEnd_toStartOf="@+id/joinSuggestedRoomButton"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toEndOf="@id/roomAvatarContainer"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="@sample/matrix.json/data/displayName" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/suggestedRoomDescription"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="3dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textColor="?riotx_text_secondary"
|
||||
android:textSize="15sp"
|
||||
app:layout_constraintEnd_toStartOf="@id/joinSuggestedRoomButton"
|
||||
app:layout_constraintStart_toStartOf="@+id/roomNameView"
|
||||
app:layout_constraintTop_toBottomOf="@+id/roomNameView"
|
||||
tools:text="@sample/matrix.json/data/message" />
|
||||
|
||||
<!-- Margin bottom does not work, so I use space -->
|
||||
<Space
|
||||
android:id="@+id/roomLastEventBottomSpace"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="7dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/roomLastEventView"
|
||||
tools:layout_marginStart="120dp" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/joinSuggestedRoomButton"
|
||||
style="@style/VectorButtonStyleOutlined"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:maxWidth="@dimen/button_max_width"
|
||||
android:text="@string/join"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/joinSuggestedLoading"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="20dp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/joinSuggestedRoomButton"
|
||||
app:layout_constraintEnd_toEndOf="@id/joinSuggestedRoomButton"
|
||||
app:layout_constraintStart_toStartOf="@id/joinSuggestedRoomButton"
|
||||
app:layout_constraintTop_toTopOf="@id/joinSuggestedRoomButton" />
|
||||
|
||||
<androidx.constraintlayout.widget.Barrier
|
||||
android:id="@+id/roomBottomBarrier"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:barrierDirection="bottom"
|
||||
app:constraint_referenced_ids="roomAvatarBottomSpace,roomLastEventBottomSpace" />
|
||||
|
||||
<View
|
||||
android:id="@+id/roomDividerView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="1dp"
|
||||
android:background="?riotx_header_panel_border_mobile"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/roomBottomBarrier" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -476,6 +476,7 @@
|
|||
<string name="invitations_header">Invites</string>
|
||||
<string name="low_priority_header">Low priority</string>
|
||||
<string name="system_alerts_header">"System Alerts"</string>
|
||||
<string name="suggested_header">Suggested Rooms</string>
|
||||
|
||||
<!-- People fragment -->
|
||||
<string name="direct_chats_header">Conversations</string>
|
||||
|
@ -3291,5 +3292,6 @@
|
|||
<string name="join_anyway">Join Anyway</string>
|
||||
<string name="room_alias_preview_not_found">This alias is not accessible at this time.\nTry again later, or ask a room admin to check if you have access.</string>
|
||||
|
||||
<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>
|
||||
|
||||
</resources>
|
||||
|
|
Loading…
Add table
Reference in a new issue