From 7d2d7b411e0efc7069932cafe4b7a1549d08dd2b Mon Sep 17 00:00:00 2001 From: Valere Date: Thu, 25 Feb 2021 00:23:53 +0100 Subject: [PATCH] Space Create Wizard Flow --- .../session/room/failure/CreateRoomFailure.kt | 2 +- .../android/sdk/api/session/space/Space.kt | 6 ++ .../session/room/DefaultRoomService.kt | 9 +- .../sdk/internal/session/room/SpaceGetter.kt | 39 ++++++++ .../session/room/create/CreateRoomTask.kt | 2 +- .../session/room/state/DefaultStateService.kt | 2 +- .../internal/session/space/CreateSpaceTask.kt | 56 +++++++++++ .../internal/session/space/DefaultSpace.kt | 8 +- .../session/space/DefaultSpaceService.kt | 38 +++----- .../sdk/internal/session/space/SpaceModule.kt | 8 ++ .../app/features/form/FormEditTextItem.kt | 9 ++ .../form/FormEditableSquareAvatarItem.kt | 31 ++++-- .../grouplist/HomeSpaceSummaryItem.kt | 4 + .../vector/app/features/home/HomeActivity.kt | 21 +++- .../features/navigation/DefaultNavigator.kt | 21 ++++ .../app/features/navigation/Navigator.kt | 2 + .../features/spaces/SpaceCreationActivity.kt | 29 +++++- .../features/spaces/SpacesListViewModel.kt | 15 ++- .../spaces/create/ChooseSpaceTypeFragment.kt | 2 +- .../create/CreateSpaceDefaultRoomsFragment.kt | 6 +- .../create/CreateSpaceDetailsFragment.kt | 16 +++- .../spaces/create/CreateSpaceViewModel.kt | 94 +++++++++++++++++- .../spaces/create/CreateSpaceViewModelTask.kt | 95 +++++++++++++++++++ .../create/SpaceDefaultRoomEpoxyController.kt | 26 +++-- .../spaces/create/WizardButtonView.kt | 3 +- vector/src/main/res/values/ids.xml | 3 + vector/src/main/res/values/strings.xml | 2 + 27 files changed, 481 insertions(+), 68 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/SpaceGetter.kt create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/CreateSpaceTask.kt create mode 100644 vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModelTask.kt create mode 100644 vector/src/main/res/values/ids.xml diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/failure/CreateRoomFailure.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/failure/CreateRoomFailure.kt index 208cdd4556..deab0ca3e7 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/failure/CreateRoomFailure.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/failure/CreateRoomFailure.kt @@ -21,7 +21,7 @@ import org.matrix.android.sdk.api.failure.MatrixError import org.matrix.android.sdk.api.session.room.alias.RoomAliasError sealed class CreateRoomFailure : Failure.FeatureFailure() { - object CreatedWithTimeout : CreateRoomFailure() + data class CreatedWithTimeout(val roomID: String) : CreateRoomFailure() data class CreatedWithFederationFailure(val matrixError: MatrixError) : CreateRoomFailure() data class AliasError(val aliasError: RoomAliasError) : CreateRoomFailure() } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/Space.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/Space.kt index 01a0dbc929..0172b3701b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/Space.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/Space.kt @@ -17,11 +17,17 @@ 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.model.RoomSummary interface Space { fun asRoom() : Room + /** + * A current snapshot of [RoomSummary] associated with the room + */ + fun spaceSummary(): SpaceSummary? + suspend fun addChildren(roomId: String, viaServers: List, order: String?, autoJoin: Boolean = false) suspend fun removeRoom(roomId: String) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt index 22f61bc517..4724167e87 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt @@ -66,8 +66,13 @@ internal class DefaultRoomService @Inject constructor( private val roomChangeMembershipStateDataSource: RoomChangeMembershipStateDataSource ) : RoomService { - override suspend fun createRoom(createRoomParams: CreateRoomParams): String { - return createRoomTask.execute(createRoomParams) + override fun createRoom(createRoomParams: CreateRoomParams, callback: MatrixCallback): Cancelable { + return createRoomTask + .configureWith(createRoomParams) { + this.callback = callback + this.retryCount = 3 + } + .executeBy(taskExecutor) } override fun getRoom(roomId: String): Room? { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/SpaceGetter.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/SpaceGetter.kt new file mode 100644 index 0000000000..f440a67710 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/SpaceGetter.kt @@ -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) } + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomTask.kt index bafe2b90ae..de6a71e581 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomTask.kt @@ -102,7 +102,7 @@ internal class DefaultCreateRoomTask @Inject constructor( .equalTo(RoomSummaryEntityFields.MEMBERSHIP_STR, Membership.JOIN.name) } } catch (exception: TimeoutCancellationException) { - throw CreateRoomFailure.CreatedWithTimeout + throw CreateRoomFailure.CreatedWithTimeout(roomId) } Realm.getInstance(realmConfiguration).executeTransactionAsync { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt index 615bc99096..4948e3ace5 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/state/DefaultStateService.kt @@ -73,7 +73,7 @@ internal class DefaultStateService @AssistedInject constructor(@Assisted private eventType = eventType, body = body.toSafeJson(eventType) ) - sendStateTask.execute(params) + sendStateTask.executeRetry(params, 3) } private fun JsonDict.toSafeJson(eventType: String): JsonDict { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/CreateSpaceTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/CreateSpaceTask.kt new file mode 100644 index 0000000000..5f174587d0 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/CreateSpaceTask.kt @@ -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 + +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 + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpace.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpace.kt index 264cfd44ed..efba103ab7 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpace.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpace.kt @@ -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.room.Room 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 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 { return room } + override fun spaceSummary(): SpaceSummary? { + return spaceSummaryDataSource.getSpaceSummary(asRoom().roomId) + } + override suspend fun addChildren(roomId: String, viaServers: List, order: String?, autoJoin: Boolean) { asRoom().sendStateEvent( eventType = EventType.STATE_SPACE_CHILD, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpaceService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpaceService.kt index 906886d5e8..0210c81e5d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpaceService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpaceService.kt @@ -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.toModel 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.create.CreateRoomPreset 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.model.SpaceChildContent 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.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.SpaceGetter 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.SpacePeekResult -import org.matrix.android.sdk.internal.session.user.accountdata.UpdateBreadcrumbsTask -import org.matrix.android.sdk.internal.task.TaskExecutor import javax.inject.Inject internal class DefaultSpaceService @Inject constructor( @SessionDatabase private val monarchy: Monarchy, - private val createRoomTask: CreateRoomTask, - private val joinRoomTask: JoinRoomTask, + private val createSpaceTask: CreateSpaceTask, +// private val joinRoomTask: JoinRoomTask, private val joinSpaceTask: JoinSpaceTask, - private val markAllRoomsReadTask: MarkAllRoomsReadTask, - private val updateBreadcrumbsTask: UpdateBreadcrumbsTask, - private val roomIdByAliasTask: GetRoomIdByAliasTask, - private val deleteRoomAliasTask: DeleteRoomAliasTask, - private val roomGetter: RoomGetter, + private val spaceGetter: SpaceGetter, +// private val markAllRoomsReadTask: MarkAllRoomsReadTask, +// private val updateBreadcrumbsTask: UpdateBreadcrumbsTask, +// private val roomIdByAliasTask: GetRoomIdByAliasTask, +// private val deleteRoomAliasTask: DeleteRoomAliasTask, +// private val roomGetter: RoomGetter, private val spaceSummaryDataSource: SpaceSummaryDataSource, private val peekSpaceTask: PeekSpaceTask, private val resolveSpaceInfoTask: ResolveSpaceInfoTask, - private val leaveRoomTask: LeaveRoomTask, - private val roomChangeMembershipStateDataSource: RoomChangeMembershipStateDataSource, - private val taskExecutor: TaskExecutor + private val leaveRoomTask: LeaveRoomTask +// private val roomChangeMembershipStateDataSource: RoomChangeMembershipStateDataSource, +// private val taskExecutor: TaskExecutor ) : SpaceService { 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 { @@ -78,9 +70,7 @@ internal class DefaultSpaceService @Inject constructor( } override fun getSpace(spaceId: String): Space? { - return roomGetter.getRoom(spaceId) - ?.takeIf { it.roomSummary()?.roomType == RoomType.SPACE } - ?.let { DefaultSpace(it) } + return spaceGetter.get(spaceId) } override fun getSpaceSummariesLive(queryParams: SpaceSummaryQueryParams): LiveData> { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/SpaceModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/SpaceModule.kt index 4612d9e142..84a2d5267f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/SpaceModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/SpaceModule.kt @@ -20,6 +20,8 @@ import dagger.Binds import dagger.Module import dagger.Provides 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.PeekSpaceTask import retrofit2.Retrofit @@ -45,4 +47,10 @@ internal abstract class SpaceModule { @Binds abstract fun bindJoinSpaceTask(task: DefaultJoinSpaceTask): JoinSpaceTask + + @Binds + abstract fun bindSpaceGetter(getter: DefaultSpaceGetter): SpaceGetter + + @Binds + abstract fun bindCreateSpaceTask(getter: DefaultCreateSpaceTask): CreateSpaceTask } diff --git a/vector/src/main/java/im/vector/app/features/form/FormEditTextItem.kt b/vector/src/main/java/im/vector/app/features/form/FormEditTextItem.kt index 68e2e6b371..a1bd2bd1a3 100644 --- a/vector/src/main/java/im/vector/app/features/form/FormEditTextItem.kt +++ b/vector/src/main/java/im/vector/app/features/form/FormEditTextItem.kt @@ -18,6 +18,7 @@ package im.vector.app.features.form import android.text.Editable import android.view.View +import android.view.inputmethod.EditorInfo import androidx.core.view.isVisible import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyModelClass @@ -50,6 +51,12 @@ abstract class FormEditTextItem : VectorEpoxyModel() { @EpoxyAttribute var inputType: Int? = null + @EpoxyAttribute + var singleLine: Boolean? = null + + @EpoxyAttribute + var imeOptions: Int? = null + @EpoxyAttribute var onTextChange: ((String) -> Unit)? = null @@ -69,6 +76,8 @@ abstract class FormEditTextItem : VectorEpoxyModel() { holder.textInputEditText.setTextSafe(value) holder.textInputEditText.isEnabled = enabled inputType?.let { holder.textInputEditText.inputType = it } + holder.textInputEditText.isSingleLine = singleLine ?: false + holder.textInputEditText.imeOptions = imeOptions ?: EditorInfo.IME_ACTION_NONE holder.textInputEditText.addTextChangedListener(onTextChangeListener) holder.bottomSeparator.isVisible = showBottomSeparator diff --git a/vector/src/main/java/im/vector/app/features/form/FormEditableSquareAvatarItem.kt b/vector/src/main/java/im/vector/app/features/form/FormEditableSquareAvatarItem.kt index 5023c1b483..cbb545825d 100644 --- a/vector/src/main/java/im/vector/app/features/form/FormEditableSquareAvatarItem.kt +++ b/vector/src/main/java/im/vector/app/features/form/FormEditableSquareAvatarItem.kt @@ -16,13 +16,16 @@ package im.vector.app.features.form import android.net.Uri +import android.util.TypedValue import android.view.View import android.widget.ImageView import androidx.core.view.isVisible import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyModelClass 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.core.epoxy.ClickListener import im.vector.app.core.epoxy.VectorEpoxyHolder @@ -55,13 +58,24 @@ abstract class FormEditableSquareAvatarItem : EpoxyModelWithHolder { + val corner = TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, + 8f, + holder.view.resources.displayMetrics + ).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.onClick(deleteListener?.takeIf { enabled }) @@ -72,6 +86,7 @@ abstract class FormEditableSquareAvatarItem : EpoxyModelWithHolder(R.id.itemEditableAvatarImageContainer) val image by bind(R.id.itemEditableAvatarImage) diff --git a/vector/src/main/java/im/vector/app/features/grouplist/HomeSpaceSummaryItem.kt b/vector/src/main/java/im/vector/app/features/grouplist/HomeSpaceSummaryItem.kt index ade86a9d89..c3f459f49e 100644 --- a/vector/src/main/java/im/vector/app/features/grouplist/HomeSpaceSummaryItem.kt +++ b/vector/src/main/java/im/vector/app/features/grouplist/HomeSpaceSummaryItem.kt @@ -35,6 +35,10 @@ abstract class HomeSpaceSummaryItem : VectorEpoxyModel 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) { super.bind(holder) holder.rootView.setOnClickListener { listener?.invoke() } diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt index 982b59263d..40d0cdd622 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt @@ -16,6 +16,7 @@ package im.vector.app.features.home +import android.app.Activity import android.content.Context import android.content.Intent 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.extensions.exhaustive 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.platform.ToolbarConfigurable import im.vector.app.core.platform.VectorBaseActivity @@ -103,6 +105,23 @@ class HomeActivity : @Inject lateinit var avatarRenderer: AvatarRenderer @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() { override fun onDrawerStateChanged(newState: Int) { hideKeyboard() @@ -147,7 +166,7 @@ class HomeActivity : startActivity(SpacePreviewActivity.newIntent(this, sharedAction.spaceId)) } is HomeActivitySharedAction.AddSpace -> { - startActivity(SpaceCreationActivity.newIntent(this)) + createSpaceResultLauncher.launch(SpaceCreationActivity.newIntent(this)) } }.exhaustive } diff --git a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt index 76e4cad28f..d6eeade97e 100644 --- a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt +++ b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt @@ -29,6 +29,7 @@ import androidx.core.app.ActivityOptionsCompat import androidx.core.app.TaskStackBuilder import androidx.core.util.Pair import androidx.core.view.ViewCompat +import arrow.core.Option import im.vector.app.R import im.vector.app.core.di.ActiveSessionHolder 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.debug.DebugMenuActivity 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.RoomDetailArgs 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.widgets.model.Widget import org.matrix.android.sdk.api.session.widgets.model.WidgetType +import timber.log.Timber import javax.inject.Inject import javax.inject.Singleton @@ -85,6 +88,7 @@ class DefaultNavigator @Inject constructor( private val sessionHolder: ActiveSessionHolder, private val vectorPreferences: VectorPreferences, private val widgetArgsBuilder: WidgetArgsBuilder, + private val selectedSpaceDataSource: SelectedSpaceDataSource, private val supportedVerificationMethodsProvider: SupportedVerificationMethodsProvider ) : Navigator { @@ -98,6 +102,23 @@ class DefaultNavigator @Inject constructor( 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) { val session = sessionHolder.getSafeActiveSession() ?: return val tx = session.cryptoService().verificationService().getExistingTransaction(otherUserId, sasTransactionId) diff --git a/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt b/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt index b4bd677b0c..fcd35be162 100644 --- a/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt +++ b/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt @@ -38,6 +38,8 @@ interface Navigator { 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 requestSessionVerification(context: Context, otherSessionId: String) diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceCreationActivity.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceCreationActivity.kt index 65c8102020..ed8b85f587 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpaceCreationActivity.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceCreationActivity.kt @@ -20,7 +20,9 @@ import android.content.Context import android.content.Intent import android.os.Bundle import android.view.MenuItem +import androidx.appcompat.app.AlertDialog import androidx.fragment.app.Fragment +import com.airbnb.mvrx.Loading import com.airbnb.mvrx.viewModel import com.airbnb.mvrx.withState import im.vector.app.R @@ -91,6 +93,23 @@ class SpaceCreationActivity : SimpleFragmentActivity(), CreateSpaceViewModel.Fac CreateSpaceEvents.NavigateToAddRooms -> { 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) { 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.AddRooms -> R.string.your_public_space } supportActionBar?.let { it.title = getString(titleRes) } ?: run { setTitle(getString(titleRes)) } + + if (state.creationResult is Loading) { + showWaitingView(getString(R.string.create_spaces_loading_message)) + } } 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 { return Intent(context, SpaceCreationActivity::class.java).apply { // putExtra(MvRx.KEY_ARG, SpaceDirectoryArgs(spaceId)) diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt index b8089d6f98..4ec6e7d4ad 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpacesListViewModel.kt @@ -86,8 +86,15 @@ class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: Sp private var currentGroupId = "" init { - observeGroupSummaries() + observeSpaceSummaries() observeSelectionState() + selectedSpaceDataSource.observe().execute { + if (this.selectedSpace != it.invoke()?.orNull()) { + copy( + selectedSpace = it.invoke()?.orNull() + ) + } else this + } } private fun observeSelectionState() { @@ -143,8 +150,8 @@ class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: Sp _viewEvents.post(SpaceListViewEvents.AddSpace) } - private fun observeGroupSummaries() { - val roomSummaryQueryParams = roomSummaryQueryParams() { + private fun observeSpaceSummaries() { + val spaceSummaryQueryParams = roomSummaryQueryParams() { memberships = listOf(Membership.JOIN, Membership.INVITE) displayName = QueryStringValue.IsNotEmpty excludeType = listOf(/**RoomType.MESSAGING,$*/ @@ -171,7 +178,7 @@ class SpacesListViewModel @AssistedInject constructor(@Assisted initialState: Sp }, session .rx() - .liveSpaceSummaries(roomSummaryQueryParams), + .liveSpaceSummaries(spaceSummaryQueryParams), BiFunction { allCommunityGroup, communityGroups -> listOf(allCommunityGroup) + communityGroups } diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/ChooseSpaceTypeFragment.kt b/vector/src/main/java/im/vector/app/features/spaces/create/ChooseSpaceTypeFragment.kt index 080bb99ea4..b174952ec6 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/create/ChooseSpaceTypeFragment.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/create/ChooseSpaceTypeFragment.kt @@ -43,7 +43,7 @@ class ChooseSpaceTypeFragment @Inject constructor( })) views.privateButton.setOnClickListener(DebouncedClickListener({ - sharedViewModel.handle(CreateSpaceAction.SetRoomType(SpaceType.Private)) + // sharedViewModel.handle(CreateSpaceAction.SetRoomType(SpaceType.Private)) })) } } diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceDefaultRoomsFragment.kt b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceDefaultRoomsFragment.kt index 2dc36f8715..fb1ed8e5f8 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceDefaultRoomsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceDefaultRoomsFragment.kt @@ -46,10 +46,14 @@ class CreateSpaceDefaultRoomsFragment @Inject constructor( } 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 // ----------------------------- diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceDetailsFragment.kt b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceDetailsFragment.kt index 506f71c92f..670876fdf1 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceDetailsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceDetailsFragment.kt @@ -16,25 +16,32 @@ package im.vector.app.features.spaces.create +import android.net.Uri 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.core.dialogs.GalleryOrCameraDialogHelper import im.vector.app.core.extensions.configureWith import im.vector.app.core.platform.VectorBaseFragment +import im.vector.app.core.resources.ColorProvider import im.vector.app.databinding.FragmentSpaceCreateGenericEpoxyFormBinding import javax.inject.Inject class CreateSpaceDetailsFragment @Inject constructor( - private val epoxyController: SpaceDetailEpoxyController -) : VectorBaseFragment(), SpaceDetailEpoxyController.Listener { + private val epoxyController: SpaceDetailEpoxyController, + private val colorProvider: ColorProvider +) : VectorBaseFragment(), SpaceDetailEpoxyController.Listener, + GalleryOrCameraDialogHelper.Listener { private val sharedViewModel: CreateSpaceViewModel by activityViewModel() override fun getBinding(inflater: LayoutInflater, container: ViewGroup?) = FragmentSpaceCreateGenericEpoxyFormBinding.inflate(layoutInflater, container, false) + private val galleryOrCameraDialogHelper = GalleryOrCameraDialogHelper(this, colorProvider) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 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 // ----------------------------- override fun onAvatarDelete() { + sharedViewModel.handle(CreateSpaceAction.SetAvatar(null)) } override fun onAvatarChange() { + galleryOrCameraDialogHelper.show() } override fun onNameChange(newName: String) { diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModel.kt index aca8452300..cd2680ef5f 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModel.kt @@ -17,20 +17,29 @@ package im.vector.app.features.spaces.create import android.net.Uri +import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.ActivityViewModelContext +import com.airbnb.mvrx.Async +import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext +import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.Success +import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.R +import im.vector.app.core.error.ErrorFormatter import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewEvents import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.platform.VectorViewModelAction import im.vector.app.core.resources.StringProvider +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.Session data class CreateSpaceState( @@ -39,8 +48,9 @@ data class CreateSpaceState( val topic: String = "", val step: Step = Step.ChooseType, val spaceType: SpaceType? = null, - val nameInlineError : String? = null, - val defaultRooms: List? = null + val nameInlineError: String? = null, + val defaultRooms: Map? = null, + val creationResult: Async = Uninitialized ) : MvRxState { enum class Step { @@ -59,8 +69,11 @@ sealed class CreateSpaceAction : VectorViewModelAction { data class SetRoomType(val type: SpaceType) : CreateSpaceAction() data class NameChanged(val name: String) : CreateSpaceAction() data class TopicChanged(val topic: String) : CreateSpaceAction() + data class SetAvatar(val uri: Uri?) : CreateSpaceAction() object OnBackPressed : CreateSpaceAction() object NextFromDetails : CreateSpaceAction() + object NextFromDefaultRooms : CreateSpaceAction() + data class DefaultRoomNameChanged(val index: Int, val name: String) : CreateSpaceAction() } sealed class CreateSpaceEvents : VectorViewEvents { @@ -68,12 +81,17 @@ sealed class CreateSpaceEvents : VectorViewEvents { object NavigateToChooseType : CreateSpaceEvents() object NavigateToAddRooms : 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( @Assisted initialState: CreateSpaceState, private val session: Session, - private val stringProvider: StringProvider + private val stringProvider: StringProvider, + private val createSpaceViewModelTask: CreateSpaceViewModelTask, + private val errorFormatter: ErrorFormatter ) : VectorViewModel(initialState) { @AssistedFactory @@ -90,6 +108,12 @@ class CreateSpaceViewModel @AssistedInject constructor( } 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) { @@ -124,6 +148,21 @@ class CreateSpaceViewModel @AssistedInject constructor( CreateSpaceAction.NextFromDetails -> { 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 } @@ -167,4 +206,53 @@ class CreateSpaceViewModel @AssistedInject constructor( _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))) + } + } + } } diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModelTask.kt b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModelTask.kt new file mode 100644 index 0000000000..a565e290f6 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModelTask.kt @@ -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) : CreateSpaceTaskResult() + + data class PartialSuccess(val spaceId: String, val childIds: List, val failedRooms: Map) : 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 = emptyList() +) + +class CreateSpaceViewModelTask @Inject constructor( + private val session: Session, + private val stringProvider: StringProvider +) : ViewModelTask { + + 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() + val childIds = mutableListOf() + if (params.isPublic) { + params.defaultRooms + .filter { it.isNotBlank() } + .forEach { roomName -> + try { + val roomId = try { + awaitCallback { + 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) + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/SpaceDefaultRoomEpoxyController.kt b/vector/src/main/java/im/vector/app/features/spaces/create/SpaceDefaultRoomEpoxyController.kt index 05abcf95b0..27b57713ba 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/create/SpaceDefaultRoomEpoxyController.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/create/SpaceDefaultRoomEpoxyController.kt @@ -28,7 +28,6 @@ import javax.inject.Inject class SpaceDefaultRoomEpoxyController @Inject constructor( private val stringProvider: StringProvider, private val colorProvider: ColorProvider -// private val avatarRenderer: AvatarRenderer ) : TypedEpoxyController() { var listener: Listener? = null @@ -50,44 +49,41 @@ class SpaceDefaultRoomEpoxyController @Inject constructor( formEditTextItem { id("roomName1") enabled(true) - value(data?.name) - hint(stringProvider.getString(R.string.create_room_name_hint)) + value(data?.defaultRooms?.get(0)) + hint(stringProvider.getString(R.string.create_room_name_section)) showBottomSeparator(false) -// errorMessage(data?.nameInlineError) onTextChange { text -> -// listener?.onNameChange(text) + listener?.onNameChange(0, text) } } formEditTextItem { id("roomName2") enabled(true) -// value(data?.name) - hint(stringProvider.getString(R.string.create_room_name_hint)) + value(data?.defaultRooms?.get(1)) + hint(stringProvider.getString(R.string.create_room_name_section)) showBottomSeparator(false) -// errorMessage(data?.nameInlineError) onTextChange { text -> -// listener?.onNameChange(text) + listener?.onNameChange(1, text) } } formEditTextItem { id("roomName3") enabled(true) -// value(data?.name) - hint(stringProvider.getString(R.string.create_room_name_hint)) + value(data?.defaultRooms?.get(2)) + hint(stringProvider.getString(R.string.create_room_name_section)) showBottomSeparator(false) -// errorMessage(data?.nameInlineError) onTextChange { text -> -// listener?.onNameChange(text) + listener?.onNameChange(2, text) } } } interface Listener { -// fun onAvatarDelete() + // fun onAvatarDelete() // fun onAvatarChange() -// fun onNameChange(newName: String) + fun onNameChange(index: Int, newName: String) // fun onTopicChange(newTopic: String) } } diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/WizardButtonView.kt b/vector/src/main/java/im/vector/app/features/spaces/create/WizardButtonView.kt index 552a98ded2..066c329c34 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/create/WizardButtonView.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/create/WizardButtonView.kt @@ -18,6 +18,7 @@ package im.vector.app.features.spaces.create import android.content.Context import android.graphics.drawable.Drawable +import android.os.Build import android.util.AttributeSet import android.util.TypedValue import androidx.appcompat.content.res.AppCompatResources.getDrawable @@ -85,7 +86,7 @@ class WizardButtonView @JvmOverloads constructor(context: Context, attrs: Attrib val outValue = TypedValue() 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) } diff --git a/vector/src/main/res/values/ids.xml b/vector/src/main/res/values/ids.xml new file mode 100644 index 0000000000..099d6f4279 --- /dev/null +++ b/vector/src/main/res/values/ids.xml @@ -0,0 +1,3 @@ + + + diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index b2bfe76f57..fb578f3802 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -3275,6 +3275,8 @@ Give it a name to continue. What are some discussions you want to have in Runner’s World? We’ll create rooms for them, and auto-join everyone. You can add more later too. + General + Creating Space…