Merge pull request #3279 from vector-im/feature/bca/space_beta_people
Spaces | beta Browsing Member Directory In Space
This commit is contained in:
commit
096e95f9da
@ -295,6 +295,7 @@
|
|||||||
<activity android:name=".features.spaces.SpaceExploreActivity" />
|
<activity android:name=".features.spaces.SpaceExploreActivity" />
|
||||||
<activity android:name=".features.spaces.SpaceCreationActivity" />
|
<activity android:name=".features.spaces.SpaceCreationActivity" />
|
||||||
<activity android:name=".features.spaces.manage.SpaceManageActivity" />
|
<activity android:name=".features.spaces.manage.SpaceManageActivity" />
|
||||||
|
<activity android:name=".features.spaces.people.SpacePeopleActivity" />
|
||||||
<!-- Services -->
|
<!-- Services -->
|
||||||
|
|
||||||
<service
|
<service
|
||||||
|
@ -125,6 +125,7 @@ import im.vector.app.features.spaces.create.CreateSpaceDefaultRoomsFragment
|
|||||||
import im.vector.app.features.spaces.create.CreateSpaceDetailsFragment
|
import im.vector.app.features.spaces.create.CreateSpaceDetailsFragment
|
||||||
import im.vector.app.features.spaces.explore.SpaceDirectoryFragment
|
import im.vector.app.features.spaces.explore.SpaceDirectoryFragment
|
||||||
import im.vector.app.features.spaces.manage.SpaceAddRoomFragment
|
import im.vector.app.features.spaces.manage.SpaceAddRoomFragment
|
||||||
|
import im.vector.app.features.spaces.people.SpacePeopleFragment
|
||||||
import im.vector.app.features.spaces.preview.SpacePreviewFragment
|
import im.vector.app.features.spaces.preview.SpacePreviewFragment
|
||||||
import im.vector.app.features.terms.ReviewTermsFragment
|
import im.vector.app.features.terms.ReviewTermsFragment
|
||||||
import im.vector.app.features.usercode.ShowUserCodeFragment
|
import im.vector.app.features.usercode.ShowUserCodeFragment
|
||||||
@ -678,4 +679,9 @@ interface FragmentModule {
|
|||||||
@IntoMap
|
@IntoMap
|
||||||
@FragmentKey(SpaceAddRoomFragment::class)
|
@FragmentKey(SpaceAddRoomFragment::class)
|
||||||
fun bindSpaceAddRoomFragment(fragment: SpaceAddRoomFragment): Fragment
|
fun bindSpaceAddRoomFragment(fragment: SpaceAddRoomFragment): Fragment
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoMap
|
||||||
|
@FragmentKey(SpacePeopleFragment::class)
|
||||||
|
fun bindSpacePeopleFragment(fragment: SpacePeopleFragment): Fragment
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,7 @@ import im.vector.app.features.roomprofile.alias.detail.RoomAliasBottomSheetShare
|
|||||||
import im.vector.app.features.roomprofile.settings.historyvisibility.RoomHistoryVisibilitySharedActionViewModel
|
import im.vector.app.features.roomprofile.settings.historyvisibility.RoomHistoryVisibilitySharedActionViewModel
|
||||||
import im.vector.app.features.roomprofile.settings.joinrule.RoomJoinRuleSharedActionViewModel
|
import im.vector.app.features.roomprofile.settings.joinrule.RoomJoinRuleSharedActionViewModel
|
||||||
import im.vector.app.features.spaces.SpacePreviewSharedActionViewModel
|
import im.vector.app.features.spaces.SpacePreviewSharedActionViewModel
|
||||||
|
import im.vector.app.features.spaces.people.SpacePeopleSharedActionViewModel
|
||||||
import im.vector.app.features.userdirectory.UserListSharedActionViewModel
|
import im.vector.app.features.userdirectory.UserListSharedActionViewModel
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
@ -148,4 +149,9 @@ interface ViewModelModule {
|
|||||||
@IntoMap
|
@IntoMap
|
||||||
@ViewModelKey(SpacePreviewSharedActionViewModel::class)
|
@ViewModelKey(SpacePreviewSharedActionViewModel::class)
|
||||||
fun bindSpacePreviewSharedActionViewModel(viewModel: SpacePreviewSharedActionViewModel): ViewModel
|
fun bindSpacePreviewSharedActionViewModel(viewModel: SpacePreviewSharedActionViewModel): ViewModel
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoMap
|
||||||
|
@ViewModelKey(SpacePeopleSharedActionViewModel::class)
|
||||||
|
fun bindSpacePeopleSharedActionViewModel(viewModel: SpacePeopleSharedActionViewModel): ViewModel
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
package im.vector.app.core.epoxy.profiles
|
package im.vector.app.core.epoxy.profiles
|
||||||
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import androidx.annotation.CallSuper
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import com.airbnb.epoxy.EpoxyAttribute
|
import com.airbnb.epoxy.EpoxyAttribute
|
||||||
import im.vector.app.core.epoxy.VectorEpoxyModel
|
import im.vector.app.core.epoxy.VectorEpoxyModel
|
||||||
@ -34,6 +35,7 @@ abstract class BaseProfileMatrixItem<T : ProfileMatrixItem.Holder> : VectorEpoxy
|
|||||||
var userEncryptionTrustLevel: RoomEncryptionTrustLevel? = null
|
var userEncryptionTrustLevel: RoomEncryptionTrustLevel? = null
|
||||||
@EpoxyAttribute var clickListener: View.OnClickListener? = null
|
@EpoxyAttribute var clickListener: View.OnClickListener? = null
|
||||||
|
|
||||||
|
@CallSuper
|
||||||
override fun bind(holder: T) {
|
override fun bind(holder: T) {
|
||||||
super.bind(holder)
|
super.bind(holder)
|
||||||
val bestName = matrixItem.getBestName()
|
val bestName = matrixItem.getBestName()
|
||||||
|
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020 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.core.epoxy.profiles
|
||||||
|
|
||||||
|
import android.widget.TextView
|
||||||
|
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.extensions.setTextOrHide
|
||||||
|
|
||||||
|
@EpoxyModelClass(layout = R.layout.item_profile_matrix_item)
|
||||||
|
abstract class ProfileMatrixItemWithPowerLevel : BaseProfileMatrixItem<ProfileMatrixItemWithPowerLevel.Holder>() {
|
||||||
|
|
||||||
|
@EpoxyAttribute var powerLevelLabel: CharSequence? = null
|
||||||
|
|
||||||
|
override fun bind(holder: Holder) {
|
||||||
|
super.bind(holder)
|
||||||
|
holder.editableView.isVisible = false
|
||||||
|
holder.powerLabel.setTextOrHide(powerLevelLabel)
|
||||||
|
}
|
||||||
|
|
||||||
|
class Holder : ProfileMatrixItem.Holder() {
|
||||||
|
val powerLabel by bind<TextView>(R.id.matrixItemPowerLevelLabel)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2021 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.core.platform
|
||||||
|
|
||||||
|
import android.os.Parcelable
|
||||||
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic argument with one String. Can be an id (ex: roomId, spaceId, callId, etc.), or anything else
|
||||||
|
*/
|
||||||
|
@Parcelize
|
||||||
|
data class GenericIdArgs(
|
||||||
|
val id: String
|
||||||
|
) : Parcelable
|
@ -75,6 +75,7 @@ import im.vector.app.features.spaces.InviteRoomSpaceChooserBottomSheet
|
|||||||
import im.vector.app.features.spaces.SpaceExploreActivity
|
import im.vector.app.features.spaces.SpaceExploreActivity
|
||||||
import im.vector.app.features.spaces.SpacePreviewActivity
|
import im.vector.app.features.spaces.SpacePreviewActivity
|
||||||
import im.vector.app.features.spaces.manage.SpaceManageActivity
|
import im.vector.app.features.spaces.manage.SpaceManageActivity
|
||||||
|
import im.vector.app.features.spaces.people.SpacePeopleActivity
|
||||||
import im.vector.app.features.terms.ReviewTermsActivity
|
import im.vector.app.features.terms.ReviewTermsActivity
|
||||||
import im.vector.app.features.widgets.WidgetActivity
|
import im.vector.app.features.widgets.WidgetActivity
|
||||||
import im.vector.app.features.widgets.WidgetArgsBuilder
|
import im.vector.app.features.widgets.WidgetArgsBuilder
|
||||||
@ -283,7 +284,19 @@ class DefaultNavigator @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun openCreateDirectRoom(context: Context) {
|
override fun openCreateDirectRoom(context: Context) {
|
||||||
val intent = CreateDirectRoomActivity.getIntent(context)
|
val intent = when (val currentGroupingMethod = appStateHandler.getCurrentRoomGroupingMethod()) {
|
||||||
|
is RoomGroupingMethod.ByLegacyGroup -> {
|
||||||
|
CreateDirectRoomActivity.getIntent(context)
|
||||||
|
}
|
||||||
|
is RoomGroupingMethod.BySpace -> {
|
||||||
|
if (currentGroupingMethod.spaceSummary != null) {
|
||||||
|
SpacePeopleActivity.newIntent(context, currentGroupingMethod.spaceSummary.roomId)
|
||||||
|
} else {
|
||||||
|
CreateDirectRoomActivity.getIntent(context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> null
|
||||||
|
} ?: return
|
||||||
context.startActivity(intent)
|
context.startActivity(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,11 +47,16 @@ class RoomMemberListFragment @Inject constructor(
|
|||||||
private val roomMemberListController: RoomMemberListController,
|
private val roomMemberListController: RoomMemberListController,
|
||||||
private val avatarRenderer: AvatarRenderer
|
private val avatarRenderer: AvatarRenderer
|
||||||
) : VectorBaseFragment<FragmentRoomMemberListBinding>(),
|
) : VectorBaseFragment<FragmentRoomMemberListBinding>(),
|
||||||
RoomMemberListController.Callback {
|
RoomMemberListController.Callback,
|
||||||
|
RoomMemberListViewModel.Factory {
|
||||||
|
|
||||||
private val viewModel: RoomMemberListViewModel by fragmentViewModel()
|
private val viewModel: RoomMemberListViewModel by fragmentViewModel()
|
||||||
private val roomProfileArgs: RoomProfileArgs by args()
|
private val roomProfileArgs: RoomProfileArgs by args()
|
||||||
|
|
||||||
|
override fun create(initialState: RoomMemberListViewState): RoomMemberListViewModel {
|
||||||
|
return viewModelFactory.create(initialState)
|
||||||
|
}
|
||||||
|
|
||||||
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentRoomMemberListBinding {
|
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentRoomMemberListBinding {
|
||||||
return FragmentRoomMemberListBinding.inflate(inflater, container, false)
|
return FragmentRoomMemberListBinding.inflate(inflater, container, false)
|
||||||
}
|
}
|
||||||
|
@ -17,12 +17,13 @@
|
|||||||
package im.vector.app.features.roomprofile.members
|
package im.vector.app.features.roomprofile.members
|
||||||
|
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.airbnb.mvrx.ActivityViewModelContext
|
||||||
import com.airbnb.mvrx.FragmentViewModelContext
|
import com.airbnb.mvrx.FragmentViewModelContext
|
||||||
import com.airbnb.mvrx.MvRxViewModelFactory
|
import com.airbnb.mvrx.MvRxViewModelFactory
|
||||||
import com.airbnb.mvrx.ViewModelContext
|
import com.airbnb.mvrx.ViewModelContext
|
||||||
import dagger.assisted.Assisted
|
import dagger.assisted.Assisted
|
||||||
import dagger.assisted.AssistedInject
|
|
||||||
import dagger.assisted.AssistedFactory
|
import dagger.assisted.AssistedFactory
|
||||||
|
import dagger.assisted.AssistedInject
|
||||||
import im.vector.app.core.extensions.exhaustive
|
import im.vector.app.core.extensions.exhaustive
|
||||||
import im.vector.app.core.platform.EmptyViewEvents
|
import im.vector.app.core.platform.EmptyViewEvents
|
||||||
import im.vector.app.core.platform.VectorViewModel
|
import im.vector.app.core.platform.VectorViewModel
|
||||||
@ -62,8 +63,11 @@ class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState
|
|||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
override fun create(viewModelContext: ViewModelContext, state: RoomMemberListViewState): RoomMemberListViewModel? {
|
override fun create(viewModelContext: ViewModelContext, state: RoomMemberListViewState): RoomMemberListViewModel? {
|
||||||
val fragment: RoomMemberListFragment = (viewModelContext as FragmentViewModelContext).fragment()
|
val factory = when (viewModelContext) {
|
||||||
return fragment.viewModelFactory.create(state)
|
is FragmentViewModelContext -> viewModelContext.fragment as? Factory
|
||||||
|
is ActivityViewModelContext -> viewModelContext.activity as? Factory
|
||||||
|
}
|
||||||
|
return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,7 +192,7 @@ class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState
|
|||||||
override fun handle(action: RoomMemberListAction) {
|
override fun handle(action: RoomMemberListAction) {
|
||||||
when (action) {
|
when (action) {
|
||||||
is RoomMemberListAction.RevokeThreePidInvite -> handleRevokeThreePidInvite(action)
|
is RoomMemberListAction.RevokeThreePidInvite -> handleRevokeThreePidInvite(action)
|
||||||
is RoomMemberListAction.FilterMemberList -> handleFilterMemberList(action)
|
is RoomMemberListAction.FilterMemberList -> handleFilterMemberList(action)
|
||||||
}.exhaustive
|
}.exhaustive
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,6 +21,7 @@ import com.airbnb.mvrx.Async
|
|||||||
import com.airbnb.mvrx.MvRxState
|
import com.airbnb.mvrx.MvRxState
|
||||||
import com.airbnb.mvrx.Uninitialized
|
import com.airbnb.mvrx.Uninitialized
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
|
import im.vector.app.core.platform.GenericIdArgs
|
||||||
import im.vector.app.features.roomprofile.RoomProfileArgs
|
import im.vector.app.features.roomprofile.RoomProfileArgs
|
||||||
import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel
|
import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel
|
||||||
import org.matrix.android.sdk.api.session.events.model.Event
|
import org.matrix.android.sdk.api.session.events.model.Event
|
||||||
@ -38,6 +39,8 @@ data class RoomMemberListViewState(
|
|||||||
) : MvRxState {
|
) : MvRxState {
|
||||||
|
|
||||||
constructor(args: RoomProfileArgs) : this(roomId = args.roomId)
|
constructor(args: RoomProfileArgs) : this(roomId = args.roomId)
|
||||||
|
|
||||||
|
constructor(args: GenericIdArgs) : this(roomId = args.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
data class ActionPermissions(
|
data class ActionPermissions(
|
||||||
|
@ -29,9 +29,11 @@ class RoomMemberSummaryFilter @Inject constructor() : Predicate<RoomMemberSummar
|
|||||||
// No filter
|
// No filter
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
// if filter is "Jo Do", it should match "John Doe"
|
||||||
return roomMemberSummary.displayName?.contains(filter, ignoreCase = true).orFalse()
|
return filter.split(" ").all {
|
||||||
// We should maybe exclude the domain from the userId
|
roomMemberSummary.displayName?.contains(it, ignoreCase = true).orFalse()
|
||||||
|| roomMemberSummary.userId.contains(filter, ignoreCase = true)
|
// We should maybe exclude the domain from the userId
|
||||||
|
|| roomMemberSummary.userId.contains(it, ignoreCase = true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2021 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.spaces.people
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.core.view.isGone
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import com.airbnb.mvrx.MvRx
|
||||||
|
import im.vector.app.R
|
||||||
|
import im.vector.app.core.extensions.commitTransaction
|
||||||
|
import im.vector.app.core.extensions.hideKeyboard
|
||||||
|
import im.vector.app.core.platform.GenericIdArgs
|
||||||
|
import im.vector.app.core.platform.VectorBaseActivity
|
||||||
|
import im.vector.app.databinding.ActivitySimpleLoadingBinding
|
||||||
|
import im.vector.app.features.spaces.ShareSpaceBottomSheet
|
||||||
|
|
||||||
|
class SpacePeopleActivity : VectorBaseActivity<ActivitySimpleLoadingBinding>() {
|
||||||
|
|
||||||
|
override fun getBinding() = ActivitySimpleLoadingBinding.inflate(layoutInflater)
|
||||||
|
|
||||||
|
private lateinit var sharedActionViewModel: SpacePeopleSharedActionViewModel
|
||||||
|
|
||||||
|
override fun initUiAndData() {
|
||||||
|
super.initUiAndData()
|
||||||
|
waitingView = views.waitingView.waitingView
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showWaitingView(text: String?) {
|
||||||
|
hideKeyboard()
|
||||||
|
views.waitingView.waitingStatusText.isGone = views.waitingView.waitingStatusText.text.isNullOrBlank()
|
||||||
|
super.showWaitingView(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hideWaitingView() {
|
||||||
|
views.waitingView.waitingStatusText.text = null
|
||||||
|
views.waitingView.waitingStatusText.isGone = true
|
||||||
|
views.waitingView.waitingHorizontalProgress.progress = 0
|
||||||
|
views.waitingView.waitingHorizontalProgress.isVisible = false
|
||||||
|
super.hideWaitingView()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
val args = intent?.getParcelableExtra<GenericIdArgs>(MvRx.KEY_ARG)
|
||||||
|
if (isFirstCreation()) {
|
||||||
|
val simpleName = SpacePeopleFragment::class.java.simpleName
|
||||||
|
if (supportFragmentManager.findFragmentByTag(simpleName) == null) {
|
||||||
|
supportFragmentManager.commitTransaction {
|
||||||
|
replace(R.id.simpleFragmentContainer,
|
||||||
|
SpacePeopleFragment::class.java,
|
||||||
|
Bundle().apply { this.putParcelable(MvRx.KEY_ARG, args) },
|
||||||
|
simpleName
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sharedActionViewModel = viewModelProvider.get(SpacePeopleSharedActionViewModel::class.java)
|
||||||
|
sharedActionViewModel
|
||||||
|
.observe()
|
||||||
|
.subscribe { sharedAction ->
|
||||||
|
when (sharedAction) {
|
||||||
|
SpacePeopleSharedAction.Dismiss -> finish()
|
||||||
|
is SpacePeopleSharedAction.NavigateToRoom -> navigateToRooms(sharedAction)
|
||||||
|
SpacePeopleSharedAction.HideModalLoading -> hideWaitingView()
|
||||||
|
SpacePeopleSharedAction.ShowModalLoading -> {
|
||||||
|
showWaitingView()
|
||||||
|
}
|
||||||
|
is SpacePeopleSharedAction.NavigateToInvite -> {
|
||||||
|
ShareSpaceBottomSheet.show(supportFragmentManager, sharedAction.spaceId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.disposeOnDestroy()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun navigateToRooms(action: SpacePeopleSharedAction.NavigateToRoom) {
|
||||||
|
navigator.openRoom(this, action.roomId)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun newIntent(context: Context, spaceId: String): Intent {
|
||||||
|
return Intent(context, SpacePeopleActivity::class.java).apply {
|
||||||
|
putExtra(MvRx.KEY_ARG, GenericIdArgs(spaceId))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,158 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2021 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.spaces.people
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import com.airbnb.mvrx.Fail
|
||||||
|
import com.airbnb.mvrx.Loading
|
||||||
|
import com.airbnb.mvrx.Success
|
||||||
|
import com.airbnb.mvrx.Uninitialized
|
||||||
|
import com.airbnb.mvrx.fragmentViewModel
|
||||||
|
import com.airbnb.mvrx.withState
|
||||||
|
import com.jakewharton.rxbinding3.appcompat.queryTextChanges
|
||||||
|
import im.vector.app.R
|
||||||
|
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.core.resources.ColorProvider
|
||||||
|
import im.vector.app.core.resources.DrawableProvider
|
||||||
|
import im.vector.app.databinding.FragmentRecyclerviewWithSearchBinding
|
||||||
|
import im.vector.app.features.roomprofile.members.RoomMemberListAction
|
||||||
|
import im.vector.app.features.roomprofile.members.RoomMemberListViewModel
|
||||||
|
import im.vector.app.features.roomprofile.members.RoomMemberListViewState
|
||||||
|
import io.reactivex.rxkotlin.subscribeBy
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class SpacePeopleFragment @Inject constructor(
|
||||||
|
private val viewModelFactory: SpacePeopleViewModel.Factory,
|
||||||
|
private val roomMemberModelFactory: RoomMemberListViewModel.Factory,
|
||||||
|
private val drawableProvider: DrawableProvider,
|
||||||
|
private val colorProvider: ColorProvider,
|
||||||
|
private val epoxyController: SpacePeopleListController
|
||||||
|
) : VectorBaseFragment<FragmentRecyclerviewWithSearchBinding>(),
|
||||||
|
SpacePeopleViewModel.Factory,
|
||||||
|
RoomMemberListViewModel.Factory,
|
||||||
|
OnBackPressed, SpacePeopleListController.InteractionListener {
|
||||||
|
|
||||||
|
private val viewModel by fragmentViewModel(SpacePeopleViewModel::class)
|
||||||
|
private val membersViewModel by fragmentViewModel(RoomMemberListViewModel::class)
|
||||||
|
private lateinit var sharedActionViewModel: SpacePeopleSharedActionViewModel
|
||||||
|
|
||||||
|
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?) =
|
||||||
|
FragmentRecyclerviewWithSearchBinding.inflate(inflater, container, false)
|
||||||
|
|
||||||
|
override fun onBackPressed(toolbarButton: Boolean): Boolean {
|
||||||
|
sharedActionViewModel.post(SpacePeopleSharedAction.Dismiss)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun create(initialState: SpacePeopleViewState): SpacePeopleViewModel {
|
||||||
|
return viewModelFactory.create(initialState)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun create(initialState: RoomMemberListViewState): RoomMemberListViewModel {
|
||||||
|
return roomMemberModelFactory.create(initialState)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun invalidate() = withState(membersViewModel) { memberListState ->
|
||||||
|
views.appBarTitle.text = getString(R.string.bottom_action_people)
|
||||||
|
val memberCount = (memberListState.roomSummary.invoke()?.otherMemberIds?.size ?: 0) + 1
|
||||||
|
views.appBarSpaceInfo.text = resources.getQuantityString(R.plurals.room_title_members, memberCount, memberCount)
|
||||||
|
// views.listBuildingProgress.isVisible = true
|
||||||
|
epoxyController.setData(memberListState)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
sharedActionViewModel = activityViewModelProvider.get(SpacePeopleSharedActionViewModel::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
setupRecyclerView()
|
||||||
|
setupSearchView()
|
||||||
|
|
||||||
|
views.addRoomToSpaceToolbar.navigationIcon = drawableProvider.getDrawable(
|
||||||
|
R.drawable.ic_close_24dp,
|
||||||
|
colorProvider.getColorFromAttribute(R.attr.riot_primary_text_color)
|
||||||
|
)
|
||||||
|
views.addRoomToSpaceToolbar.setNavigationOnClickListener {
|
||||||
|
sharedActionViewModel.post(SpacePeopleSharedAction.Dismiss)
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModel.observeViewEvents {
|
||||||
|
handleViewEvents(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModel.subscribe(this) {
|
||||||
|
when (it.createAndInviteState) {
|
||||||
|
is Loading -> sharedActionViewModel.post(SpacePeopleSharedAction.ShowModalLoading)
|
||||||
|
Uninitialized,
|
||||||
|
is Fail -> sharedActionViewModel.post(SpacePeopleSharedAction.HideModalLoading)
|
||||||
|
is Success -> {
|
||||||
|
// don't hide on success, it will navigate out. If not the loading goes out before navigation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
epoxyController.listener = null
|
||||||
|
views.roomList.cleanup()
|
||||||
|
super.onDestroyView()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupRecyclerView() {
|
||||||
|
views.roomList.configureWith(epoxyController, hasFixedSize = false, disableItemAnimation = false)
|
||||||
|
epoxyController.listener = this
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupSearchView() {
|
||||||
|
views.memberNameFilter.queryHint = getString(R.string.search_members_hint)
|
||||||
|
views.memberNameFilter.queryTextChanges()
|
||||||
|
.debounce(100, TimeUnit.MILLISECONDS)
|
||||||
|
.subscribeBy {
|
||||||
|
membersViewModel.handle(RoomMemberListAction.FilterMemberList(it.toString()))
|
||||||
|
}
|
||||||
|
.disposeOnDestroyView()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleViewEvents(events: SpacePeopleViewEvents) {
|
||||||
|
when (events) {
|
||||||
|
is SpacePeopleViewEvents.OpenRoom -> {
|
||||||
|
sharedActionViewModel.post(SpacePeopleSharedAction.NavigateToRoom(events.roomId))
|
||||||
|
}
|
||||||
|
is SpacePeopleViewEvents.InviteToSpace -> {
|
||||||
|
sharedActionViewModel.post(SpacePeopleSharedAction.NavigateToInvite(events.spaceId))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSpaceMemberClicked(roomMemberSummary: RoomMemberSummary) {
|
||||||
|
viewModel.handle(SpacePeopleViewAction.ChatWith(roomMemberSummary))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onInviteToSpaceSelected() {
|
||||||
|
viewModel.handle(SpacePeopleViewAction.InviteToSpace)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,167 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2021 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.spaces.people
|
||||||
|
|
||||||
|
import com.airbnb.epoxy.TypedEpoxyController
|
||||||
|
import im.vector.app.R
|
||||||
|
import im.vector.app.core.epoxy.dividerItem
|
||||||
|
import im.vector.app.core.epoxy.loadingItem
|
||||||
|
import im.vector.app.core.epoxy.profiles.profileMatrixItemWithPowerLevel
|
||||||
|
import im.vector.app.core.extensions.join
|
||||||
|
import im.vector.app.core.resources.ColorProvider
|
||||||
|
import im.vector.app.core.resources.StringProvider
|
||||||
|
import im.vector.app.core.ui.list.GenericItem
|
||||||
|
import im.vector.app.core.ui.list.genericItem
|
||||||
|
import im.vector.app.core.utils.DimensionConverter
|
||||||
|
import im.vector.app.features.home.AvatarRenderer
|
||||||
|
import im.vector.app.features.roomprofile.members.RoomMemberListCategories
|
||||||
|
import im.vector.app.features.roomprofile.members.RoomMemberListViewState
|
||||||
|
import im.vector.app.features.roomprofile.members.RoomMemberSummaryFilter
|
||||||
|
import me.gujun.android.span.span
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
|
||||||
|
import org.matrix.android.sdk.api.util.toMatrixItem
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class SpacePeopleListController @Inject constructor(
|
||||||
|
private val avatarRenderer: AvatarRenderer,
|
||||||
|
private val colorProvider: ColorProvider,
|
||||||
|
private val stringProvider: StringProvider,
|
||||||
|
private val dimensionConverter: DimensionConverter,
|
||||||
|
private val roomMemberSummaryFilter: RoomMemberSummaryFilter
|
||||||
|
) : TypedEpoxyController<RoomMemberListViewState>() {
|
||||||
|
|
||||||
|
private val dividerColor = colorProvider.getColorFromAttribute(R.attr.vctr_list_divider_color)
|
||||||
|
|
||||||
|
interface InteractionListener {
|
||||||
|
fun onSpaceMemberClicked(roomMemberSummary: RoomMemberSummary)
|
||||||
|
fun onInviteToSpaceSelected()
|
||||||
|
}
|
||||||
|
|
||||||
|
var listener: InteractionListener? = null
|
||||||
|
|
||||||
|
init {
|
||||||
|
setData(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun buildModels(data: RoomMemberListViewState?) {
|
||||||
|
val memberSummaries = data?.roomMemberSummaries?.invoke()
|
||||||
|
if (memberSummaries == null) {
|
||||||
|
loadingItem { id("loading") }
|
||||||
|
return
|
||||||
|
}
|
||||||
|
roomMemberSummaryFilter.filter = data.filter
|
||||||
|
var foundCount = 0
|
||||||
|
memberSummaries.forEach { memberEntry ->
|
||||||
|
|
||||||
|
val filtered = memberEntry.second
|
||||||
|
.filter { roomMemberSummaryFilter.test(it) }
|
||||||
|
if (filtered.isNotEmpty()) {
|
||||||
|
dividerItem {
|
||||||
|
id("divider_type_${memberEntry.first.titleRes}")
|
||||||
|
color(dividerColor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foundCount += filtered.size
|
||||||
|
filtered
|
||||||
|
.join(
|
||||||
|
each = { _, roomMember ->
|
||||||
|
profileMatrixItemWithPowerLevel {
|
||||||
|
id(roomMember.userId)
|
||||||
|
matrixItem(roomMember.toMatrixItem())
|
||||||
|
avatarRenderer(avatarRenderer)
|
||||||
|
userEncryptionTrustLevel(data.trustLevelMap.invoke()?.get(roomMember.userId))
|
||||||
|
.apply {
|
||||||
|
val pl = memberEntry.first.toPowerLevelLabel()
|
||||||
|
if (memberEntry.first == RoomMemberListCategories.INVITE) {
|
||||||
|
powerLevelLabel(
|
||||||
|
span {
|
||||||
|
span(stringProvider.getString(R.string.invited)) {
|
||||||
|
textColor = colorProvider.getColorFromAttribute(R.attr.riotx_text_secondary)
|
||||||
|
textStyle = "bold"
|
||||||
|
// fontFamily = "monospace"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} else if (pl != null) {
|
||||||
|
powerLevelLabel(
|
||||||
|
span {
|
||||||
|
span(" $pl ") {
|
||||||
|
backgroundColor = colorProvider.getColor(R.color.notification_accent_color)
|
||||||
|
paddingTop = dimensionConverter.dpToPx(2)
|
||||||
|
paddingBottom = dimensionConverter.dpToPx(2)
|
||||||
|
textColor = colorProvider.getColor(R.color.white)
|
||||||
|
textStyle = "bold"
|
||||||
|
// fontFamily = "monospace"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
powerLevelLabel(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clickListener { _ ->
|
||||||
|
listener?.onSpaceMemberClicked(roomMember)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
between = { _, roomMemberBefore ->
|
||||||
|
dividerItem {
|
||||||
|
id("divider_${roomMemberBefore.userId}")
|
||||||
|
color(dividerColor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foundCount == 0 && data.filter.isNotEmpty()) {
|
||||||
|
// add the footer thing
|
||||||
|
genericItem {
|
||||||
|
id("not_found")
|
||||||
|
title(
|
||||||
|
span {
|
||||||
|
+"\n"
|
||||||
|
+stringProvider.getString(R.string.no_result_placeholder)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
description(
|
||||||
|
span {
|
||||||
|
+stringProvider.getString(R.string.looking_for_someone_not_in_space, data.roomSummary.invoke()?.displayName ?: "")
|
||||||
|
+"\n"
|
||||||
|
span("Invite them") {
|
||||||
|
textColor = colorProvider.getColorFromAttribute(R.attr.colorAccent)
|
||||||
|
textStyle = "bold"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
itemClickAction(GenericItem.Action("invite").apply {
|
||||||
|
perform = Runnable {
|
||||||
|
listener?.onInviteToSpaceSelected()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun RoomMemberListCategories.toPowerLevelLabel(): String? {
|
||||||
|
return when (this) {
|
||||||
|
RoomMemberListCategories.ADMIN -> stringProvider.getString(R.string.power_level_admin)
|
||||||
|
RoomMemberListCategories.MODERATOR -> stringProvider.getString(R.string.power_level_moderator)
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2021 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.spaces.people
|
||||||
|
|
||||||
|
import im.vector.app.core.platform.VectorSharedAction
|
||||||
|
|
||||||
|
sealed class SpacePeopleSharedAction : VectorSharedAction {
|
||||||
|
object Dismiss : SpacePeopleSharedAction()
|
||||||
|
object ShowModalLoading : SpacePeopleSharedAction()
|
||||||
|
object HideModalLoading : SpacePeopleSharedAction()
|
||||||
|
data class NavigateToRoom(val roomId: String) : SpacePeopleSharedAction()
|
||||||
|
data class NavigateToInvite(val spaceId: String) : SpacePeopleSharedAction()
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2021 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.spaces.people
|
||||||
|
|
||||||
|
import im.vector.app.core.platform.VectorSharedActionViewModel
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class SpacePeopleSharedActionViewModel @Inject constructor() : VectorSharedActionViewModel<SpacePeopleSharedAction>()
|
@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2021 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.spaces.people
|
||||||
|
|
||||||
|
import im.vector.app.core.platform.VectorViewModelAction
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
|
||||||
|
|
||||||
|
sealed class SpacePeopleViewAction : VectorViewModelAction {
|
||||||
|
data class ChatWith(val member: RoomMemberSummary) : SpacePeopleViewAction()
|
||||||
|
object InviteToSpace : SpacePeopleViewAction()
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2021 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.spaces.people
|
||||||
|
|
||||||
|
import im.vector.app.core.platform.VectorViewEvents
|
||||||
|
|
||||||
|
sealed class SpacePeopleViewEvents : VectorViewEvents {
|
||||||
|
data class OpenRoom(val roomId: String) : SpacePeopleViewEvents()
|
||||||
|
data class InviteToSpace(val spaceId: String) : SpacePeopleViewEvents()
|
||||||
|
}
|
@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2021 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.spaces.people
|
||||||
|
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.airbnb.mvrx.ActivityViewModelContext
|
||||||
|
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 dagger.assisted.Assisted
|
||||||
|
import dagger.assisted.AssistedFactory
|
||||||
|
import dagger.assisted.AssistedInject
|
||||||
|
import im.vector.app.core.extensions.exhaustive
|
||||||
|
import im.vector.app.core.platform.VectorViewModel
|
||||||
|
import im.vector.app.features.raw.wellknown.getElementWellknown
|
||||||
|
import im.vector.app.features.raw.wellknown.isE2EByDefault
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.matrix.android.sdk.api.raw.RawService
|
||||||
|
import org.matrix.android.sdk.api.session.Session
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
|
||||||
|
|
||||||
|
class SpacePeopleViewModel @AssistedInject constructor(
|
||||||
|
@Assisted val initialState: SpacePeopleViewState,
|
||||||
|
private val rawService: RawService,
|
||||||
|
private val session: Session
|
||||||
|
) : VectorViewModel<SpacePeopleViewState, SpacePeopleViewAction, SpacePeopleViewEvents>(initialState) {
|
||||||
|
|
||||||
|
@AssistedFactory
|
||||||
|
interface Factory {
|
||||||
|
fun create(initialState: SpacePeopleViewState): SpacePeopleViewModel
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object : MvRxViewModelFactory<SpacePeopleViewModel, SpacePeopleViewState> {
|
||||||
|
override fun create(viewModelContext: ViewModelContext, state: SpacePeopleViewState): SpacePeopleViewModel? {
|
||||||
|
val factory = when (viewModelContext) {
|
||||||
|
is FragmentViewModelContext -> viewModelContext.fragment as? Factory
|
||||||
|
is ActivityViewModelContext -> viewModelContext.activity as? Factory
|
||||||
|
}
|
||||||
|
return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun handle(action: SpacePeopleViewAction) {
|
||||||
|
when (action) {
|
||||||
|
is SpacePeopleViewAction.ChatWith -> handleChatWith(action)
|
||||||
|
SpacePeopleViewAction.InviteToSpace -> handleInviteToSpace()
|
||||||
|
}.exhaustive
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleInviteToSpace() {
|
||||||
|
_viewEvents.post(SpacePeopleViewEvents.InviteToSpace(initialState.spaceId))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleChatWith(action: SpacePeopleViewAction.ChatWith) {
|
||||||
|
val otherUserId = action.member.userId
|
||||||
|
if (otherUserId == session.myUserId) return
|
||||||
|
val existingRoomId = session.getExistingDirectRoomWithUser(otherUserId)
|
||||||
|
if (existingRoomId != null) {
|
||||||
|
// just open it
|
||||||
|
_viewEvents.post(SpacePeopleViewEvents.OpenRoom(existingRoomId))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setState { copy(createAndInviteState = Loading()) }
|
||||||
|
|
||||||
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
val adminE2EByDefault = rawService.getElementWellknown(session.myUserId)
|
||||||
|
?.isE2EByDefault()
|
||||||
|
?: true
|
||||||
|
|
||||||
|
val roomParams = CreateRoomParams()
|
||||||
|
.apply {
|
||||||
|
invitedUserIds.add(otherUserId)
|
||||||
|
setDirectMessage()
|
||||||
|
enableEncryptionIfInvitedUsersSupportIt = adminE2EByDefault
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
val roomId = session.createRoom(roomParams)
|
||||||
|
_viewEvents.post(SpacePeopleViewEvents.OpenRoom(roomId))
|
||||||
|
setState { copy(createAndInviteState = Success(roomId)) }
|
||||||
|
} catch (failure: Throwable) {
|
||||||
|
setState { copy(createAndInviteState = Fail(failure)) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2021 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.spaces.people
|
||||||
|
|
||||||
|
import com.airbnb.mvrx.Async
|
||||||
|
import com.airbnb.mvrx.MvRxState
|
||||||
|
import com.airbnb.mvrx.Uninitialized
|
||||||
|
import im.vector.app.core.platform.GenericIdArgs
|
||||||
|
|
||||||
|
data class SpacePeopleViewState(
|
||||||
|
val spaceId: String,
|
||||||
|
val createAndInviteState: Async<String> = Uninitialized
|
||||||
|
) : MvRxState {
|
||||||
|
constructor(args: GenericIdArgs) : this(
|
||||||
|
spaceId = args.id
|
||||||
|
)
|
||||||
|
}
|
17
vector/src/main/res/layout/activity_simple_loading.xml
Normal file
17
vector/src/main/res/layout/activity_simple_loading.xml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/coordinatorLayout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<androidx.fragment.app.FragmentContainerView
|
||||||
|
android:id="@+id/simpleFragmentContainer"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent" />
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/waiting_view"
|
||||||
|
layout="@layout/merge_overlay_waiting_view" />
|
||||||
|
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
@ -0,0 +1,96 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.coordinatorlayout.widget.CoordinatorLayout 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/coordinatorLayout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="?riotx_background">
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/roomList"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="?riotx_background"
|
||||||
|
android:overScrollMode="always"
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||||
|
tools:listitem="@layout/item_profile_matrix_item" />
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
|
style="@style/VectorAppBarLayoutStyle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:elevation="4dp">
|
||||||
|
|
||||||
|
<!-- minHeight="0dp" is important to collapse on scroll -->
|
||||||
|
<androidx.appcompat.widget.Toolbar
|
||||||
|
android:id="@+id/addRoomToSpaceToolbar"
|
||||||
|
style="@style/VectorToolbarStyle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
android:elevation="4dp"
|
||||||
|
android:minHeight="0dp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap|enterAlways">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical|start"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/appBarTitle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:gravity="start|center"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:textColor="?riotx_text_primary"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
tools:text="@tools:sample/lorem/random" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/appBarSpaceInfo"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:gravity="start|center"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:textColor="?riotx_text_secondary"
|
||||||
|
android:textSize="16sp"
|
||||||
|
tools:text="@tools:sample/lorem/random" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</androidx.appcompat.widget.Toolbar>
|
||||||
|
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.SearchView
|
||||||
|
android:id="@+id/memberNameFilter"
|
||||||
|
style="@style/VectorSearchView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/addRoomToSpaceToolbar"
|
||||||
|
app:queryHint="@string/search_hint_room_name" />
|
||||||
|
|
||||||
|
<!-- <ProgressBar-->
|
||||||
|
<!-- android:id="@+id/listBuildingProgress"-->
|
||||||
|
<!-- android:indeterminate="true"-->
|
||||||
|
<!-- android:layout_width="match_parent"-->
|
||||||
|
<!-- android:layout_height="8dp"-->
|
||||||
|
<!-- style="@style/Widget.AppCompat.ProgressBar.Horizontal"-->
|
||||||
|
<!-- />-->
|
||||||
|
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
@ -37,44 +37,53 @@
|
|||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/matrixItemTitle"
|
android:id="@+id/matrixItemTitle"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="16dp"
|
android:layout_marginStart="16dp"
|
||||||
android:layout_marginEnd="16dp"
|
android:layout_marginEnd="8dp"
|
||||||
android:drawablePadding="16dp"
|
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:textColor="?riotx_text_primary"
|
android:textColor="?riotx_text_primary"
|
||||||
android:textSize="16sp"
|
android:textSize="16sp"
|
||||||
app:layout_constrainedWidth="true"
|
app:layout_constrainedWidth="true"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/matrixItemSubtitle"
|
app:layout_constraintBottom_toTopOf="@+id/matrixItemSubtitle"
|
||||||
app:layout_constraintEnd_toStartOf="@+id/matrixItemEditable"
|
app:layout_constraintEnd_toStartOf="@+id/matrixItemPowerLevelLabel"
|
||||||
app:layout_constraintHorizontal_bias="0.0"
|
|
||||||
app:layout_constraintStart_toEndOf="@id/matrixItemAvatar"
|
app:layout_constraintStart_toEndOf="@id/matrixItemAvatar"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:layout_goneMarginStart="0dp"
|
app:layout_goneMarginEnd="80dp"
|
||||||
tools:text="@sample/matrix.json/data/displayName" />
|
tools:text="@sample/matrix.json/data/displayName" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/matrixItemSubtitle"
|
android:id="@+id/matrixItemSubtitle"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="16dp"
|
android:layout_marginStart="16dp"
|
||||||
android:layout_marginEnd="16dp"
|
android:layout_marginEnd="8dp"
|
||||||
android:drawablePadding="16dp"
|
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:textColor="?riotx_text_secondary"
|
android:textColor="?riotx_text_secondary"
|
||||||
android:textSize="12sp"
|
android:textSize="12sp"
|
||||||
app:layout_constrainedWidth="true"
|
app:layout_constrainedWidth="true"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toStartOf="@+id/matrixItemEditable"
|
app:layout_constraintEnd_toStartOf="@+id/matrixItemPowerLevelLabel"
|
||||||
app:layout_constraintHorizontal_bias="0.0"
|
|
||||||
app:layout_constraintStart_toEndOf="@id/matrixItemAvatar"
|
app:layout_constraintStart_toEndOf="@id/matrixItemAvatar"
|
||||||
app:layout_constraintTop_toBottomOf="@id/matrixItemTitle"
|
app:layout_constraintTop_toBottomOf="@id/matrixItemTitle"
|
||||||
app:layout_goneMarginStart="0dp"
|
app:layout_goneMarginEnd="8dp"
|
||||||
tools:text="@sample/matrix.json/data/mxid" />
|
tools:text="@sample/matrix.json/data/mxid" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/matrixItemPowerLevelLabel"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/matrixItemEditable"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:text="Admin"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/matrixItemEditable"
|
android:id="@+id/matrixItemEditable"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
@ -87,5 +96,4 @@
|
|||||||
app:tint="?riotx_text_secondary"
|
app:tint="?riotx_text_secondary"
|
||||||
tools:ignore="MissingPrefix" />
|
tools:ignore="MissingPrefix" />
|
||||||
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
@ -37,11 +37,10 @@
|
|||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/matrixItemTitle"
|
android:id="@+id/matrixItemTitle"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="16dp"
|
android:layout_marginStart="16dp"
|
||||||
android:layout_marginEnd="16dp"
|
android:layout_marginEnd="8dp"
|
||||||
android:drawablePadding="16dp"
|
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:textColor="?riotx_text_primary"
|
android:textColor="?riotx_text_primary"
|
||||||
@ -49,19 +48,17 @@
|
|||||||
app:layout_constrainedWidth="true"
|
app:layout_constrainedWidth="true"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/matrixItemSubtitle"
|
app:layout_constraintBottom_toTopOf="@+id/matrixItemSubtitle"
|
||||||
app:layout_constraintEnd_toStartOf="@+id/matrixItemProgress"
|
app:layout_constraintEnd_toStartOf="@+id/matrixItemProgress"
|
||||||
app:layout_constraintHorizontal_bias="0.0"
|
|
||||||
app:layout_constraintStart_toEndOf="@id/matrixItemAvatar"
|
app:layout_constraintStart_toEndOf="@id/matrixItemAvatar"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:layout_goneMarginStart="0dp"
|
app:layout_goneMarginEnd="80dp"
|
||||||
tools:text="@sample/matrix.json/data/displayName" />
|
tools:text="@sample/matrix.json/data/displayName" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/matrixItemSubtitle"
|
android:id="@+id/matrixItemSubtitle"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="16dp"
|
android:layout_marginStart="16dp"
|
||||||
android:layout_marginEnd="16dp"
|
android:layout_marginEnd="8dp"
|
||||||
android:drawablePadding="16dp"
|
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:textColor="?riotx_text_secondary"
|
android:textColor="?riotx_text_secondary"
|
||||||
@ -69,10 +66,9 @@
|
|||||||
app:layout_constrainedWidth="true"
|
app:layout_constrainedWidth="true"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toStartOf="@+id/matrixItemProgress"
|
app:layout_constraintEnd_toStartOf="@+id/matrixItemProgress"
|
||||||
app:layout_constraintHorizontal_bias="0.0"
|
|
||||||
app:layout_constraintStart_toEndOf="@id/matrixItemAvatar"
|
app:layout_constraintStart_toEndOf="@id/matrixItemAvatar"
|
||||||
app:layout_constraintTop_toBottomOf="@id/matrixItemTitle"
|
app:layout_constraintTop_toBottomOf="@id/matrixItemTitle"
|
||||||
app:layout_goneMarginStart="0dp"
|
app:layout_goneMarginEnd="8dp"
|
||||||
tools:text="@sample/matrix.json/data/mxid" />
|
tools:text="@sample/matrix.json/data/mxid" />
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
@ -93,12 +89,10 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:importantForAccessibility="no"
|
android:importantForAccessibility="no"
|
||||||
android:src="@drawable/ic_arrow_right"
|
android:src="@drawable/ic_arrow_right"
|
||||||
android:visibility="visible"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:tint="?riotx_text_secondary"
|
app:tint="?riotx_text_secondary"
|
||||||
tools:ignore="MissingPrefix"
|
tools:ignore="MissingPrefix" />
|
||||||
tools:visibility="visible" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
@ -3353,4 +3353,6 @@
|
|||||||
<string name="labs_use_restricted_join_rule">Experimental Space - Restricted Room.</string>
|
<string name="labs_use_restricted_join_rule">Experimental Space - Restricted Room.</string>
|
||||||
<string name="labs_use_restricted_join_rule_desc">Warning requires server support and experimental room version</string>
|
<string name="labs_use_restricted_join_rule_desc">Warning requires server support and experimental room version</string>
|
||||||
<string name="user_invites_you">%s invites you</string>
|
<string name="user_invites_you">%s invites you</string>
|
||||||
|
|
||||||
|
<string name="looking_for_someone_not_in_space">Looking for someone not in %s?</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
Loading…
Reference in New Issue
Block a user