diff --git a/newsfragment/3485.feature b/newsfragment/3485.feature new file mode 100644 index 0000000000..68c78267be --- /dev/null +++ b/newsfragment/3485.feature @@ -0,0 +1 @@ +Add beta warning to private space creation flow \ No newline at end of file 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 57c6b40404..dbcb9fd5bf 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,6 +20,7 @@ import android.content.Context import android.content.Intent import android.os.Bundle import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentManager import com.airbnb.mvrx.Loading import com.airbnb.mvrx.viewModel import com.airbnb.mvrx.withState @@ -28,6 +29,7 @@ import im.vector.app.R import im.vector.app.core.di.ScreenComponent import im.vector.app.core.extensions.toMvRxBundle import im.vector.app.core.platform.SimpleFragmentActivity +import im.vector.app.features.spaces.create.BetaWarningBottomSheet import im.vector.app.features.spaces.create.ChoosePrivateSpaceTypeFragment import im.vector.app.features.spaces.create.ChooseSpaceTypeFragment import im.vector.app.features.spaces.create.CreateSpaceAction @@ -40,7 +42,7 @@ import im.vector.app.features.spaces.create.SpaceTopology import im.vector.app.features.spaces.create.SpaceType import javax.inject.Inject -class SpaceCreationActivity : SimpleFragmentActivity(), CreateSpaceViewModel.Factory { +class SpaceCreationActivity : SimpleFragmentActivity(), CreateSpaceViewModel.Factory, BetaWarningBottomSheet.InteractionListener { @Inject lateinit var viewModelFactory: CreateSpaceViewModel.Factory @@ -51,18 +53,38 @@ class SpaceCreationActivity : SimpleFragmentActivity(), CreateSpaceViewModel.Fac val viewModel: CreateSpaceViewModel by viewModel() + private val fragmentLifecycleCallbacks = object : FragmentManager.FragmentLifecycleCallbacks() { + override fun onFragmentAttached(fm: FragmentManager, f: Fragment, context: Context) { + when (f) { + is BetaWarningBottomSheet -> { + f.interactionListener = this@SpaceCreationActivity + } + } + super.onFragmentAttached(fm, f, context) + } + + override fun onFragmentDetached(fm: FragmentManager, f: Fragment) { + when (f) { + is BetaWarningBottomSheet -> { + f.interactionListener = null + } + } + super.onFragmentDetached(fm, f) + } + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - + supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, false) if (isFirstCreation()) { when (withState(viewModel) { it.step }) { - CreateSpaceState.Step.ChooseType -> { + CreateSpaceState.Step.ChooseType -> { navigateToFragment(ChooseSpaceTypeFragment::class.java) } - CreateSpaceState.Step.SetDetails -> { + CreateSpaceState.Step.SetDetails -> { navigateToFragment(ChooseSpaceTypeFragment::class.java) } - CreateSpaceState.Step.AddRooms -> { + CreateSpaceState.Step.AddRooms -> { navigateToFragment(CreateSpaceDefaultRoomsFragment::class.java) } CreateSpaceState.Step.ChoosePrivateType -> { @@ -72,6 +94,11 @@ class SpaceCreationActivity : SimpleFragmentActivity(), CreateSpaceViewModel.Fac } } + override fun onDestroy() { + supportFragmentManager.unregisterFragmentLifecycleCallbacks(fragmentLifecycleCallbacks) + super.onDestroy() + } + override fun initUiAndData() { super.initUiAndData() @@ -81,29 +108,29 @@ class SpaceCreationActivity : SimpleFragmentActivity(), CreateSpaceViewModel.Fac viewModel.observeViewEvents { when (it) { - CreateSpaceEvents.NavigateToDetails -> { + CreateSpaceEvents.NavigateToDetails -> { navigateToFragment(CreateSpaceDetailsFragment::class.java) } - CreateSpaceEvents.NavigateToChooseType -> { + CreateSpaceEvents.NavigateToChooseType -> { navigateToFragment(ChooseSpaceTypeFragment::class.java) } - CreateSpaceEvents.Dismiss -> { + CreateSpaceEvents.Dismiss -> { finish() } - CreateSpaceEvents.NavigateToAddRooms -> { + CreateSpaceEvents.NavigateToAddRooms -> { navigateToFragment(CreateSpaceDefaultRoomsFragment::class.java) } CreateSpaceEvents.NavigateToChoosePrivateType -> { navigateToFragment(ChoosePrivateSpaceTypeFragment::class.java) } - is CreateSpaceEvents.ShowModalError -> { + is CreateSpaceEvents.ShowModalError -> { hideWaitingView() MaterialAlertDialogBuilder(this) .setMessage(it.errorMessage) .setPositiveButton(getString(R.string.ok), null) .show() } - is CreateSpaceEvents.FinishSuccess -> { + is CreateSpaceEvents.FinishSuccess -> { setResult(RESULT_OK, Intent().apply { putExtra(RESULT_DATA_CREATED_SPACE_ID, it.spaceId) putExtra(RESULT_DATA_DEFAULT_ROOM_ID, it.defaultRoomId) @@ -111,7 +138,7 @@ class SpaceCreationActivity : SimpleFragmentActivity(), CreateSpaceViewModel.Fac }) finish() } - CreateSpaceEvents.HideModalLoading -> { + CreateSpaceEvents.HideModalLoading -> { hideWaitingView() } } @@ -135,9 +162,9 @@ class SpaceCreationActivity : SimpleFragmentActivity(), CreateSpaceViewModel.Fac private fun renderState(state: CreateSpaceState) { val titleRes = when (state.step) { - CreateSpaceState.Step.ChooseType -> R.string.activity_create_space_title + CreateSpaceState.Step.ChooseType -> R.string.activity_create_space_title CreateSpaceState.Step.SetDetails, - CreateSpaceState.Step.AddRooms -> { + CreateSpaceState.Step.AddRooms -> { if (state.spaceType == SpaceType.Public) R.string.your_public_space else R.string.your_private_space } @@ -179,4 +206,8 @@ class SpaceCreationActivity : SimpleFragmentActivity(), CreateSpaceViewModel.Fac } override fun create(initialState: CreateSpaceState): CreateSpaceViewModel = viewModelFactory.create(initialState) + + override fun betaWarningOnContinueAnyway() { + viewModel.handle(CreateSpaceAction.ConfirmBetaWarning) + } } diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/BetaWarningBottomSheet.kt b/vector/src/main/java/im/vector/app/features/spaces/create/BetaWarningBottomSheet.kt new file mode 100644 index 0000000000..82e8721b07 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/spaces/create/BetaWarningBottomSheet.kt @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.spaces.create + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment +import im.vector.app.databinding.BottomSheetSpaceCreatePrivateWarningBinding + +class BetaWarningBottomSheet : VectorBaseBottomSheetDialogFragment<BottomSheetSpaceCreatePrivateWarningBinding>() { + + interface InteractionListener { + fun betaWarningOnContinueAnyway() + } + + var interactionListener: InteractionListener? = null + + override fun getBinding(inflater: LayoutInflater, container: ViewGroup?) = + BottomSheetSpaceCreatePrivateWarningBinding.inflate(inflater, container, false) + + override val showExpanded = true + + override fun onDetach() { + interactionListener = null + super.onDetach() + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + views.continueButton.debouncedClicks { + interactionListener?.betaWarningOnContinueAnyway() + dismiss() + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/ChoosePrivateSpaceTypeFragment.kt b/vector/src/main/java/im/vector/app/features/spaces/create/ChoosePrivateSpaceTypeFragment.kt index 4f079551eb..49fe9a7dec 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/create/ChoosePrivateSpaceTypeFragment.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/create/ChoosePrivateSpaceTypeFragment.kt @@ -46,12 +46,20 @@ class ChoosePrivateSpaceTypeFragment @Inject constructor( } views.teammatesButton.onClick { - sharedViewModel.handle(CreateSpaceAction.SetSpaceTopology(SpaceTopology.MeAndTeammates)) + BetaWarningBottomSheet().show(parentFragmentManager, "warning") } sharedViewModel.subscribe { state -> views.accessInfoHelpText.text = stringProvider.getString(R.string.create_spaces_make_sure_access, state.name ?: "") } + + sharedViewModel.observeViewEvents { + when (it) { + CreateSpaceEvents.OnConfirmBetaWarning -> { + sharedViewModel.handle(CreateSpaceAction.SetSpaceTopology(SpaceTopology.MeAndTeammates)) + } + } + } } override fun onBackPressed(toolbarButton: Boolean): Boolean { diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceAction.kt b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceAction.kt index cd31b40354..291679764f 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceAction.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceAction.kt @@ -29,4 +29,5 @@ sealed class CreateSpaceAction : VectorViewModelAction { object NextFromDefaultRooms : CreateSpaceAction() data class DefaultRoomNameChanged(val index: Int, val name: String) : CreateSpaceAction() data class SetSpaceTopology(val topology: SpaceTopology) : CreateSpaceAction() + object ConfirmBetaWarning : CreateSpaceAction() } diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceEvents.kt b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceEvents.kt index c3fa2b2068..3e40fc6ad7 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceEvents.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceEvents.kt @@ -27,4 +27,5 @@ sealed class CreateSpaceEvents : VectorViewEvents { data class FinishSuccess(val spaceId: String, val defaultRoomId: String?, val topology: SpaceTopology?) : CreateSpaceEvents() data class ShowModalError(val errorMessage: String) : CreateSpaceEvents() object HideModalLoading : CreateSpaceEvents() + object OnConfirmBetaWarning : CreateSpaceEvents() } 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 9881232f4d..3da6a7554f 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 @@ -118,6 +118,9 @@ class CreateSpaceViewModel @AssistedInject constructor( is CreateSpaceAction.SetSpaceTopology -> { handleSetTopology(action) } + CreateSpaceAction.ConfirmBetaWarning -> { + _viewEvents.post(CreateSpaceEvents.OnConfirmBetaWarning) + } }.exhaustive } diff --git a/vector/src/main/res/layout/bottom_sheet_space_create_private_warning.xml b/vector/src/main/res/layout/bottom_sheet_space_create_private_warning.xml new file mode 100644 index 0000000000..7292c7df55 --- /dev/null +++ b/vector/src/main/res/layout/bottom_sheet_space_create_private_warning.xml @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="?colorSurface" + android:orientation="vertical" + android:padding="16dp"> + + <ImageView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_horizontal" + android:layout_marginTop="16dp" + android:layout_marginBottom="12dp" + android:importantForAccessibility="no" + android:src="@drawable/ic_beta_pill" /> + + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="16dp" + android:layout_marginBottom="12dp" + android:gravity="center" + android:text="@string/teammate_spaces_arent_quite_ready" + android:textColor="?vctr_content_primary" + android:textSize="20sp" + android:textStyle="bold" /> + + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="20dp" + android:gravity="center" + android:text="@string/teammate_spaces_might_not_join" + android:textColor="?vctr_content_secondary" + android:textSize="16sp" /> + + <Button + android:id="@+id/continueButton" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/layout_vertical_margin" + android:text="@string/continue_anyway" + android:textAllCaps="true" /> + +</LinearLayout> diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 80120b51bf..621ecb7aee 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -771,6 +771,7 @@ <string name="yes">YES</string> <string name="no">NO</string> <string name="_continue">Continue</string> + <string name="continue_anyway">Continue Anyway</string> <!-- Actions --> <string name="remove">Remove</string> @@ -3399,4 +3400,7 @@ <string name="this_space_has_no_rooms_admin">Some rooms may be hidden because they’re private and you need an invite.</string> <string name="unnamed_room">Unnamed Room</string> + + <string name="teammate_spaces_arent_quite_ready">"Teammate spaces aren’t quite ready but you can still give them a try"</string> + <string name="teammate_spaces_might_not_join">"At the moment people might not be able to join any private rooms you make.\n\nWe’ll be improving this as part of the beta, but just wanted to let you know."</string> </resources>