Space Create Wizard Flow

This commit is contained in:
Valere 2021-02-25 00:23:53 +01:00
parent 6c69a6055d
commit 7d2d7b411e
27 changed files with 481 additions and 68 deletions

View file

@ -21,7 +21,7 @@ import org.matrix.android.sdk.api.failure.MatrixError
import org.matrix.android.sdk.api.session.room.alias.RoomAliasError import org.matrix.android.sdk.api.session.room.alias.RoomAliasError
sealed class CreateRoomFailure : Failure.FeatureFailure() { sealed class CreateRoomFailure : Failure.FeatureFailure() {
object CreatedWithTimeout : CreateRoomFailure() data class CreatedWithTimeout(val roomID: String) : CreateRoomFailure()
data class CreatedWithFederationFailure(val matrixError: MatrixError) : CreateRoomFailure() data class CreatedWithFederationFailure(val matrixError: MatrixError) : CreateRoomFailure()
data class AliasError(val aliasError: RoomAliasError) : CreateRoomFailure() data class AliasError(val aliasError: RoomAliasError) : CreateRoomFailure()
} }

View file

@ -17,11 +17,17 @@
package org.matrix.android.sdk.api.session.space package org.matrix.android.sdk.api.session.space
import org.matrix.android.sdk.api.session.room.Room import org.matrix.android.sdk.api.session.room.Room
import org.matrix.android.sdk.api.session.room.model.RoomSummary
interface Space { interface Space {
fun asRoom() : Room fun asRoom() : Room
/**
* A current snapshot of [RoomSummary] associated with the room
*/
fun spaceSummary(): SpaceSummary?
suspend fun addChildren(roomId: String, viaServers: List<String>, order: String?, autoJoin: Boolean = false) suspend fun addChildren(roomId: String, viaServers: List<String>, order: String?, autoJoin: Boolean = false)
suspend fun removeRoom(roomId: String) suspend fun removeRoom(roomId: String)

View file

@ -66,8 +66,13 @@ internal class DefaultRoomService @Inject constructor(
private val roomChangeMembershipStateDataSource: RoomChangeMembershipStateDataSource private val roomChangeMembershipStateDataSource: RoomChangeMembershipStateDataSource
) : RoomService { ) : RoomService {
override suspend fun createRoom(createRoomParams: CreateRoomParams): String { override fun createRoom(createRoomParams: CreateRoomParams, callback: MatrixCallback<String>): Cancelable {
return createRoomTask.execute(createRoomParams) return createRoomTask
.configureWith(createRoomParams) {
this.callback = callback
this.retryCount = 3
}
.executeBy(taskExecutor)
} }
override fun getRoom(roomId: String): Room? { override fun getRoom(roomId: String): Room? {

View file

@ -0,0 +1,39 @@
/*
* 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.internal.session.room
import org.matrix.android.sdk.api.session.room.model.RoomType
import org.matrix.android.sdk.api.session.space.Space
import org.matrix.android.sdk.internal.session.space.DefaultSpace
import org.matrix.android.sdk.internal.session.space.SpaceSummaryDataSource
import javax.inject.Inject
internal interface SpaceGetter {
fun get(spaceId: String): Space?
}
internal class DefaultSpaceGetter @Inject constructor(
private val roomGetter: RoomGetter,
private val spaceSummaryDataSource: SpaceSummaryDataSource
) : SpaceGetter {
override fun get(spaceId: String): Space? {
return roomGetter.getRoom(spaceId)
?.takeIf { it.roomSummary()?.roomType == RoomType.SPACE }
?.let { DefaultSpace(it, spaceSummaryDataSource) }
}
}

View file

@ -102,7 +102,7 @@ internal class DefaultCreateRoomTask @Inject constructor(
.equalTo(RoomSummaryEntityFields.MEMBERSHIP_STR, Membership.JOIN.name) .equalTo(RoomSummaryEntityFields.MEMBERSHIP_STR, Membership.JOIN.name)
} }
} catch (exception: TimeoutCancellationException) { } catch (exception: TimeoutCancellationException) {
throw CreateRoomFailure.CreatedWithTimeout throw CreateRoomFailure.CreatedWithTimeout(roomId)
} }
Realm.getInstance(realmConfiguration).executeTransactionAsync { Realm.getInstance(realmConfiguration).executeTransactionAsync {

View file

@ -73,7 +73,7 @@ internal class DefaultStateService @AssistedInject constructor(@Assisted private
eventType = eventType, eventType = eventType,
body = body.toSafeJson(eventType) body = body.toSafeJson(eventType)
) )
sendStateTask.execute(params) sendStateTask.executeRetry(params, 3)
} }
private fun JsonDict.toSafeJson(eventType: String): JsonDict { private fun JsonDict.toSafeJson(eventType: String): JsonDict {

View file

@ -0,0 +1,56 @@
/*
* 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.internal.session.space
import io.realm.RealmConfiguration
import kotlinx.coroutines.TimeoutCancellationException
import org.matrix.android.sdk.api.session.room.failure.CreateRoomFailure
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
import org.matrix.android.sdk.internal.database.awaitNotEmptyResult
import org.matrix.android.sdk.internal.database.model.SpaceSummaryEntity
import org.matrix.android.sdk.internal.database.model.SpaceSummaryEntityFields
import org.matrix.android.sdk.internal.di.SessionDatabase
import org.matrix.android.sdk.internal.session.room.create.CreateRoomTask
import org.matrix.android.sdk.internal.task.Task
import java.util.concurrent.TimeUnit
import javax.inject.Inject
/**
* A simple wrapper of create room task that adds waiting for DB entities of spaces
*/
internal interface CreateSpaceTask : Task<CreateRoomParams, String>
internal class DefaultCreateSpaceTask @Inject constructor(
private val createRoomTask: CreateRoomTask,
@SessionDatabase private val realmConfiguration: RealmConfiguration
) : CreateSpaceTask {
override suspend fun execute(params: CreateRoomParams): String {
val spaceId = createRoomTask.execute(params)
try {
awaitNotEmptyResult(realmConfiguration, TimeUnit.MINUTES.toMillis(1L)) { realm ->
realm.where(SpaceSummaryEntity::class.java)
.equalTo(SpaceSummaryEntityFields.SPACE_ID, spaceId)
}
} catch (exception: TimeoutCancellationException) {
throw CreateRoomFailure.CreatedWithTimeout(spaceId)
}
return spaceId
}
}

View file

@ -22,15 +22,19 @@ import org.matrix.android.sdk.api.session.events.model.toContent
import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.room.Room import org.matrix.android.sdk.api.session.room.Room
import org.matrix.android.sdk.api.session.space.Space import org.matrix.android.sdk.api.session.space.Space
import org.matrix.android.sdk.api.session.space.SpaceSummary
import org.matrix.android.sdk.api.session.space.model.SpaceChildContent import org.matrix.android.sdk.api.session.space.model.SpaceChildContent
import java.lang.IllegalArgumentException
class DefaultSpace(private val room: Room) : Space { internal class DefaultSpace(private val room: Room, private val spaceSummaryDataSource: SpaceSummaryDataSource) : Space {
override fun asRoom(): Room { override fun asRoom(): Room {
return room return room
} }
override fun spaceSummary(): SpaceSummary? {
return spaceSummaryDataSource.getSpaceSummary(asRoom().roomId)
}
override suspend fun addChildren(roomId: String, viaServers: List<String>, order: String?, autoJoin: Boolean) { override suspend fun addChildren(roomId: String, viaServers: List<String>, order: String?, autoJoin: Boolean) {
asRoom().sendStateEvent( asRoom().sendStateEvent(
eventType = EventType.STATE_SPACE_CHILD, eventType = EventType.STATE_SPACE_CHILD,

View file

@ -22,7 +22,6 @@ import com.zhuinden.monarchy.Monarchy
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.events.model.toModel import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.RoomSummary
import org.matrix.android.sdk.api.session.room.model.RoomType
import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo import org.matrix.android.sdk.api.session.room.model.SpaceChildInfo
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomPreset import org.matrix.android.sdk.api.session.room.model.create.CreateRoomPreset
import org.matrix.android.sdk.api.session.space.CreateSpaceParams import org.matrix.android.sdk.api.session.space.CreateSpaceParams
@ -32,40 +31,33 @@ import org.matrix.android.sdk.api.session.space.SpaceSummary
import org.matrix.android.sdk.api.session.space.SpaceSummaryQueryParams import org.matrix.android.sdk.api.session.space.SpaceSummaryQueryParams
import org.matrix.android.sdk.api.session.space.model.SpaceChildContent import org.matrix.android.sdk.api.session.space.model.SpaceChildContent
import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.di.SessionDatabase
import org.matrix.android.sdk.internal.session.room.RoomGetter import org.matrix.android.sdk.internal.session.room.SpaceGetter
import org.matrix.android.sdk.internal.session.room.alias.DeleteRoomAliasTask
import org.matrix.android.sdk.internal.session.room.alias.GetRoomIdByAliasTask
import org.matrix.android.sdk.internal.session.room.create.CreateRoomTask
import org.matrix.android.sdk.internal.session.room.membership.RoomChangeMembershipStateDataSource
import org.matrix.android.sdk.internal.session.room.membership.joining.JoinRoomTask
import org.matrix.android.sdk.internal.session.room.membership.leaving.LeaveRoomTask import org.matrix.android.sdk.internal.session.room.membership.leaving.LeaveRoomTask
import org.matrix.android.sdk.internal.session.room.read.MarkAllRoomsReadTask
import org.matrix.android.sdk.internal.session.space.peeking.PeekSpaceTask import org.matrix.android.sdk.internal.session.space.peeking.PeekSpaceTask
import org.matrix.android.sdk.internal.session.space.peeking.SpacePeekResult import org.matrix.android.sdk.internal.session.space.peeking.SpacePeekResult
import org.matrix.android.sdk.internal.session.user.accountdata.UpdateBreadcrumbsTask
import org.matrix.android.sdk.internal.task.TaskExecutor
import javax.inject.Inject import javax.inject.Inject
internal class DefaultSpaceService @Inject constructor( internal class DefaultSpaceService @Inject constructor(
@SessionDatabase private val monarchy: Monarchy, @SessionDatabase private val monarchy: Monarchy,
private val createRoomTask: CreateRoomTask, private val createSpaceTask: CreateSpaceTask,
private val joinRoomTask: JoinRoomTask, // private val joinRoomTask: JoinRoomTask,
private val joinSpaceTask: JoinSpaceTask, private val joinSpaceTask: JoinSpaceTask,
private val markAllRoomsReadTask: MarkAllRoomsReadTask, private val spaceGetter: SpaceGetter,
private val updateBreadcrumbsTask: UpdateBreadcrumbsTask, // private val markAllRoomsReadTask: MarkAllRoomsReadTask,
private val roomIdByAliasTask: GetRoomIdByAliasTask, // private val updateBreadcrumbsTask: UpdateBreadcrumbsTask,
private val deleteRoomAliasTask: DeleteRoomAliasTask, // private val roomIdByAliasTask: GetRoomIdByAliasTask,
private val roomGetter: RoomGetter, // private val deleteRoomAliasTask: DeleteRoomAliasTask,
// private val roomGetter: RoomGetter,
private val spaceSummaryDataSource: SpaceSummaryDataSource, private val spaceSummaryDataSource: SpaceSummaryDataSource,
private val peekSpaceTask: PeekSpaceTask, private val peekSpaceTask: PeekSpaceTask,
private val resolveSpaceInfoTask: ResolveSpaceInfoTask, private val resolveSpaceInfoTask: ResolveSpaceInfoTask,
private val leaveRoomTask: LeaveRoomTask, private val leaveRoomTask: LeaveRoomTask
private val roomChangeMembershipStateDataSource: RoomChangeMembershipStateDataSource, // private val roomChangeMembershipStateDataSource: RoomChangeMembershipStateDataSource,
private val taskExecutor: TaskExecutor // private val taskExecutor: TaskExecutor
) : SpaceService { ) : SpaceService {
override suspend fun createSpace(params: CreateSpaceParams): String { override suspend fun createSpace(params: CreateSpaceParams): String {
return createRoomTask.execute(params) return createSpaceTask.executeRetry(params, 3)
} }
override suspend fun createSpace(name: String, topic: String?, avatarUri: Uri?, isPublic: Boolean): String { override suspend fun createSpace(name: String, topic: String?, avatarUri: Uri?, isPublic: Boolean): String {
@ -78,9 +70,7 @@ internal class DefaultSpaceService @Inject constructor(
} }
override fun getSpace(spaceId: String): Space? { override fun getSpace(spaceId: String): Space? {
return roomGetter.getRoom(spaceId) return spaceGetter.get(spaceId)
?.takeIf { it.roomSummary()?.roomType == RoomType.SPACE }
?.let { DefaultSpace(it) }
} }
override fun getSpaceSummariesLive(queryParams: SpaceSummaryQueryParams): LiveData<List<SpaceSummary>> { override fun getSpaceSummariesLive(queryParams: SpaceSummaryQueryParams): LiveData<List<SpaceSummary>> {

View file

@ -20,6 +20,8 @@ import dagger.Binds
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import org.matrix.android.sdk.internal.session.SessionScope import org.matrix.android.sdk.internal.session.SessionScope
import org.matrix.android.sdk.internal.session.room.DefaultSpaceGetter
import org.matrix.android.sdk.internal.session.room.SpaceGetter
import org.matrix.android.sdk.internal.session.space.peeking.DefaultPeekSpaceTask import org.matrix.android.sdk.internal.session.space.peeking.DefaultPeekSpaceTask
import org.matrix.android.sdk.internal.session.space.peeking.PeekSpaceTask import org.matrix.android.sdk.internal.session.space.peeking.PeekSpaceTask
import retrofit2.Retrofit import retrofit2.Retrofit
@ -45,4 +47,10 @@ internal abstract class SpaceModule {
@Binds @Binds
abstract fun bindJoinSpaceTask(task: DefaultJoinSpaceTask): JoinSpaceTask abstract fun bindJoinSpaceTask(task: DefaultJoinSpaceTask): JoinSpaceTask
@Binds
abstract fun bindSpaceGetter(getter: DefaultSpaceGetter): SpaceGetter
@Binds
abstract fun bindCreateSpaceTask(getter: DefaultCreateSpaceTask): CreateSpaceTask
} }

View file

@ -18,6 +18,7 @@ package im.vector.app.features.form
import android.text.Editable import android.text.Editable
import android.view.View import android.view.View
import android.view.inputmethod.EditorInfo
import androidx.core.view.isVisible import androidx.core.view.isVisible
import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass import com.airbnb.epoxy.EpoxyModelClass
@ -50,6 +51,12 @@ abstract class FormEditTextItem : VectorEpoxyModel<FormEditTextItem.Holder>() {
@EpoxyAttribute @EpoxyAttribute
var inputType: Int? = null var inputType: Int? = null
@EpoxyAttribute
var singleLine: Boolean? = null
@EpoxyAttribute
var imeOptions: Int? = null
@EpoxyAttribute @EpoxyAttribute
var onTextChange: ((String) -> Unit)? = null var onTextChange: ((String) -> Unit)? = null
@ -69,6 +76,8 @@ abstract class FormEditTextItem : VectorEpoxyModel<FormEditTextItem.Holder>() {
holder.textInputEditText.setTextSafe(value) holder.textInputEditText.setTextSafe(value)
holder.textInputEditText.isEnabled = enabled holder.textInputEditText.isEnabled = enabled
inputType?.let { holder.textInputEditText.inputType = it } inputType?.let { holder.textInputEditText.inputType = it }
holder.textInputEditText.isSingleLine = singleLine ?: false
holder.textInputEditText.imeOptions = imeOptions ?: EditorInfo.IME_ACTION_NONE
holder.textInputEditText.addTextChangedListener(onTextChangeListener) holder.textInputEditText.addTextChangedListener(onTextChangeListener)
holder.bottomSeparator.isVisible = showBottomSeparator holder.bottomSeparator.isVisible = showBottomSeparator

View file

@ -16,13 +16,16 @@
package im.vector.app.features.form package im.vector.app.features.form
import android.net.Uri import android.net.Uri
import android.util.TypedValue
import android.view.View import android.view.View
import android.widget.ImageView import android.widget.ImageView
import androidx.core.view.isVisible import androidx.core.view.isVisible
import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass import com.airbnb.epoxy.EpoxyModelClass
import com.airbnb.epoxy.EpoxyModelWithHolder import com.airbnb.epoxy.EpoxyModelWithHolder
import com.bumptech.glide.request.RequestOptions import com.bumptech.glide.load.MultiTransformation
import com.bumptech.glide.load.resource.bitmap.CenterCrop
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.epoxy.ClickListener import im.vector.app.core.epoxy.ClickListener
import im.vector.app.core.epoxy.VectorEpoxyHolder import im.vector.app.core.epoxy.VectorEpoxyHolder
@ -55,13 +58,24 @@ abstract class FormEditableSquareAvatarItem : EpoxyModelWithHolder<FormEditableS
override fun bind(holder: Holder) { override fun bind(holder: Holder) {
super.bind(holder) super.bind(holder)
holder.imageContainer.onClick(clickListener?.takeIf { enabled }) holder.imageContainer.onClick(clickListener?.takeIf { enabled })
if (matrixItem != null) { when {
avatarRenderer?.renderSpace(matrixItem!!, holder.image) imageUri != null -> {
} else { val corner = TypedValue.applyDimension(
GlideApp.with(holder.image) TypedValue.COMPLEX_UNIT_DIP,
.load(imageUri) 8f,
.apply(RequestOptions.circleCropTransform()) holder.view.resources.displayMetrics
.into(holder.image) ).toInt()
GlideApp.with(holder.image)
.load(imageUri)
.transform(MultiTransformation(CenterCrop(), RoundedCorners(corner)))
.into(holder.image)
}
matrixItem != null -> {
avatarRenderer?.renderSpace(matrixItem!!, holder.image)
}
else -> {
avatarRenderer?.clear(holder.image)
}
} }
holder.delete.isVisible = enabled && (imageUri != null || matrixItem?.avatarUrl?.isNotEmpty() == true) holder.delete.isVisible = enabled && (imageUri != null || matrixItem?.avatarUrl?.isNotEmpty() == true)
holder.delete.onClick(deleteListener?.takeIf { enabled }) holder.delete.onClick(deleteListener?.takeIf { enabled })
@ -72,6 +86,7 @@ abstract class FormEditableSquareAvatarItem : EpoxyModelWithHolder<FormEditableS
GlideApp.with(holder.image).clear(holder.image) GlideApp.with(holder.image).clear(holder.image)
super.unbind(holder) super.unbind(holder)
} }
class Holder : VectorEpoxyHolder() { class Holder : VectorEpoxyHolder() {
val imageContainer by bind<View>(R.id.itemEditableAvatarImageContainer) val imageContainer by bind<View>(R.id.itemEditableAvatarImageContainer)
val image by bind<ImageView>(R.id.itemEditableAvatarImage) val image by bind<ImageView>(R.id.itemEditableAvatarImage)

View file

@ -35,6 +35,10 @@ abstract class HomeSpaceSummaryItem : VectorEpoxyModel<HomeSpaceSummaryItem.Hold
@EpoxyAttribute var selected: Boolean = false @EpoxyAttribute var selected: Boolean = false
@EpoxyAttribute var listener: (() -> Unit)? = null @EpoxyAttribute var listener: (() -> Unit)? = null
override fun getViewType(): Int {
// mm.. it's reusing the same layout for basic space item
return R.id.space_item_home
}
override fun bind(holder: Holder) { override fun bind(holder: Holder) {
super.bind(holder) super.bind(holder)
holder.rootView.setOnClickListener { listener?.invoke() } holder.rootView.setOnClickListener { listener?.invoke() }

View file

@ -16,6 +16,7 @@
package im.vector.app.features.home package im.vector.app.features.home
import android.app.Activity
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
@ -36,6 +37,7 @@ import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.di.ScreenComponent import im.vector.app.core.di.ScreenComponent
import im.vector.app.core.extensions.exhaustive import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.extensions.hideKeyboard import im.vector.app.core.extensions.hideKeyboard
import im.vector.app.core.extensions.registerStartForActivityResult
import im.vector.app.core.extensions.replaceFragment import im.vector.app.core.extensions.replaceFragment
import im.vector.app.core.platform.ToolbarConfigurable import im.vector.app.core.platform.ToolbarConfigurable
import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.platform.VectorBaseActivity
@ -103,6 +105,23 @@ class HomeActivity :
@Inject lateinit var avatarRenderer: AvatarRenderer @Inject lateinit var avatarRenderer: AvatarRenderer
@Inject lateinit var initSyncStepFormatter: InitSyncStepFormatter @Inject lateinit var initSyncStepFormatter: InitSyncStepFormatter
private val createSpaceResultLauncher = registerStartForActivityResult { activityResult ->
if (activityResult.resultCode == Activity.RESULT_OK) {
val spaceId = activityResult.data?.extras?.getString(SpaceCreationActivity.RESULT_DATA_CREATED_SPACE_ID)
val defaultRoomsId = activityResult.data?.extras?.getString(SpaceCreationActivity.RESULT_DATA_DEFAULT_ROOM_ID)
views.drawerLayout.closeDrawer(GravityCompat.START)
// Here we want to change current space to the newly created one, and then immediatly open the default room
if (spaceId != null) {
navigator.switchToSpace(this, spaceId, defaultRoomsId)
}
// Also we should show the share space bottomsheet
} else {
// viewModel.handle(CrossSigningSettingsAction.ReAuthCancelled)
}
}
private val drawerListener = object : DrawerLayout.SimpleDrawerListener() { private val drawerListener = object : DrawerLayout.SimpleDrawerListener() {
override fun onDrawerStateChanged(newState: Int) { override fun onDrawerStateChanged(newState: Int) {
hideKeyboard() hideKeyboard()
@ -147,7 +166,7 @@ class HomeActivity :
startActivity(SpacePreviewActivity.newIntent(this, sharedAction.spaceId)) startActivity(SpacePreviewActivity.newIntent(this, sharedAction.spaceId))
} }
is HomeActivitySharedAction.AddSpace -> { is HomeActivitySharedAction.AddSpace -> {
startActivity(SpaceCreationActivity.newIntent(this)) createSpaceResultLauncher.launch(SpaceCreationActivity.newIntent(this))
} }
}.exhaustive }.exhaustive
} }

View file

@ -29,6 +29,7 @@ import androidx.core.app.ActivityOptionsCompat
import androidx.core.app.TaskStackBuilder import androidx.core.app.TaskStackBuilder
import androidx.core.util.Pair import androidx.core.util.Pair
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import arrow.core.Option
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.error.fatalError import im.vector.app.core.error.fatalError
@ -46,6 +47,7 @@ import im.vector.app.features.crypto.verification.SupportedVerificationMethodsPr
import im.vector.app.features.crypto.verification.VerificationBottomSheet import im.vector.app.features.crypto.verification.VerificationBottomSheet
import im.vector.app.features.debug.DebugMenuActivity import im.vector.app.features.debug.DebugMenuActivity
import im.vector.app.features.devtools.RoomDevToolActivity import im.vector.app.features.devtools.RoomDevToolActivity
import im.vector.app.features.grouplist.SelectedSpaceDataSource
import im.vector.app.features.home.room.detail.RoomDetailActivity import im.vector.app.features.home.room.detail.RoomDetailActivity
import im.vector.app.features.home.room.detail.RoomDetailArgs import im.vector.app.features.home.room.detail.RoomDetailArgs
import im.vector.app.features.home.room.detail.search.SearchActivity import im.vector.app.features.home.room.detail.search.SearchActivity
@ -77,6 +79,7 @@ import org.matrix.android.sdk.api.session.room.model.thirdparty.RoomDirectoryDat
import org.matrix.android.sdk.api.session.terms.TermsService import org.matrix.android.sdk.api.session.terms.TermsService
import org.matrix.android.sdk.api.session.widgets.model.Widget import org.matrix.android.sdk.api.session.widgets.model.Widget
import org.matrix.android.sdk.api.session.widgets.model.WidgetType import org.matrix.android.sdk.api.session.widgets.model.WidgetType
import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -85,6 +88,7 @@ class DefaultNavigator @Inject constructor(
private val sessionHolder: ActiveSessionHolder, private val sessionHolder: ActiveSessionHolder,
private val vectorPreferences: VectorPreferences, private val vectorPreferences: VectorPreferences,
private val widgetArgsBuilder: WidgetArgsBuilder, private val widgetArgsBuilder: WidgetArgsBuilder,
private val selectedSpaceDataSource: SelectedSpaceDataSource,
private val supportedVerificationMethodsProvider: SupportedVerificationMethodsProvider private val supportedVerificationMethodsProvider: SupportedVerificationMethodsProvider
) : Navigator { ) : Navigator {
@ -98,6 +102,23 @@ class DefaultNavigator @Inject constructor(
startActivity(context, intent, buildTask) startActivity(context, intent, buildTask)
} }
override fun switchToSpace(context: Context, spaceId: String, roomId: String?) {
if (sessionHolder.getSafeActiveSession()?.spaceService()?.getSpace(spaceId) == null) {
fatalError("Trying to open an unknown space $spaceId", vectorPreferences.failFast())
return
}
sessionHolder.getSafeActiveSession()?.spaceService()?.getSpace(spaceId)?.spaceSummary()?.let {
Timber.d("## Nav: Switching to space $spaceId / ${it.roomSummary.name}")
selectedSpaceDataSource.post(Option.just(it))
} ?: kotlin.run {
Timber.d("## Nav: Failed to switch to space $spaceId")
}
if (roomId != null) {
openRoom(context, roomId)
}
}
override fun performDeviceVerification(context: Context, otherUserId: String, sasTransactionId: String) { override fun performDeviceVerification(context: Context, otherUserId: String, sasTransactionId: String) {
val session = sessionHolder.getSafeActiveSession() ?: return val session = sessionHolder.getSafeActiveSession() ?: return
val tx = session.cryptoService().verificationService().getExistingTransaction(otherUserId, sasTransactionId) val tx = session.cryptoService().verificationService().getExistingTransaction(otherUserId, sasTransactionId)

View file

@ -38,6 +38,8 @@ interface Navigator {
fun openRoom(context: Context, roomId: String, eventId: String? = null, buildTask: Boolean = false) fun openRoom(context: Context, roomId: String, eventId: String? = null, buildTask: Boolean = false)
fun switchToSpace(context: Context, spaceId: String, roomId: String?)
fun performDeviceVerification(context: Context, otherUserId: String, sasTransactionId: String) fun performDeviceVerification(context: Context, otherUserId: String, sasTransactionId: String)
fun requestSessionVerification(context: Context, otherSessionId: String) fun requestSessionVerification(context: Context, otherSessionId: String)

View file

@ -20,7 +20,9 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.view.MenuItem import android.view.MenuItem
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.viewModel import com.airbnb.mvrx.viewModel
import com.airbnb.mvrx.withState import com.airbnb.mvrx.withState
import im.vector.app.R import im.vector.app.R
@ -91,6 +93,23 @@ class SpaceCreationActivity : SimpleFragmentActivity(), CreateSpaceViewModel.Fac
CreateSpaceEvents.NavigateToAddRooms -> { CreateSpaceEvents.NavigateToAddRooms -> {
navigateToFragment(CreateSpaceDefaultRoomsFragment::class.java) navigateToFragment(CreateSpaceDefaultRoomsFragment::class.java)
} }
is CreateSpaceEvents.ShowModalError -> {
hideWaitingView()
AlertDialog.Builder(this)
.setMessage(it.errorMessage)
.setPositiveButton(getString(R.string.ok), null)
.show()
}
is CreateSpaceEvents.FinishSuccess -> {
setResult(RESULT_OK, Intent().apply {
putExtra(RESULT_DATA_CREATED_SPACE_ID, it.spaceId)
putExtra(RESULT_DATA_DEFAULT_ROOM_ID, it.defaultRoomId)
})
finish()
}
CreateSpaceEvents.HideModalLoading -> {
hideWaitingView()
}
} }
} }
} }
@ -114,16 +133,24 @@ class SpaceCreationActivity : SimpleFragmentActivity(), CreateSpaceViewModel.Fac
val titleRes = when (state.step) { val titleRes = when (state.step) {
CreateSpaceState.Step.ChooseType -> R.string.activity_create_space_title CreateSpaceState.Step.ChooseType -> R.string.activity_create_space_title
CreateSpaceState.Step.SetDetails -> R.string.your_public_space CreateSpaceState.Step.SetDetails -> R.string.your_public_space
CreateSpaceState.Step.AddRooms -> R.string.your_public_space CreateSpaceState.Step.AddRooms -> R.string.your_public_space
} }
supportActionBar?.let { supportActionBar?.let {
it.title = getString(titleRes) it.title = getString(titleRes)
} ?: run { } ?: run {
setTitle(getString(titleRes)) setTitle(getString(titleRes))
} }
if (state.creationResult is Loading) {
showWaitingView(getString(R.string.create_spaces_loading_message))
}
} }
companion object { companion object {
const val RESULT_DATA_CREATED_SPACE_ID = "RESULT_DATA_CREATED_SPACE_ID"
const val RESULT_DATA_DEFAULT_ROOM_ID = "RESULT_DATA_DEFAULT_ROOM_ID"
fun newIntent(context: Context): Intent { fun newIntent(context: Context): Intent {
return Intent(context, SpaceCreationActivity::class.java).apply { return Intent(context, SpaceCreationActivity::class.java).apply {
// putExtra(MvRx.KEY_ARG, SpaceDirectoryArgs(spaceId)) // putExtra(MvRx.KEY_ARG, SpaceDirectoryArgs(spaceId))

View file

@ -86,8 +86,15 @@ class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: Sp
private var currentGroupId = "" private var currentGroupId = ""
init { init {
observeGroupSummaries() observeSpaceSummaries()
observeSelectionState() observeSelectionState()
selectedSpaceDataSource.observe().execute {
if (this.selectedSpace != it.invoke()?.orNull()) {
copy(
selectedSpace = it.invoke()?.orNull()
)
} else this
}
} }
private fun observeSelectionState() { private fun observeSelectionState() {
@ -143,8 +150,8 @@ class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: Sp
_viewEvents.post(SpaceListViewEvents.AddSpace) _viewEvents.post(SpaceListViewEvents.AddSpace)
} }
private fun observeGroupSummaries() { private fun observeSpaceSummaries() {
val roomSummaryQueryParams = roomSummaryQueryParams() { val spaceSummaryQueryParams = roomSummaryQueryParams() {
memberships = listOf(Membership.JOIN, Membership.INVITE) memberships = listOf(Membership.JOIN, Membership.INVITE)
displayName = QueryStringValue.IsNotEmpty displayName = QueryStringValue.IsNotEmpty
excludeType = listOf(/**RoomType.MESSAGING,$*/ excludeType = listOf(/**RoomType.MESSAGING,$*/
@ -171,7 +178,7 @@ class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: Sp
}, },
session session
.rx() .rx()
.liveSpaceSummaries(roomSummaryQueryParams), .liveSpaceSummaries(spaceSummaryQueryParams),
BiFunction { allCommunityGroup, communityGroups -> BiFunction { allCommunityGroup, communityGroups ->
listOf(allCommunityGroup) + communityGroups listOf(allCommunityGroup) + communityGroups
} }

View file

@ -43,7 +43,7 @@ class ChooseSpaceTypeFragment @Inject constructor(
})) }))
views.privateButton.setOnClickListener(DebouncedClickListener({ views.privateButton.setOnClickListener(DebouncedClickListener({
sharedViewModel.handle(CreateSpaceAction.SetRoomType(SpaceType.Private)) // sharedViewModel.handle(CreateSpaceAction.SetRoomType(SpaceType.Private))
})) }))
} }
} }

View file

@ -46,10 +46,14 @@ class CreateSpaceDefaultRoomsFragment @Inject constructor(
} }
views.nextButton.debouncedClicks { views.nextButton.debouncedClicks {
sharedViewModel.handle(CreateSpaceAction.NextFromDetails) sharedViewModel.handle(CreateSpaceAction.NextFromDefaultRooms)
} }
} }
override fun onNameChange(index: Int, newName: String) {
sharedViewModel.handle(CreateSpaceAction.DefaultRoomNameChanged(index, newName))
}
// ----------------------------- // -----------------------------
// Epoxy controller listener methods // Epoxy controller listener methods
// ----------------------------- // -----------------------------

View file

@ -16,25 +16,32 @@
package im.vector.app.features.spaces.create package im.vector.app.features.spaces.create
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import com.airbnb.mvrx.activityViewModel import com.airbnb.mvrx.activityViewModel
import im.vector.app.core.dialogs.GalleryOrCameraDialogHelper
import im.vector.app.core.extensions.configureWith import im.vector.app.core.extensions.configureWith
import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.resources.ColorProvider
import im.vector.app.databinding.FragmentSpaceCreateGenericEpoxyFormBinding import im.vector.app.databinding.FragmentSpaceCreateGenericEpoxyFormBinding
import javax.inject.Inject import javax.inject.Inject
class CreateSpaceDetailsFragment @Inject constructor( class CreateSpaceDetailsFragment @Inject constructor(
private val epoxyController: SpaceDetailEpoxyController private val epoxyController: SpaceDetailEpoxyController,
) : VectorBaseFragment<FragmentSpaceCreateGenericEpoxyFormBinding>(), SpaceDetailEpoxyController.Listener { private val colorProvider: ColorProvider
) : VectorBaseFragment<FragmentSpaceCreateGenericEpoxyFormBinding>(), SpaceDetailEpoxyController.Listener,
GalleryOrCameraDialogHelper.Listener {
private val sharedViewModel: CreateSpaceViewModel by activityViewModel() private val sharedViewModel: CreateSpaceViewModel by activityViewModel()
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?) = override fun getBinding(inflater: LayoutInflater, container: ViewGroup?) =
FragmentSpaceCreateGenericEpoxyFormBinding.inflate(layoutInflater, container, false) FragmentSpaceCreateGenericEpoxyFormBinding.inflate(layoutInflater, container, false)
private val galleryOrCameraDialogHelper = GalleryOrCameraDialogHelper(this, colorProvider)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
@ -50,14 +57,19 @@ class CreateSpaceDetailsFragment @Inject constructor(
} }
} }
override fun onImageReady(uri: Uri?) {
sharedViewModel.handle(CreateSpaceAction.SetAvatar(uri))
}
// ----------------------------- // -----------------------------
// Epoxy controller listener methods // Epoxy controller listener methods
// ----------------------------- // -----------------------------
override fun onAvatarDelete() { override fun onAvatarDelete() {
sharedViewModel.handle(CreateSpaceAction.SetAvatar(null))
} }
override fun onAvatarChange() { override fun onAvatarChange() {
galleryOrCameraDialogHelper.show()
} }
override fun onNameChange(newName: String) { override fun onNameChange(newName: String) {

View file

@ -17,20 +17,29 @@
package im.vector.app.features.spaces.create package im.vector.app.features.spaces.create
import android.net.Uri import android.net.Uri
import androidx.lifecycle.viewModelScope
import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.ActivityViewModelContext
import com.airbnb.mvrx.Async
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.ViewModelContext import com.airbnb.mvrx.ViewModelContext
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject import dagger.assisted.AssistedInject
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.error.ErrorFormatter
import im.vector.app.core.extensions.exhaustive import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewEvents import im.vector.app.core.platform.VectorViewEvents
import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.platform.VectorViewModelAction import im.vector.app.core.platform.VectorViewModelAction
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.Session
data class CreateSpaceState( data class CreateSpaceState(
@ -39,8 +48,9 @@ data class CreateSpaceState(
val topic: String = "", val topic: String = "",
val step: Step = Step.ChooseType, val step: Step = Step.ChooseType,
val spaceType: SpaceType? = null, val spaceType: SpaceType? = null,
val nameInlineError : String? = null, val nameInlineError: String? = null,
val defaultRooms: List<String>? = null val defaultRooms: Map<Int, String?>? = null,
val creationResult: Async<String> = Uninitialized
) : MvRxState { ) : MvRxState {
enum class Step { enum class Step {
@ -59,8 +69,11 @@ sealed class CreateSpaceAction : VectorViewModelAction {
data class SetRoomType(val type: SpaceType) : CreateSpaceAction() data class SetRoomType(val type: SpaceType) : CreateSpaceAction()
data class NameChanged(val name: String) : CreateSpaceAction() data class NameChanged(val name: String) : CreateSpaceAction()
data class TopicChanged(val topic: String) : CreateSpaceAction() data class TopicChanged(val topic: String) : CreateSpaceAction()
data class SetAvatar(val uri: Uri?) : CreateSpaceAction()
object OnBackPressed : CreateSpaceAction() object OnBackPressed : CreateSpaceAction()
object NextFromDetails : CreateSpaceAction() object NextFromDetails : CreateSpaceAction()
object NextFromDefaultRooms : CreateSpaceAction()
data class DefaultRoomNameChanged(val index: Int, val name: String) : CreateSpaceAction()
} }
sealed class CreateSpaceEvents : VectorViewEvents { sealed class CreateSpaceEvents : VectorViewEvents {
@ -68,12 +81,17 @@ sealed class CreateSpaceEvents : VectorViewEvents {
object NavigateToChooseType : CreateSpaceEvents() object NavigateToChooseType : CreateSpaceEvents()
object NavigateToAddRooms : CreateSpaceEvents() object NavigateToAddRooms : CreateSpaceEvents()
object Dismiss : CreateSpaceEvents() object Dismiss : CreateSpaceEvents()
data class FinishSuccess(val spaceId: String, val defaultRoomId: String?) : CreateSpaceEvents()
data class ShowModalError(val errorMessage: String) : CreateSpaceEvents()
object HideModalLoading : CreateSpaceEvents()
} }
class CreateSpaceViewModel @AssistedInject constructor( class CreateSpaceViewModel @AssistedInject constructor(
@Assisted initialState: CreateSpaceState, @Assisted initialState: CreateSpaceState,
private val session: Session, private val session: Session,
private val stringProvider: StringProvider private val stringProvider: StringProvider,
private val createSpaceViewModelTask: CreateSpaceViewModelTask,
private val errorFormatter: ErrorFormatter
) : VectorViewModel<CreateSpaceState, CreateSpaceAction, CreateSpaceEvents>(initialState) { ) : VectorViewModel<CreateSpaceState, CreateSpaceAction, CreateSpaceEvents>(initialState) {
@AssistedFactory @AssistedFactory
@ -90,6 +108,12 @@ class CreateSpaceViewModel @AssistedInject constructor(
} }
return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface") return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface")
} }
override fun initialState(viewModelContext: ViewModelContext): CreateSpaceState? {
return CreateSpaceState(
defaultRooms = mapOf(0 to viewModelContext.activity.getString(R.string.create_spaces_default_public_room_name))
)
}
} }
override fun handle(action: CreateSpaceAction) { override fun handle(action: CreateSpaceAction) {
@ -124,6 +148,21 @@ class CreateSpaceViewModel @AssistedInject constructor(
CreateSpaceAction.NextFromDetails -> { CreateSpaceAction.NextFromDetails -> {
handleNextFromDetails() handleNextFromDetails()
} }
CreateSpaceAction.NextFromDefaultRooms -> {
handleNextFromDefaultRooms()
}
is CreateSpaceAction.DefaultRoomNameChanged -> {
setState {
copy(
defaultRooms = (defaultRooms ?: emptyMap()).toMutableMap().apply {
this[action.index] = action.name
}
)
}
}
is CreateSpaceAction.SetAvatar -> {
setState { copy(avatarUri = action.uri) }
}
}.exhaustive }.exhaustive
} }
@ -167,4 +206,53 @@ class CreateSpaceViewModel @AssistedInject constructor(
_viewEvents.post(CreateSpaceEvents.NavigateToAddRooms) _viewEvents.post(CreateSpaceEvents.NavigateToAddRooms)
} }
} }
private fun handleNextFromDefaultRooms() = withState { state ->
val spaceName = state.name ?: return@withState
setState {
copy(creationResult = Loading())
}
viewModelScope.launch(Dispatchers.IO) {
try {
val result = createSpaceViewModelTask.execute(
CreateSpaceTaskParams(
spaceName = spaceName,
spaceTopic = state.topic,
spaceAvatar = state.avatarUri,
isPublic = state.spaceType == SpaceType.Public,
defaultRooms = state.defaultRooms
?.entries
?.sortedBy { it.key }
?.mapNotNull { it.value } ?: emptyList()
)
)
when (result) {
is CreateSpaceTaskResult.Success -> {
setState {
copy(creationResult = Success(result.spaceId))
}
_viewEvents.post(CreateSpaceEvents.FinishSuccess(result.spaceId, result.childIds.firstOrNull()))
}
is CreateSpaceTaskResult.PartialSuccess -> {
// XXX what can we do here?
setState {
copy(creationResult = Success(result.spaceId))
}
_viewEvents.post(CreateSpaceEvents.FinishSuccess(result.spaceId, result.childIds.firstOrNull()))
}
is CreateSpaceTaskResult.FailedToCreateSpace -> {
setState {
copy(creationResult = Fail(result.failure))
}
_viewEvents.post(CreateSpaceEvents.ShowModalError(errorFormatter.toHumanReadable(result.failure)))
}
}
} catch (failure: Throwable) {
setState {
copy(creationResult = Fail(failure))
}
_viewEvents.post(CreateSpaceEvents.ShowModalError(errorFormatter.toHumanReadable(failure)))
}
}
}
} }

View file

@ -0,0 +1,95 @@
/*
* 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.net.Uri
import im.vector.app.core.platform.ViewModelTask
import im.vector.app.core.resources.StringProvider
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.room.failure.CreateRoomFailure
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams
import org.matrix.android.sdk.api.session.room.model.create.CreateRoomPreset
import org.matrix.android.sdk.internal.util.awaitCallback
import timber.log.Timber
import javax.inject.Inject
sealed class CreateSpaceTaskResult {
data class Success(val spaceId: String, val childIds: List<String>) : CreateSpaceTaskResult()
data class PartialSuccess(val spaceId: String, val childIds: List<String>, val failedRooms: Map<String, Throwable>) : CreateSpaceTaskResult()
class FailedToCreateSpace(val failure: Throwable) : CreateSpaceTaskResult()
}
data class CreateSpaceTaskParams(
val spaceName: String,
val spaceTopic: String?,
val spaceAvatar: Uri? = null,
val isPublic: Boolean,
val defaultRooms: List<String> = emptyList()
)
class CreateSpaceViewModelTask @Inject constructor(
private val session: Session,
private val stringProvider: StringProvider
) : ViewModelTask<CreateSpaceTaskParams, CreateSpaceTaskResult> {
override suspend fun execute(params: CreateSpaceTaskParams): CreateSpaceTaskResult {
val spaceID = try {
session.spaceService().createSpace(params.spaceName, params.spaceTopic, params.spaceAvatar, params.isPublic)
} catch (failure: Throwable) {
return CreateSpaceTaskResult.FailedToCreateSpace(failure)
}
val createdSpace = session.spaceService().getSpace(spaceID)
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> {
session.createRoom(CreateRoomParams().apply {
this.name = roomName
this.preset = CreateRoomPreset.PRESET_PUBLIC_CHAT
}, it)
}
} catch (timeout: CreateRoomFailure.CreatedWithTimeout) {
// we ignore that?
timeout.roomID
}
val via = session.sessionParams.homeServerHost?.let { listOf(it) } ?: emptyList()
createdSpace!!.addChildren(roomId, via, null, true)
childIds.add(roomId)
} catch (failure: Throwable) {
Timber.d("Failed to create child room in $spaceID")
childErrors[roomName] = failure
}
}
}
return if (childErrors.isEmpty()) {
CreateSpaceTaskResult.Success(spaceID, childIds)
} else {
CreateSpaceTaskResult.PartialSuccess(spaceID, childIds, childErrors)
}
}
}

View file

@ -28,7 +28,6 @@ import javax.inject.Inject
class SpaceDefaultRoomEpoxyController @Inject constructor( class SpaceDefaultRoomEpoxyController @Inject constructor(
private val stringProvider: StringProvider, private val stringProvider: StringProvider,
private val colorProvider: ColorProvider private val colorProvider: ColorProvider
// private val avatarRenderer: AvatarRenderer
) : TypedEpoxyController<CreateSpaceState>() { ) : TypedEpoxyController<CreateSpaceState>() {
var listener: Listener? = null var listener: Listener? = null
@ -50,44 +49,41 @@ class SpaceDefaultRoomEpoxyController @Inject constructor(
formEditTextItem { formEditTextItem {
id("roomName1") id("roomName1")
enabled(true) enabled(true)
value(data?.name) value(data?.defaultRooms?.get(0))
hint(stringProvider.getString(R.string.create_room_name_hint)) hint(stringProvider.getString(R.string.create_room_name_section))
showBottomSeparator(false) showBottomSeparator(false)
// errorMessage(data?.nameInlineError)
onTextChange { text -> onTextChange { text ->
// listener?.onNameChange(text) listener?.onNameChange(0, text)
} }
} }
formEditTextItem { formEditTextItem {
id("roomName2") id("roomName2")
enabled(true) enabled(true)
// value(data?.name) value(data?.defaultRooms?.get(1))
hint(stringProvider.getString(R.string.create_room_name_hint)) hint(stringProvider.getString(R.string.create_room_name_section))
showBottomSeparator(false) showBottomSeparator(false)
// errorMessage(data?.nameInlineError)
onTextChange { text -> onTextChange { text ->
// listener?.onNameChange(text) listener?.onNameChange(1, text)
} }
} }
formEditTextItem { formEditTextItem {
id("roomName3") id("roomName3")
enabled(true) enabled(true)
// value(data?.name) value(data?.defaultRooms?.get(2))
hint(stringProvider.getString(R.string.create_room_name_hint)) hint(stringProvider.getString(R.string.create_room_name_section))
showBottomSeparator(false) showBottomSeparator(false)
// errorMessage(data?.nameInlineError)
onTextChange { text -> onTextChange { text ->
// listener?.onNameChange(text) listener?.onNameChange(2, text)
} }
} }
} }
interface Listener { interface Listener {
// fun onAvatarDelete() // fun onAvatarDelete()
// fun onAvatarChange() // fun onAvatarChange()
// fun onNameChange(newName: String) fun onNameChange(index: Int, newName: String)
// fun onTopicChange(newTopic: String) // fun onTopicChange(newTopic: String)
} }
} }

View file

@ -18,6 +18,7 @@ package im.vector.app.features.spaces.create
import android.content.Context import android.content.Context
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.os.Build
import android.util.AttributeSet import android.util.AttributeSet
import android.util.TypedValue import android.util.TypedValue
import androidx.appcompat.content.res.AppCompatResources.getDrawable import androidx.appcompat.content.res.AppCompatResources.getDrawable
@ -85,7 +86,7 @@ class WizardButtonView @JvmOverloads constructor(context: Context, attrs: Attrib
val outValue = TypedValue() val outValue = TypedValue()
context.theme.resolveAttribute(android.R.attr.selectableItemBackground, outValue, true) context.theme.resolveAttribute(android.R.attr.selectableItemBackground, outValue, true)
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
this.foreground = getDrawable(context, outValue.resourceId) this.foreground = getDrawable(context, outValue.resourceId)
} }

View file

@ -0,0 +1,3 @@
<resources>
<item type="id" name="space_item_home" />
</resources>

View file

@ -3275,6 +3275,8 @@
<string name="create_space_error_empty_field_space_name">Give it a name to continue.</string> <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 Runners World?</string> <string name="create_spaces_room_public_header">What are some discussions you want to have in Runners World?</string>
<string name="create_spaces_room_public_header_desc">Well create rooms for them, and auto-join everyone. You can add more later too.</string> <string name="create_spaces_room_public_header_desc">Well create rooms for them, and auto-join everyone. You can add more later too.</string>
<string name="create_spaces_default_public_room_name">General</string>
<string name="create_spaces_loading_message">Creating Space…</string>
</resources> </resources>