Private (me and teamates) space support

This commit is contained in:
Valere 2021-04-06 15:26:43 +02:00
parent bfbd37074e
commit 79bde6ee91
24 changed files with 461 additions and 65 deletions

View File

@ -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")
}

View File

@ -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")
}

View File

@ -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>
)

View File

@ -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
"public" -> RoomJoinRules.PUBLIC
"invite" -> RoomJoinRules.INVITE
"knock" -> RoomJoinRules.KNOCK
"private" -> RoomJoinRules.PRIVATE
else -> {
"restricted" -> RoomJoinRules.RESTRICTED
else -> {
Timber.w("Invalid value for RoomJoinRules: `$_joinRules`")
null
}
}
}

View File

@ -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"
}
}

View File

@ -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?
)

View File

@ -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()
)
}
}

View File

@ -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()
}
})
}

View File

@ -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
}

View File

@ -63,13 +63,13 @@ class RoomSettingsController @Inject constructor(
id("avatar")
enabled(data.actionPermissions.canChangeAvatar)
when (val avatarAction = data.avatarAction) {
RoomSettingsViewState.AvatarAction.None -> {
RoomSettingsViewState.AvatarAction.None -> {
// Use the current value
avatarRenderer(avatarRenderer)
// We do not want to use the fallback avatar url, which can be the other user avatar, or the current user avatar.
matrixItem(roomSummary.toMatrixItem().copy(avatarUrl = data.currentRoomAvatarUrl))
}
RoomSettingsViewState.AvatarAction.DeleteAvatar ->
RoomSettingsViewState.AvatarAction.DeleteAvatar ->
imageUri(null)
is RoomSettingsViewState.AvatarAction.UpdateAvatar ->
imageUri(avatarAction.newAvatarUri)
@ -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
// }
// })
}
}

View File

@ -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
)
)
}

View File

@ -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)

View File

@ -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
}
}

View File

@ -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))
}))
}
}

View File

@ -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()
}

View File

@ -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()

View File

@ -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
}
}

View File

@ -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
@ -152,12 +195,21 @@ class CreateSpaceViewModel @AssistedInject constructor(
)
}
} else {
setState {
copy(
step = CreateSpaceState.Step.AddRooms
)
if (state.spaceType == SpaceType.Private) {
setState {
copy(
step = CreateSpaceState.Step.ChoosePrivateType
)
}
_viewEvents.post(CreateSpaceEvents.NavigateToChoosePrivateType)
} else {
setState {
copy(
step = CreateSpaceState.Step.AddRooms
)
}
_viewEvents.post(CreateSpaceEvents.NavigateToAddRooms)
}
_viewEvents.post(CreateSpaceEvents.NavigateToAddRooms)
}
}
@ -187,7 +239,7 @@ class CreateSpaceViewModel @AssistedInject constructor(
}
_viewEvents.post(CreateSpaceEvents.FinishSuccess(result.spaceId, result.childIds.firstOrNull()))
}
is CreateSpaceTaskResult.PartialSuccess -> {
is CreateSpaceTaskResult.PartialSuccess -> {
// XXX what can we do here?
setState {
copy(creationResult = Success(result.spaceId))

View File

@ -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,38 +60,51 @@ 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> {
params.defaultRooms
.filter { it.isNotBlank() }
.forEach { roomName ->
try {
val roomId = try {
if (params.isPublic) {
awaitCallback {
session.createRoom(CreateRoomParams().apply {
this.name = roomName
this.preset = CreateRoomPreset.PRESET_PUBLIC_CHAT
}, it)
}
} catch (timeout: CreateRoomFailure.CreatedWithTimeout) {
// we ignore that?
timeout.roomID
} 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)
}
}
val via = session.sessionParams.homeServerHost?.let { listOf(it) } ?: emptyList()
createdSpace!!.addChildren(roomId, via, null, autoJoin = false, suggested = true)
// set canonical
session.spaceService().setSpaceParent(
roomId,
createdSpace.spaceId,
true,
via
)
childIds.add(roomId)
} catch (failure: Throwable) {
Timber.d("Space: Failed to create child room in $spaceID")
childErrors[roomName] = failure
} catch (timeout: CreateRoomFailure.CreatedWithTimeout) {
// we ignore that?
timeout.roomID
}
val via = session.sessionParams.homeServerHost?.let { listOf(it) } ?: emptyList()
createdSpace!!.addChildren(roomId, via, null, autoJoin = false, suggested = true)
// set canonical
session.spaceService().setSpaceParent(
roomId,
createdSpace.spaceId,
true,
via
)
childIds.add(roomId)
} catch (failure: Throwable) {
Timber.d("Space: Failed to create child room in $spaceID")
childErrors[roomName] = failure
}
}
}
return if (childErrors.isEmpty()) {
CreateSpaceTaskResult.Success(spaceID, childIds)

View File

@ -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))
}

View File

@ -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
}

View 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>

View File

@ -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>

View File

@ -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 rooms link, apart from guests</string>
<string name="room_settings_room_access_entry_anyone_with_link_including_guest">Anyone who knows the rooms 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 &amp; 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">Well 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">Lets 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>