mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2024-11-26 19:36:08 +03:00
Private (me and teamates) space support
This commit is contained in:
parent
bfbd37074e
commit
79bde6ee91
24 changed files with 461 additions and 65 deletions
|
@ -40,7 +40,7 @@ data class RoomGuestAccessContent(
|
|||
}
|
||||
|
||||
@JsonClass(generateAdapter = false)
|
||||
enum class GuestAccess {
|
||||
@Json(name = "can_join") CanJoin,
|
||||
@Json(name = "forbidden") Forbidden
|
||||
enum class GuestAccess(val value: String) {
|
||||
@Json(name = "can_join") CanJoin("can_join"),
|
||||
@Json(name = "forbidden") Forbidden("forbidden")
|
||||
}
|
||||
|
|
|
@ -24,9 +24,10 @@ import com.squareup.moshi.JsonClass
|
|||
* Enum for [RoomJoinRulesContent] : https://matrix.org/docs/spec/client_server/r0.4.0#m-room-join-rules
|
||||
*/
|
||||
@JsonClass(generateAdapter = false)
|
||||
enum class RoomJoinRules {
|
||||
@Json(name = "public") PUBLIC,
|
||||
@Json(name = "invite") INVITE,
|
||||
@Json(name = "knock") KNOCK,
|
||||
@Json(name = "private") PRIVATE
|
||||
enum class RoomJoinRules(val value: String) {
|
||||
@Json(name = "public") PUBLIC("public"),
|
||||
@Json(name = "invite") INVITE("invite"),
|
||||
@Json(name = "knock") KNOCK("knock"),
|
||||
@Json(name = "private") PRIVATE("private"),
|
||||
@Json(name = "restricted") RESTRICTED("restricted")
|
||||
}
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* 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 org.matrix.android.sdk.api.session.room.model
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class RoomJoinRulesAllowEntry(
|
||||
/**
|
||||
* space: The room ID of the space to check the membership of.
|
||||
*/
|
||||
@Json(name = "space") val spaceID: String,
|
||||
/**
|
||||
* via: A list of servers which may be used to peek for membership of the space.
|
||||
*/
|
||||
@Json(name = "via") val via: List<String>
|
||||
)
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
* Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -26,16 +27,22 @@ import timber.log.Timber
|
|||
*/
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class RoomJoinRulesContent(
|
||||
@Json(name = "join_rule") val _joinRules: String? = null
|
||||
@Json(name = "join_rule") val _joinRules: String? = null,
|
||||
/**
|
||||
* If the allow key is an empty list (or not a list at all), then the room reverts to standard public join rules
|
||||
*/
|
||||
@Json(name = "allow") val allowList:List<RoomJoinRulesAllowEntry>? = null
|
||||
) {
|
||||
val joinRules: RoomJoinRules? = when (_joinRules) {
|
||||
"public" -> RoomJoinRules.PUBLIC
|
||||
"invite" -> RoomJoinRules.INVITE
|
||||
"knock" -> RoomJoinRules.KNOCK
|
||||
"private" -> RoomJoinRules.PRIVATE
|
||||
"restricted" -> RoomJoinRules.RESTRICTED
|
||||
else -> {
|
||||
Timber.w("Invalid value for RoomJoinRules: `$_joinRules`")
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,8 @@ import org.matrix.android.sdk.api.session.room.model.GuestAccess
|
|||
import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRules
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesAllowEntry
|
||||
import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
|
||||
|
||||
// TODO Give a way to include other initial states
|
||||
|
@ -153,8 +155,14 @@ open class CreateRoomParams {
|
|||
algorithm = MXCRYPTO_ALGORITHM_MEGOLM
|
||||
}
|
||||
|
||||
|
||||
var roomVersion: String? = null
|
||||
|
||||
var joinRuleRestricted: List<RoomJoinRulesAllowEntry>? = null
|
||||
|
||||
companion object {
|
||||
private const val CREATION_CONTENT_KEY_M_FEDERATE = "m.federate"
|
||||
private const val CREATION_CONTENT_KEY_ROOM_TYPE = "org.matrix.msc1772.type"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -111,5 +111,13 @@ internal data class CreateRoomBody(
|
|||
* The power level content to override in the default power level event
|
||||
*/
|
||||
@Json(name = "power_level_content_override")
|
||||
val powerLevelContentOverride: PowerLevelsContent?
|
||||
val powerLevelContentOverride: PowerLevelsContent?,
|
||||
|
||||
/**
|
||||
* The room version to set for the room. If not provided, the homeserver is to use its configured default.
|
||||
* If provided, the homeserver will return a 400 error with the errcode M_UNSUPPORTED_ROOM_VERSION if it does not support the room version.
|
||||
*/
|
||||
@Json(name = "room_version")
|
||||
val roomVersion: String?
|
||||
)
|
||||
|
||||
|
|
|
@ -23,7 +23,11 @@ import org.matrix.android.sdk.api.session.events.model.EventType
|
|||
import org.matrix.android.sdk.api.session.events.model.toContent
|
||||
import org.matrix.android.sdk.api.session.identity.IdentityServiceError
|
||||
import org.matrix.android.sdk.api.session.identity.toMedium
|
||||
import org.matrix.android.sdk.api.session.room.model.GuestAccess
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomGuestAccessContent
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRules
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesContent
|
||||
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
|
||||
import org.matrix.android.sdk.api.util.MimeTypes
|
||||
import org.matrix.android.sdk.internal.crypto.DeviceListManager
|
||||
|
@ -73,11 +77,17 @@ internal class CreateRoomBodyBuilder @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
if (params.joinRuleRestricted != null) {
|
||||
params.roomVersion = "org.matrix.msc3083"
|
||||
params.historyVisibility = params.historyVisibility ?: RoomHistoryVisibility.SHARED
|
||||
params.guestAccess = params.guestAccess ?: GuestAccess.Forbidden
|
||||
}
|
||||
val initialStates = listOfNotNull(
|
||||
buildEncryptionWithAlgorithmEvent(params),
|
||||
buildHistoryVisibilityEvent(params),
|
||||
buildAvatarEvent(params),
|
||||
buildGuestAccess(params)
|
||||
buildGuestAccess(params),
|
||||
buildJoinRulesRestricted(params)
|
||||
)
|
||||
.takeIf { it.isNotEmpty() }
|
||||
|
||||
|
@ -92,7 +102,9 @@ internal class CreateRoomBodyBuilder @Inject constructor(
|
|||
initialStates = initialStates,
|
||||
preset = params.preset,
|
||||
isDirect = params.isDirect,
|
||||
powerLevelContentOverride = params.powerLevelContentOverride
|
||||
powerLevelContentOverride = params.powerLevelContentOverride,
|
||||
roomVersion = params.roomVersion
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -132,7 +144,21 @@ internal class CreateRoomBodyBuilder @Inject constructor(
|
|||
Event(
|
||||
type = EventType.STATE_ROOM_GUEST_ACCESS,
|
||||
stateKey = "",
|
||||
content = RoomGuestAccessContent(it.name).toContent()
|
||||
content = RoomGuestAccessContent(it.value).toContent()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildJoinRulesRestricted(params: CreateRoomParams): Event? {
|
||||
return params.joinRuleRestricted
|
||||
?.let { allowList ->
|
||||
Event(
|
||||
type = EventType.STATE_ROOM_JOIN_RULES,
|
||||
stateKey = "",
|
||||
content = RoomJoinRulesContent(
|
||||
_joinRules = RoomJoinRules.RESTRICTED.value,
|
||||
allowList = allowList
|
||||
).toContent()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import org.matrix.android.sdk.api.session.events.model.toModel
|
|||
import org.matrix.android.sdk.api.session.room.model.GuestAccess
|
||||
import org.matrix.android.sdk.api.session.room.model.Membership
|
||||
import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo
|
||||
|
@ -67,14 +68,18 @@ internal class DefaultSpaceService @Inject constructor(
|
|||
return createSpace(CreateSpaceParams().apply {
|
||||
this.name = name
|
||||
this.topic = topic
|
||||
this.preset = if (isPublic) CreateRoomPreset.PRESET_PUBLIC_CHAT else CreateRoomPreset.PRESET_PRIVATE_CHAT
|
||||
this.avatarUri = avatarUri
|
||||
if (isPublic) {
|
||||
this.powerLevelContentOverride = (powerLevelContentOverride ?: PowerLevelsContent()).copy(
|
||||
invite = 0
|
||||
)
|
||||
this.preset = CreateRoomPreset.PRESET_PUBLIC_CHAT
|
||||
this.historyVisibility = RoomHistoryVisibility.WORLD_READABLE
|
||||
this.guestAccess = GuestAccess.CanJoin
|
||||
} else {
|
||||
this.preset = CreateRoomPreset.PRESET_PRIVATE_CHAT
|
||||
visibility = RoomDirectoryVisibility.PRIVATE
|
||||
enableEncryption()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -120,6 +120,7 @@ import im.vector.app.features.settings.threepids.ThreePidsSettingsFragment
|
|||
import im.vector.app.features.share.IncomingShareFragment
|
||||
import im.vector.app.features.signout.soft.SoftLogoutFragment
|
||||
import im.vector.app.features.spaces.SpaceListFragment
|
||||
import im.vector.app.features.spaces.create.ChoosePrivateSpaceTypeFragment
|
||||
import im.vector.app.features.spaces.create.ChooseSpaceTypeFragment
|
||||
import im.vector.app.features.spaces.create.CreateSpaceDefaultRoomsFragment
|
||||
import im.vector.app.features.spaces.create.CreateSpaceDetailsFragment
|
||||
|
@ -672,4 +673,9 @@ interface FragmentModule {
|
|||
@IntoMap
|
||||
@FragmentKey(SpaceDirectoryFragment::class)
|
||||
fun bindSpaceDirectoryFragment(fragment: SpaceDirectoryFragment): Fragment
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@FragmentKey(ChoosePrivateSpaceTypeFragment::class)
|
||||
fun bindChoosePrivateSpaceTypeFragment(fragment: ChoosePrivateSpaceTypeFragment): Fragment
|
||||
}
|
||||
|
|
|
@ -128,14 +128,36 @@ class RoomSettingsController @Inject constructor(
|
|||
private fun RoomSettingsViewState.getJoinRuleWording(): String {
|
||||
val joinRule = newRoomJoinRules.newJoinRules ?: currentRoomJoinRules
|
||||
val guestAccess = newRoomJoinRules.newGuestAccess ?: currentGuestAccess
|
||||
return stringProvider.getString(if (joinRule == RoomJoinRules.INVITE) {
|
||||
R.string.room_settings_room_access_entry_only_invited
|
||||
} else {
|
||||
if (guestAccess == GuestAccess.CanJoin) {
|
||||
R.string.room_settings_room_access_entry_anyone_with_link_including_guest
|
||||
} else {
|
||||
R.string.room_settings_room_access_entry_anyone_with_link_apart_guest
|
||||
val resId = when (joinRule) {
|
||||
RoomJoinRules.INVITE -> {
|
||||
R.string.room_settings_room_access_entry_only_invited to null
|
||||
}
|
||||
})
|
||||
RoomJoinRules.PRIVATE -> {
|
||||
R.string.room_settings_room_access_entry_unknown to joinRule.value
|
||||
}
|
||||
RoomJoinRules.PUBLIC -> {
|
||||
if (guestAccess == GuestAccess.CanJoin) {
|
||||
R.string.room_settings_room_access_entry_anyone_with_link_including_guest to null
|
||||
} else {
|
||||
R.string.room_settings_room_access_entry_anyone_with_link_apart_guest to null
|
||||
}
|
||||
}
|
||||
RoomJoinRules.KNOCK -> {
|
||||
R.string.room_settings_room_access_entry_knock to null
|
||||
}
|
||||
RoomJoinRules.RESTRICTED -> {
|
||||
R.string.room_settings_room_access_entry_restricted to null
|
||||
}
|
||||
}
|
||||
return if (resId.second == null) stringProvider.getString(resId.first) else stringProvider.getString(resId.first, resId.second)
|
||||
// return stringProvider.getString(if (joinRule == RoomJoinRules.INVITE) {
|
||||
// R.string.room_settings_room_access_entry_only_invited
|
||||
// } else {
|
||||
// if (guestAccess == GuestAccess.CanJoin) {
|
||||
// R.string.room_settings_room_access_entry_anyone_with_link_including_guest
|
||||
// } else {
|
||||
// R.string.room_settings_room_access_entry_anyone_with_link_apart_guest
|
||||
// }
|
||||
// })
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,6 +51,13 @@ class RoomJoinRuleController @Inject constructor(
|
|||
title = stringProvider.getString(R.string.room_settings_room_access_entry_anyone_with_link_including_guest),
|
||||
iconResId = 0,
|
||||
isSelected = state.currentRoomJoinRule == RoomJoinRules.PUBLIC && state.currentGuestAccess == GuestAccess.CanJoin
|
||||
),
|
||||
RoomJoinRuleAction(
|
||||
roomJoinRule = RoomJoinRules.RESTRICTED,
|
||||
roomGuestAccess = null,
|
||||
title = stringProvider.getString(R.string.room_settings_room_access_entry_restricted),
|
||||
iconResId = 0,
|
||||
isSelected = state.currentRoomJoinRule == RoomJoinRules.RESTRICTED
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ import im.vector.app.R
|
|||
import im.vector.app.core.di.ScreenComponent
|
||||
import im.vector.app.core.extensions.toMvRxBundle
|
||||
import im.vector.app.core.platform.SimpleFragmentActivity
|
||||
import im.vector.app.features.spaces.create.ChoosePrivateSpaceTypeFragment
|
||||
import im.vector.app.features.spaces.create.ChooseSpaceTypeFragment
|
||||
import im.vector.app.features.spaces.create.CreateSpaceAction
|
||||
import im.vector.app.features.spaces.create.CreateSpaceDefaultRoomsFragment
|
||||
|
@ -35,6 +36,7 @@ import im.vector.app.features.spaces.create.CreateSpaceDetailsFragment
|
|||
import im.vector.app.features.spaces.create.CreateSpaceEvents
|
||||
import im.vector.app.features.spaces.create.CreateSpaceState
|
||||
import im.vector.app.features.spaces.create.CreateSpaceViewModel
|
||||
import im.vector.app.features.spaces.create.SpaceType
|
||||
import javax.inject.Inject
|
||||
|
||||
class SpaceCreationActivity : SimpleFragmentActivity(), CreateSpaceViewModel.Factory {
|
||||
|
@ -85,6 +87,9 @@ class SpaceCreationActivity : SimpleFragmentActivity(), CreateSpaceViewModel.Fac
|
|||
CreateSpaceEvents.NavigateToAddRooms -> {
|
||||
navigateToFragment(CreateSpaceDefaultRoomsFragment::class.java)
|
||||
}
|
||||
CreateSpaceEvents.NavigateToChoosePrivateType -> {
|
||||
navigateToFragment(ChoosePrivateSpaceTypeFragment::class.java)
|
||||
}
|
||||
is CreateSpaceEvents.ShowModalError -> {
|
||||
hideWaitingView()
|
||||
AlertDialog.Builder(this)
|
||||
|
@ -124,8 +129,12 @@ class SpaceCreationActivity : SimpleFragmentActivity(), CreateSpaceViewModel.Fac
|
|||
private fun renderState(state: CreateSpaceState) {
|
||||
val titleRes = when (state.step) {
|
||||
CreateSpaceState.Step.ChooseType -> R.string.activity_create_space_title
|
||||
CreateSpaceState.Step.SetDetails -> R.string.your_public_space
|
||||
CreateSpaceState.Step.AddRooms -> R.string.your_public_space
|
||||
CreateSpaceState.Step.SetDetails,
|
||||
CreateSpaceState.Step.AddRooms -> {
|
||||
if (state.spaceType == SpaceType.Public) R.string.your_public_space
|
||||
else R.string.your_private_space
|
||||
}
|
||||
CreateSpaceState.Step.ChoosePrivateType -> R.string.your_private_space
|
||||
}
|
||||
supportActionBar?.let {
|
||||
it.title = getString(titleRes)
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* 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.create
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.airbnb.mvrx.activityViewModel
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.platform.OnBackPressed
|
||||
import im.vector.app.core.platform.VectorBaseFragment
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
import im.vector.app.core.utils.DebouncedClickListener
|
||||
import im.vector.app.databinding.FragmentSpaceCreateChoosePrivateModelBinding
|
||||
import javax.inject.Inject
|
||||
|
||||
class ChoosePrivateSpaceTypeFragment @Inject constructor(
|
||||
private val stringProvider: StringProvider
|
||||
) : VectorBaseFragment<FragmentSpaceCreateChoosePrivateModelBinding>(), OnBackPressed {
|
||||
|
||||
private val sharedViewModel: CreateSpaceViewModel by activityViewModel()
|
||||
|
||||
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?) =
|
||||
FragmentSpaceCreateChoosePrivateModelBinding.inflate(layoutInflater, container, false)
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
views.justMeButton.setOnClickListener(DebouncedClickListener({
|
||||
vectorBaseActivity.notImplemented("Organize room as space is not yet implemented")
|
||||
// sharedViewModel.handle(CreateSpaceAction.SetSpaceTopology(SpaceTopology.JustMe))
|
||||
}))
|
||||
|
||||
views.teammatesButton.setOnClickListener(DebouncedClickListener({
|
||||
sharedViewModel.handle(CreateSpaceAction.SetSpaceTopology(SpaceTopology.MeAndTeammates))
|
||||
}))
|
||||
|
||||
sharedViewModel.subscribe { state ->
|
||||
views.accessInfoHelpText.text = stringProvider.getString(R.string.create_spaces_make_sure_access, state.name ?: "")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBackPressed(toolbarButton: Boolean): Boolean {
|
||||
sharedViewModel.handle(CreateSpaceAction.OnBackPressed)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
@ -41,8 +41,7 @@ class ChooseSpaceTypeFragment @Inject constructor() : VectorBaseFragment<Fragmen
|
|||
}))
|
||||
|
||||
views.privateButton.setOnClickListener(DebouncedClickListener({
|
||||
vectorBaseActivity.notImplemented("Creating private space")
|
||||
// sharedViewModel.handle(CreateSpaceAction.SetRoomType(SpaceType.Private))
|
||||
sharedViewModel.handle(CreateSpaceAction.SetRoomType(SpaceType.Private))
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,4 +28,6 @@ sealed class CreateSpaceAction : VectorViewModelAction {
|
|||
object NextFromDetails : CreateSpaceAction()
|
||||
object NextFromDefaultRooms : CreateSpaceAction()
|
||||
data class DefaultRoomNameChanged(val index: Int, val name: String) : CreateSpaceAction()
|
||||
data class SetSpaceTopology(val topology: SpaceTopology) : CreateSpaceAction()
|
||||
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ sealed class CreateSpaceEvents : VectorViewEvents {
|
|||
object NavigateToDetails : CreateSpaceEvents()
|
||||
object NavigateToChooseType : CreateSpaceEvents()
|
||||
object NavigateToAddRooms : CreateSpaceEvents()
|
||||
object NavigateToChoosePrivateType : CreateSpaceEvents()
|
||||
object Dismiss : CreateSpaceEvents()
|
||||
data class FinishSuccess(val spaceId: String, val defaultRoomId: String?) : CreateSpaceEvents()
|
||||
data class ShowModalError(val errorMessage: String) : CreateSpaceEvents()
|
||||
|
|
|
@ -27,6 +27,7 @@ data class CreateSpaceState(
|
|||
val topic: String = "",
|
||||
val step: Step = Step.ChooseType,
|
||||
val spaceType: SpaceType? = null,
|
||||
val spaceTopology: SpaceTopology? = null,
|
||||
val nameInlineError: String? = null,
|
||||
val defaultRooms: Map<Int, String?>? = null,
|
||||
val creationResult: Async<String> = Uninitialized
|
||||
|
@ -35,6 +36,8 @@ data class CreateSpaceState(
|
|||
enum class Step {
|
||||
ChooseType,
|
||||
SetDetails,
|
||||
AddRooms
|
||||
AddRooms,
|
||||
ChoosePrivateType
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -115,9 +115,34 @@ class CreateSpaceViewModel @AssistedInject constructor(
|
|||
is CreateSpaceAction.SetAvatar -> {
|
||||
setState { copy(avatarUri = action.uri) }
|
||||
}
|
||||
is CreateSpaceAction.SetSpaceTopology -> {
|
||||
handleSetTopology(action)
|
||||
}
|
||||
}.exhaustive
|
||||
}
|
||||
|
||||
private fun handleSetTopology(action: CreateSpaceAction.SetSpaceTopology) {
|
||||
when (action.topology) {
|
||||
SpaceTopology.JustMe -> {
|
||||
setState {
|
||||
copy(
|
||||
spaceTopology = SpaceTopology.JustMe
|
||||
)
|
||||
}
|
||||
// XXX finish and open the add rooms directly
|
||||
}
|
||||
SpaceTopology.MeAndTeammates -> {
|
||||
setState {
|
||||
copy(
|
||||
spaceTopology = SpaceTopology.MeAndTeammates,
|
||||
step = CreateSpaceState.Step.AddRooms
|
||||
)
|
||||
}
|
||||
_viewEvents.post(CreateSpaceEvents.NavigateToAddRooms)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleBackNavigation() = withState { state ->
|
||||
when (state.step) {
|
||||
CreateSpaceState.Step.ChooseType -> {
|
||||
|
@ -134,6 +159,24 @@ class CreateSpaceViewModel @AssistedInject constructor(
|
|||
_viewEvents.post(CreateSpaceEvents.NavigateToChooseType)
|
||||
}
|
||||
CreateSpaceState.Step.AddRooms -> {
|
||||
if (state.spaceType == SpaceType.Private && state.spaceTopology == SpaceTopology.MeAndTeammates) {
|
||||
setState {
|
||||
copy(
|
||||
spaceTopology = null,
|
||||
step = CreateSpaceState.Step.ChoosePrivateType
|
||||
)
|
||||
}
|
||||
_viewEvents.post(CreateSpaceEvents.NavigateToChoosePrivateType)
|
||||
} else {
|
||||
setState {
|
||||
copy(
|
||||
step = CreateSpaceState.Step.SetDetails
|
||||
)
|
||||
}
|
||||
_viewEvents.post(CreateSpaceEvents.NavigateToDetails)
|
||||
}
|
||||
}
|
||||
CreateSpaceState.Step.ChoosePrivateType -> {
|
||||
setState {
|
||||
copy(
|
||||
step = CreateSpaceState.Step.SetDetails
|
||||
|
@ -151,6 +194,14 @@ class CreateSpaceViewModel @AssistedInject constructor(
|
|||
nameInlineError = stringProvider.getString(R.string.create_space_error_empty_field_space_name)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if (state.spaceType == SpaceType.Private) {
|
||||
setState {
|
||||
copy(
|
||||
step = CreateSpaceState.Step.ChoosePrivateType
|
||||
)
|
||||
}
|
||||
_viewEvents.post(CreateSpaceEvents.NavigateToChoosePrivateType)
|
||||
} else {
|
||||
setState {
|
||||
copy(
|
||||
|
@ -160,6 +211,7 @@ class CreateSpaceViewModel @AssistedInject constructor(
|
|||
_viewEvents.post(CreateSpaceEvents.NavigateToAddRooms)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleNextFromDefaultRooms() = withState { state ->
|
||||
val spaceName = state.name ?: return@withState
|
||||
|
|
|
@ -20,6 +20,7 @@ import android.net.Uri
|
|||
import im.vector.app.core.platform.ViewModelTask
|
||||
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.RoomJoinRulesAllowEntry
|
||||
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
|
||||
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomPreset
|
||||
import org.matrix.android.sdk.internal.util.awaitCallback
|
||||
|
@ -59,18 +60,32 @@ class CreateSpaceViewModelTask @Inject constructor(
|
|||
|
||||
val childErrors = mutableMapOf<String, Throwable>()
|
||||
val childIds = mutableListOf<String>()
|
||||
if (params.isPublic) {
|
||||
params.defaultRooms
|
||||
.filter { it.isNotBlank() }
|
||||
.forEach { roomName ->
|
||||
try {
|
||||
|
||||
val roomId = try {
|
||||
awaitCallback<String> {
|
||||
if (params.isPublic) {
|
||||
awaitCallback {
|
||||
session.createRoom(CreateRoomParams().apply {
|
||||
this.name = roomName
|
||||
this.preset = CreateRoomPreset.PRESET_PUBLIC_CHAT
|
||||
}, it)
|
||||
}
|
||||
} else {
|
||||
awaitCallback { callback ->
|
||||
session.createRoom(CreateRoomParams().apply {
|
||||
this.name = roomName
|
||||
this.joinRuleRestricted = listOf(
|
||||
RoomJoinRulesAllowEntry(
|
||||
spaceID = spaceID,
|
||||
via = session.sessionParams.homeServerHost?.let { listOf(it) } ?: emptyList()
|
||||
)
|
||||
)
|
||||
}, callback)
|
||||
}
|
||||
}
|
||||
} catch (timeout: CreateRoomFailure.CreatedWithTimeout) {
|
||||
// we ignore that?
|
||||
timeout.roomID
|
||||
|
@ -90,7 +105,6 @@ class CreateSpaceViewModelTask @Inject constructor(
|
|||
childErrors[roomName] = failure
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return if (childErrors.isEmpty()) {
|
||||
CreateSpaceTaskResult.Success(spaceID, childIds)
|
||||
|
|
|
@ -39,13 +39,27 @@ class SpaceDefaultRoomEpoxyController @Inject constructor(
|
|||
genericFooterItem {
|
||||
id("info_help_header")
|
||||
style(ItemStyle.TITLE)
|
||||
text(stringProvider.getString(R.string.create_spaces_room_public_header, data?.name))
|
||||
text(
|
||||
if (data?.spaceType == SpaceType.Public) {
|
||||
stringProvider.getString(R.string.create_spaces_room_public_header, data.name)
|
||||
} else {
|
||||
stringProvider.getString(R.string.create_spaces_room_private_header)
|
||||
}
|
||||
)
|
||||
textColor(colorProvider.getColorFromAttribute(R.attr.riot_primary_text_color))
|
||||
}
|
||||
|
||||
genericFooterItem {
|
||||
id("info_help")
|
||||
text(stringProvider.getString(R.string.create_spaces_room_public_header_desc))
|
||||
text(
|
||||
stringProvider.getString(
|
||||
if (data?.spaceType == SpaceType.Public) {
|
||||
R.string.create_spaces_room_public_header_desc
|
||||
} else {
|
||||
R.string.create_spaces_room_private_header_desc
|
||||
}
|
||||
)
|
||||
)
|
||||
textColor(colorProvider.getColorFromAttribute(R.attr.riotx_text_secondary))
|
||||
}
|
||||
|
||||
|
|
|
@ -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.create
|
||||
|
||||
enum class SpaceTopology {
|
||||
JustMe,
|
||||
MeAndTeammates
|
||||
}
|
18
vector/src/main/res/drawable/ic_user_round.xml
Normal file
18
vector/src/main/res/drawable/ic_user_round.xml
Normal file
|
@ -0,0 +1,18 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M17.5911,20.2922C15.9951,21.3704 14.0711,22 12,22C9.7488,22 7.6713,21.2561 6,20.0007C3.5711,18.1763 2,15.2716 2,12C2,6.4771 6.4771,2 12,2C17.5228,2 22,6.4771 22,12C22,15.4518 20.2511,18.4951 17.5911,20.2922ZM12,12.5C13.6569,12.5 15,11.0449 15,9.25C15,7.4551 13.6569,6 12,6C10.3431,6 9,7.4551 9,9.25C9,11.0449 10.3431,12.5 12,12.5ZM12,20C14.162,20 16.1236,19.1424 17.5634,17.7488C16.673,15.5506 14.5176,14 12,14C9.4824,14 7.327,15.5506 6.4366,17.7488C7.8763,19.1424 9.838,20 12,20Z"
|
||||
android:fillColor="#C1C6CD"
|
||||
android:fillType="evenOdd"/>
|
||||
<group>
|
||||
<clip-path
|
||||
android:pathData="M17.5911,20.2922C15.9951,21.3704 14.0711,22 12,22C9.7488,22 7.6713,21.2561 6,20.0007C3.5711,18.1763 2,15.2716 2,12C2,6.4771 6.4771,2 12,2C17.5228,2 22,6.4771 22,12C22,15.4518 20.2511,18.4951 17.5911,20.2922ZM12,12.5C13.6569,12.5 15,11.0449 15,9.25C15,7.4551 13.6569,6 12,6C10.3431,6 9,7.4551 9,9.25C9,11.0449 10.3431,12.5 12,12.5ZM12,20C14.162,20 16.1236,19.1424 17.5634,17.7488C16.673,15.5506 14.5176,14 12,14C9.4824,14 7.327,15.5506 6.4366,17.7488C7.8763,19.1424 9.838,20 12,20Z"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M17.5911,20.2922L16.4715,18.6349L17.5911,20.2922ZM6,20.0007L4.7989,21.5999L4.7989,21.5999L6,20.0007ZM17.5634,17.7488L18.9544,19.1859L19.9234,18.2479L19.4171,16.998L17.5634,17.7488ZM6.4366,17.7488L4.5829,16.998L4.0766,18.2479L5.0456,19.1859L6.4366,17.7488ZM12,24C14.4825,24 16.7945,23.244 18.7107,21.9494L16.4715,18.6349C15.1957,19.4968 13.6596,20 12,20V24ZM4.7989,21.5999C6.8046,23.1065 9.3008,24 12,24V20C10.1967,20 8.538,19.4058 7.2011,18.4016L4.7989,21.5999ZM0,12C0,15.9273 1.8887,19.414 4.7989,21.5999L7.2011,18.4016C5.2535,16.9387 4,14.616 4,12H0ZM12,0C5.3726,0 0,5.3726 0,12H4C4,7.5817 7.5817,4 12,4V0ZM24,12C24,5.3726 18.6274,0 12,0V4C16.4183,4 20,7.5817 20,12H24ZM18.7107,21.9494C21.8977,19.7963 24,16.144 24,12H20C20,14.7596 18.6045,17.1939 16.4715,18.6349L18.7107,21.9494ZM13,9.25C13,10.0941 12.4046,10.5 12,10.5V14.5C14.9091,14.5 17,11.9958 17,9.25H13ZM12,8C12.4046,8 13,8.4059 13,9.25H17C17,6.5043 14.9091,4 12,4V8ZM11,9.25C11,8.4059 11.5954,8 12,8V4C9.0909,4 7,6.5043 7,9.25H11ZM12,10.5C11.5954,10.5 11,10.0941 11,9.25H7C7,11.9958 9.0909,14.5 12,14.5V10.5ZM16.1724,16.3118C15.0906,17.3588 13.6223,18 12,18V22C14.7017,22 17.1567,20.926 18.9544,19.1859L16.1724,16.3118ZM12,16C13.6752,16 15.1146,17.0305 15.7097,18.4996L19.4171,16.998C18.2314,14.0707 15.3599,12 12,12V16ZM8.2903,18.4996C8.8854,17.0305 10.3248,16 12,16V12C8.6401,12 5.7686,14.0707 4.5829,16.998L8.2903,18.4996ZM12,18C10.3777,18 8.9094,17.3588 7.8276,16.3118L5.0456,19.1859C6.8433,20.926 9.2983,22 12,22V18Z"
|
||||
android:fillColor="#C1C6CD"/>
|
||||
</group>
|
||||
</vector>
|
|
@ -0,0 +1,64 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView 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:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fillViewport="true">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/headerText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:text="@string/create_spaces_who_are_you_working_with"
|
||||
android:textColor="?riotx_text_primary"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/accessInfoHelpText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:gravity="center"
|
||||
android:textColor="?riotx_text_secondary"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintTop_toBottomOf="@id/headerText"
|
||||
tools:text="@string/create_spaces_make_sure_access" />
|
||||
|
||||
<im.vector.app.features.spaces.create.WizardButtonView
|
||||
android:id="@+id/justMeButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
app:icon="@drawable/ic_user_round"
|
||||
app:iconTint="?riotx_text_secondary"
|
||||
app:layout_constraintBottom_toTopOf="@id/teammatesButton"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:subTitle="@string/create_spaces_organise_rooms"
|
||||
app:title="@string/create_spaces_just_me" />
|
||||
|
||||
<im.vector.app.features.spaces.create.WizardButtonView
|
||||
android:id="@+id/teammatesButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
app:icon="@drawable/ic_member_small"
|
||||
app:iconTint="?riotx_text_secondary"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:subTitle="@string/create_spaces_private_teammates"
|
||||
app:title="@string/create_spaces_me_and_teammates" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</ScrollView>
|
|
@ -1451,6 +1451,9 @@
|
|||
<string name="room_settings_room_access_entry_only_invited">Only people who have been invited</string>
|
||||
<string name="room_settings_room_access_entry_anyone_with_link_apart_guest">Anyone who knows the room’s link, apart from guests</string>
|
||||
<string name="room_settings_room_access_entry_anyone_with_link_including_guest">Anyone who knows the room’s link, including guests</string>
|
||||
<string name="room_settings_room_access_entry_restricted">Restricted to members of a parent space</string>
|
||||
<string name="room_settings_room_access_entry_knock">Anyone can knock on the room, members can then accept or reject</string>
|
||||
<string name="room_settings_room_access_entry_unknown">Unknown access setting (%s)</string>
|
||||
|
||||
<!-- Room settings: banned users -->
|
||||
<string name="room_settings_banned_users_title">Banned users</string>
|
||||
|
@ -3264,10 +3267,17 @@
|
|||
|
||||
<string name="add_space">Add Space</string>
|
||||
<string name="your_public_space">Your public space</string>
|
||||
<string name="your_private_space">Your private space</string>
|
||||
<string name="create_spaces_type_header">Spaces are a new way to group rooms and people</string>
|
||||
<string name="create_spaces_choose_type_label">What type of space do you want to create?</string>
|
||||
<string name="create_spaces_you_can_change_later">You can change this later</string>
|
||||
<string name="create_spaces_join_info_help">To join an existing space, you need an invite.</string>
|
||||
<string name="create_spaces_who_are_you_working_with">Who are you working with?</string>
|
||||
<string name="create_spaces_make_sure_access">Make sure the right people have access to %s. You can change this later.</string>
|
||||
<string name="create_spaces_just_me">Just me</string>
|
||||
<string name="create_spaces_organise_rooms">A private space to organise your rooms</string>
|
||||
<string name="create_spaces_me_and_teammates">Me and teammates</string>
|
||||
<string name="create_spaces_private_teammates">A private space for you & your teammates</string>
|
||||
<string name="space_type_public">Public</string>
|
||||
<string name="space_type_public_desc">Open to anyone, best for communities</string>
|
||||
<string name="space_type_private">Private</string>
|
||||
|
@ -3278,6 +3288,8 @@
|
|||
<string name="create_space_error_empty_field_space_name">Give it a name to continue.</string>
|
||||
<string name="create_spaces_room_public_header">What are some discussions you want to have in %s?</string>
|
||||
<string name="create_spaces_room_public_header_desc">We’ll create rooms for them. You can add more later too.</string>
|
||||
<string name="create_spaces_room_private_header">What things are you working on?</string>
|
||||
<string name="create_spaces_room_private_header_desc">Let’s create a room for each of them. You can add more later too, including already existing ones.</string>
|
||||
<string name="create_spaces_default_public_room_name">General</string>
|
||||
<string name="create_spaces_default_public_random_room_name">Random</string>
|
||||
<string name="create_spaces_loading_message">Creating Space…</string>
|
||||
|
|
Loading…
Reference in a new issue