Merge pull request #3348 from vector-im/feature/bca/spaces_fix_invites

Fix space invite issues
This commit is contained in:
Benoit Marty 2021-05-18 13:56:01 +02:00 committed by GitHub
commit 0041743bf9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 319 additions and 118 deletions

View file

@ -8,6 +8,8 @@ Improvements 🙌:
- -
Bugfix 🐛: Bugfix 🐛:
- Space Invite by link not always displayed for public space (#3345)
- Wrong copy in share space bottom sheet (#3346)
- Fix a problem with database migration on nightly builds (#3335) - Fix a problem with database migration on nightly builds (#3335)
Translations 🗣: Translations 🗣:

View file

@ -78,12 +78,12 @@ import im.vector.app.features.settings.devices.DeviceVerificationInfoBottomSheet
import im.vector.app.features.share.IncomingShareActivity import im.vector.app.features.share.IncomingShareActivity
import im.vector.app.features.signout.soft.SoftLogoutActivity import im.vector.app.features.signout.soft.SoftLogoutActivity
import im.vector.app.features.spaces.InviteRoomSpaceChooserBottomSheet import im.vector.app.features.spaces.InviteRoomSpaceChooserBottomSheet
import im.vector.app.features.spaces.ShareSpaceBottomSheet
import im.vector.app.features.spaces.SpaceCreationActivity import im.vector.app.features.spaces.SpaceCreationActivity
import im.vector.app.features.spaces.SpaceExploreActivity import im.vector.app.features.spaces.SpaceExploreActivity
import im.vector.app.features.spaces.invite.SpaceInviteBottomSheet
import im.vector.app.features.spaces.SpaceSettingsMenuBottomSheet import im.vector.app.features.spaces.SpaceSettingsMenuBottomSheet
import im.vector.app.features.spaces.invite.SpaceInviteBottomSheet
import im.vector.app.features.spaces.manage.SpaceManageActivity import im.vector.app.features.spaces.manage.SpaceManageActivity
import im.vector.app.features.spaces.share.ShareSpaceBottomSheet
import im.vector.app.features.terms.ReviewTermsActivity import im.vector.app.features.terms.ReviewTermsActivity
import im.vector.app.features.ui.UiStateRepository import im.vector.app.features.ui.UiStateRepository
import im.vector.app.features.usercode.UserCodeActivity import im.vector.app.features.usercode.UserCodeActivity

View file

@ -59,11 +59,11 @@ import im.vector.app.features.rageshake.ReportType
import im.vector.app.features.rageshake.VectorUncaughtExceptionHandler import im.vector.app.features.rageshake.VectorUncaughtExceptionHandler
import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.settings.VectorPreferences
import im.vector.app.features.settings.VectorSettingsActivity import im.vector.app.features.settings.VectorSettingsActivity
import im.vector.app.features.spaces.ShareSpaceBottomSheet
import im.vector.app.features.spaces.SpaceCreationActivity import im.vector.app.features.spaces.SpaceCreationActivity
import im.vector.app.features.spaces.SpacePreviewActivity import im.vector.app.features.spaces.SpacePreviewActivity
import im.vector.app.features.spaces.SpaceSettingsMenuBottomSheet import im.vector.app.features.spaces.SpaceSettingsMenuBottomSheet
import im.vector.app.features.spaces.invite.SpaceInviteBottomSheet import im.vector.app.features.spaces.invite.SpaceInviteBottomSheet
import im.vector.app.features.spaces.share.ShareSpaceBottomSheet
import im.vector.app.features.themes.ThemeUtils import im.vector.app.features.themes.ThemeUtils
import im.vector.app.features.workers.signout.ServerBackupStatusViewModel import im.vector.app.features.workers.signout.ServerBackupStatusViewModel
import im.vector.app.features.workers.signout.ServerBackupStatusViewState import im.vector.app.features.workers.signout.ServerBackupStatusViewState

View file

@ -93,9 +93,9 @@ import im.vector.app.core.platform.showOptimizedSnackbar
import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.ui.views.ActiveConferenceView import im.vector.app.core.ui.views.ActiveConferenceView
import im.vector.app.core.ui.views.CurrentCallsView import im.vector.app.core.ui.views.CurrentCallsView
import im.vector.app.core.ui.views.FailedMessagesWarningView
import im.vector.app.core.ui.views.JumpToReadMarkerView import im.vector.app.core.ui.views.JumpToReadMarkerView
import im.vector.app.core.ui.views.KnownCallsViewHolder import im.vector.app.core.ui.views.KnownCallsViewHolder
import im.vector.app.core.ui.views.FailedMessagesWarningView
import im.vector.app.core.ui.views.NotificationAreaView import im.vector.app.core.ui.views.NotificationAreaView
import im.vector.app.core.utils.Debouncer import im.vector.app.core.utils.Debouncer
import im.vector.app.core.utils.DimensionConverter import im.vector.app.core.utils.DimensionConverter
@ -164,7 +164,7 @@ import im.vector.app.features.session.coroutineScope
import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.settings.VectorPreferences
import im.vector.app.features.settings.VectorSettingsActivity import im.vector.app.features.settings.VectorSettingsActivity
import im.vector.app.features.share.SharedData import im.vector.app.features.share.SharedData
import im.vector.app.features.spaces.ShareSpaceBottomSheet import im.vector.app.features.spaces.share.ShareSpaceBottomSheet
import im.vector.app.features.themes.ThemeUtils import im.vector.app.features.themes.ThemeUtils
import im.vector.app.features.widgets.WidgetActivity import im.vector.app.features.widgets.WidgetActivity
import im.vector.app.features.widgets.WidgetArgs import im.vector.app.features.widgets.WidgetArgs
@ -677,7 +677,7 @@ class RoomDetailFragment @Inject constructor(
private fun handleSpaceShare() { private fun handleSpaceShare() {
roomDetailArgs.openShareSpaceForId?.let { spaceId -> roomDetailArgs.openShareSpaceForId?.let { spaceId ->
ShareSpaceBottomSheet.show(childFragmentManager, spaceId) ShareSpaceBottomSheet.show(childFragmentManager, spaceId, true)
view?.post { view?.post {
handleChatEffect(ChatEffect.CONFETTI) handleChatEffect(ChatEffect.CONFETTI)
} }

View file

@ -1,110 +0,0 @@
/*
* 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
import android.os.Bundle
import android.os.Parcelable
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.fragment.app.FragmentManager
import im.vector.app.R
import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.di.ScreenComponent
import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
import im.vector.app.core.utils.startSharePlainTextIntent
import im.vector.app.databinding.BottomSheetSpaceInviteBinding
import im.vector.app.features.invite.InviteUsersToRoomActivity
import kotlinx.parcelize.Parcelize
import javax.inject.Inject
class ShareSpaceBottomSheet : VectorBaseBottomSheetDialogFragment<BottomSheetSpaceInviteBinding>() {
@Parcelize
data class Args(
val spaceId: String
) : Parcelable
override val showExpanded = true
@Inject
lateinit var activeSessionHolder: ActiveSessionHolder
override fun injectWith(injector: ScreenComponent) {
injector.inject(this)
}
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetSpaceInviteBinding {
return BottomSheetSpaceInviteBinding.inflate(inflater, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// Not going for full view model for now, as it may change
val args: Args = arguments?.getParcelable(EXTRA_ARGS)
?: return Unit.also { dismiss() }
val summary = activeSessionHolder.getSafeActiveSession()?.spaceService()?.getSpace(args.spaceId)?.spaceSummary()
val spaceName = summary?.name
views.descriptionText.text = getString(R.string.invite_people_to_your_space_desc, spaceName)
// XXX enable back when supported
views.inviteByMailButton.isVisible = false
views.inviteByMailButton.debouncedClicks {
}
views.inviteByMxidButton.debouncedClicks {
val intent = InviteUsersToRoomActivity.getIntent(requireContext(), args.spaceId)
startActivity(intent)
}
views.inviteByLinkButton.debouncedClicks {
activeSessionHolder.getSafeActiveSession()?.permalinkService()?.createRoomPermalink(args.spaceId)?.let { permalink ->
startSharePlainTextIntent(
fragment = this,
activityResultLauncher = null,
chooserTitle = getString(R.string.share_by_text),
text = getString(R.string.share_space_link_message, spaceName, permalink),
extraTitle = getString(R.string.share_space_link_message, spaceName, permalink)
)
}
}
// views.skipButton.debouncedClicks {
// dismiss()
// }
}
companion object {
const val EXTRA_ARGS = "EXTRA_ARGS"
fun show(fragmentManager: FragmentManager, spaceId: String): ShareSpaceBottomSheet {
return ShareSpaceBottomSheet().apply {
isCancelable = true
arguments = Bundle().apply {
this.putParcelable(EXTRA_ARGS, ShareSpaceBottomSheet.Args(spaceId = spaceId))
}
}.also {
it.show(fragmentManager, ShareSpaceBottomSheet::class.java.name)
}
}
}
}

View file

@ -43,6 +43,7 @@ import im.vector.app.features.spaces.manage.SpaceManageActivity
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
import org.matrix.android.sdk.api.util.toMatrixItem import org.matrix.android.sdk.api.util.toMatrixItem
@ -105,7 +106,7 @@ class SpaceSettingsMenuBottomSheet : VectorBaseBottomSheetDialogFragment<BottomS
views.spaceSettings.isVisible = canChangeAvatar || canChangeName || canChangeTopic views.spaceSettings.isVisible = canChangeAvatar || canChangeName || canChangeTopic
views.invitePeople.isVisible = canInvite views.invitePeople.isVisible = canInvite || roomSummary?.isPublic.orFalse()
views.addRooms.isVisible = canAddChild views.addRooms.isVisible = canAddChild
}.disposeOnDestroyView() }.disposeOnDestroyView()

View file

@ -28,7 +28,7 @@ import im.vector.app.core.extensions.hideKeyboard
import im.vector.app.core.platform.GenericIdArgs import im.vector.app.core.platform.GenericIdArgs
import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.databinding.ActivitySimpleLoadingBinding import im.vector.app.databinding.ActivitySimpleLoadingBinding
import im.vector.app.features.spaces.ShareSpaceBottomSheet import im.vector.app.features.spaces.share.ShareSpaceBottomSheet
class SpacePeopleActivity : VectorBaseActivity<ActivitySimpleLoadingBinding>() { class SpacePeopleActivity : VectorBaseActivity<ActivitySimpleLoadingBinding>() {

View file

@ -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.share
import im.vector.app.core.platform.VectorViewModelAction
sealed class ShareSpaceAction : VectorViewModelAction {
object InviteByMxId : ShareSpaceAction()
object InviteByLink : ShareSpaceAction()
}

View file

@ -0,0 +1,126 @@
/*
* 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.share
import android.os.Bundle
import android.os.Parcelable
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.fragment.app.FragmentManager
import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState
import im.vector.app.R
import im.vector.app.core.di.ScreenComponent
import im.vector.app.core.extensions.setTextOrHide
import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
import im.vector.app.core.utils.startSharePlainTextIntent
import im.vector.app.databinding.BottomSheetSpaceInviteBinding
import im.vector.app.features.invite.InviteUsersToRoomActivity
import kotlinx.parcelize.Parcelize
import javax.inject.Inject
class ShareSpaceBottomSheet : VectorBaseBottomSheetDialogFragment<BottomSheetSpaceInviteBinding>(), ShareSpaceViewModel.Factory {
@Parcelize
data class Args(
val spaceId: String,
val postCreation: Boolean = false
) : Parcelable
override val showExpanded = true
private val viewModel: ShareSpaceViewModel by fragmentViewModel(ShareSpaceViewModel::class)
@Inject lateinit var viewModelFactory: ShareSpaceViewModel.Factory
override fun create(initialState: ShareSpaceViewState): ShareSpaceViewModel = viewModelFactory.create(initialState)
override fun injectWith(injector: ScreenComponent) {
injector.inject(this)
}
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetSpaceInviteBinding {
return BottomSheetSpaceInviteBinding.inflate(inflater, container, false)
}
override fun invalidate() = withState(viewModel) { state ->
super.invalidate()
val summary = state.spaceSummary.invoke()
val spaceName = summary?.name
if (state.postCreation) {
views.headerText.text = getString(R.string.invite_people_to_your_space)
views.descriptionText.setTextOrHide(getString(R.string.invite_people_to_your_space_desc, spaceName))
} else {
views.headerText.text = getString(R.string.invite_to_space, spaceName)
views.descriptionText.setTextOrHide(null)
}
views.inviteByMailButton.isVisible = false // not yet implemented
views.inviteByLinkButton.isVisible = state.canShareLink
views.inviteByMxidButton.isVisible = state.canInviteByMxId
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// XXX enable back when supported
views.inviteByMailButton.isVisible = false
views.inviteByMailButton.debouncedClicks {
}
views.inviteByMxidButton.debouncedClicks {
viewModel.handle(ShareSpaceAction.InviteByMxId)
}
views.inviteByLinkButton.debouncedClicks {
viewModel.handle(ShareSpaceAction.InviteByLink)
}
viewModel.observeViewEvents { event ->
when (event) {
is ShareSpaceViewEvents.NavigateToInviteUser -> {
val intent = InviteUsersToRoomActivity.getIntent(requireContext(), event.spaceId)
startActivity(intent)
}
is ShareSpaceViewEvents.ShowInviteByLink -> {
startSharePlainTextIntent(
fragment = this,
activityResultLauncher = null,
chooserTitle = getString(R.string.share_by_text),
text = getString(R.string.share_space_link_message, event.spaceName, event.permalink),
extraTitle = getString(R.string.share_space_link_message, event.spaceName, event.permalink)
)
}
}
}
}
companion object {
fun show(fragmentManager: FragmentManager, spaceId: String, postCreation: Boolean = false): ShareSpaceBottomSheet {
return ShareSpaceBottomSheet().apply {
setArguments(Args(spaceId = spaceId, postCreation = postCreation))
}.also {
it.show(fragmentManager, ShareSpaceBottomSheet::class.java.name)
}
}
}
}

View file

@ -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.share
import im.vector.app.core.platform.VectorViewEvents
sealed class ShareSpaceViewEvents : VectorViewEvents {
data class NavigateToInviteUser(val spaceId: String) : ShareSpaceViewEvents()
data class ShowInviteByLink(val permalink: String, val spaceName: String) : ShareSpaceViewEvents()
}

View file

@ -0,0 +1,98 @@
/*
* 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.share
import com.airbnb.mvrx.ActivityViewModelContext
import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.ViewModelContext
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.features.powerlevel.PowerLevelsObservableFactory
import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper
class ShareSpaceViewModel @AssistedInject constructor(
@Assisted private val initialState: ShareSpaceViewState,
private val session: Session) : VectorViewModel<ShareSpaceViewState, ShareSpaceAction, ShareSpaceViewEvents>(initialState) {
@AssistedFactory
interface Factory {
fun create(initialState: ShareSpaceViewState): ShareSpaceViewModel
}
companion object : MvRxViewModelFactory<ShareSpaceViewModel, ShareSpaceViewState> {
override fun create(viewModelContext: ViewModelContext, state: ShareSpaceViewState): ShareSpaceViewModel? {
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")
}
}
init {
val roomSummary = session.getRoomSummary(initialState.spaceId)
setState {
copy(
spaceSummary = roomSummary?.let { Success(it) } ?: Uninitialized,
canShareLink = roomSummary?.isPublic.orFalse()
)
}
observePowerLevel()
}
private fun observePowerLevel() {
val room = session.getRoom(initialState.spaceId) ?: return
PowerLevelsObservableFactory(room)
.createObservable()
.subscribe { powerLevelContent ->
val powerLevelsHelper = PowerLevelsHelper(powerLevelContent)
setState {
copy(
canInviteByMxId = powerLevelsHelper.isUserAbleToInvite(session.myUserId)
)
}
}
.disposeOnClear()
}
override fun handle(action: ShareSpaceAction) {
when (action) {
ShareSpaceAction.InviteByLink -> {
val roomSummary = session.getRoomSummary(initialState.spaceId)
val alias = roomSummary?.canonicalAlias
val permalink = if (alias != null) {
session.permalinkService().createPermalink(alias)
} else {
session.permalinkService().createRoomPermalink(initialState.spaceId)
}
if (permalink != null) {
_viewEvents.post(ShareSpaceViewEvents.ShowInviteByLink(permalink, roomSummary?.name ?: ""))
}
}
ShareSpaceAction.InviteByMxId -> {
_viewEvents.post(ShareSpaceViewEvents.NavigateToInviteUser(initialState.spaceId))
}
}
}
}

View file

@ -0,0 +1,35 @@
/*
* 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.share
import com.airbnb.mvrx.Async
import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.Uninitialized
import org.matrix.android.sdk.api.session.room.model.RoomSummary
data class ShareSpaceViewState(
val spaceId: String,
val spaceSummary: Async<RoomSummary> = Uninitialized,
val canInviteByMxId: Boolean = false,
val canShareLink: Boolean = false,
val postCreation: Boolean = false
) : MvRxState {
constructor(args: ShareSpaceBottomSheet.Args) : this(
spaceId = args.spaceId,
postCreation = args.postCreation
)
}

View file

@ -3323,6 +3323,7 @@
<string name="create_space_topic_hint">Description</string> <string name="create_space_topic_hint">Description</string>
<string name="invite_people_to_your_space">Invite people to your space</string> <string name="invite_people_to_your_space">Invite people to your space</string>
<string name="invite_people_menu">Invite people</string> <string name="invite_people_menu">Invite people</string>
<string name="invite_to_space">Invite to %s</string>
<string name="invite_people_to_your_space_desc">Its just you at the moment. %s will be even better with others.</string> <string name="invite_people_to_your_space_desc">Its just you at the moment. %s will be even better with others.</string>
<string name="invite_by_email">Invite by email</string> <string name="invite_by_email">Invite by email</string>
<string name="invite_by_mxid">Invite by username</string> <string name="invite_by_mxid">Invite by username</string>