mirror of
https://github.com/anyproto/anytype-kotlin.git
synced 2025-06-07 21:37:02 +09:00
DROID-3446 Vault | New space or chat creation flow (#2481)
This commit is contained in:
parent
c9f5ec3c3f
commit
78fb9002f7
13 changed files with 595 additions and 235 deletions
|
@ -6,16 +6,13 @@ import com.anytypeio.anytype.core_utils.di.scope.PerScreen
|
|||
import com.anytypeio.anytype.di.common.ComponentDependencies
|
||||
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
|
||||
import com.anytypeio.anytype.domain.block.repo.BlockRepository
|
||||
import com.anytypeio.anytype.domain.multiplayer.SpaceViewSubscriptionContainer
|
||||
import com.anytypeio.anytype.domain.workspace.SpaceManager
|
||||
import com.anytypeio.anytype.presentation.analytics.AnalyticSpaceHelperDelegate
|
||||
import com.anytypeio.anytype.presentation.spaces.CreateSpaceViewModel
|
||||
import com.anytypeio.anytype.presentation.spaces.SpaceGradientProvider
|
||||
import com.anytypeio.anytype.ui.spaces.CreateSpaceFragment
|
||||
import dagger.Binds
|
||||
import dagger.Component
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
|
||||
@Component(
|
||||
dependencies = [CreateSpaceDependencies::class],
|
||||
|
@ -50,5 +47,4 @@ interface CreateSpaceDependencies : ComponentDependencies {
|
|||
fun dispatchers(): AppCoroutineDispatchers
|
||||
fun spaceManager(): SpaceManager
|
||||
fun analyticSpaceHelper(): AnalyticSpaceHelperDelegate
|
||||
fun spaceViewContainer(): SpaceViewSubscriptionContainer
|
||||
}
|
|
@ -4,6 +4,9 @@ import android.os.Bundle
|
|||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.PickVisualMediaRequest
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
|
@ -12,6 +15,8 @@ import androidx.compose.ui.platform.ViewCompositionStrategy
|
|||
import androidx.fragment.app.viewModels
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import com.anytypeio.anytype.R
|
||||
import com.anytypeio.anytype.core_utils.ext.argString
|
||||
import com.anytypeio.anytype.core_utils.ext.parseImagePath
|
||||
import com.anytypeio.anytype.core_utils.ext.toast
|
||||
import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetComposeFragment
|
||||
import com.anytypeio.anytype.di.common.componentManager
|
||||
|
@ -29,6 +34,8 @@ class CreateSpaceFragment : BaseBottomSheetComposeFragment() {
|
|||
|
||||
private val vm by viewModels<CreateSpaceViewModel> { factory }
|
||||
|
||||
private val spaceType get() = argString(ARG_SPACE_TYPE)
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
|
@ -39,11 +46,33 @@ class CreateSpaceFragment : BaseBottomSheetComposeFragment() {
|
|||
MaterialTheme(
|
||||
typography = typography
|
||||
) {
|
||||
val imagePickerLauncher = rememberLauncherForActivityResult(
|
||||
contract = ActivityResultContracts.PickVisualMedia(),
|
||||
onResult = { uri ->
|
||||
if (uri != null) {
|
||||
vm.onImageSelected(url = uri.parseImagePath(context))
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
CreateSpaceScreen(
|
||||
spaceIconView = vm.spaceIconView.collectAsState().value,
|
||||
onCreate = vm::onCreateSpace,
|
||||
onSpaceIconClicked = vm::onSpaceIconClicked,
|
||||
isLoading = vm.isInProgress.collectAsState()
|
||||
onCreate = { name, isSpaceLevelChatSwitchChecked ->
|
||||
vm.onCreateSpace(
|
||||
name = name,
|
||||
withChat = spaceType == TYPE_CHAT
|
||||
)
|
||||
},
|
||||
onSpaceIconUploadClicked = {
|
||||
imagePickerLauncher.launch(
|
||||
PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly)
|
||||
)
|
||||
},
|
||||
onSpaceIconRemoveClicked = {
|
||||
vm.onSpaceIconRemovedClicked()
|
||||
},
|
||||
isLoading = vm.isInProgress.collectAsState(),
|
||||
isChatSpace = spaceType == TYPE_CHAT
|
||||
)
|
||||
LaunchedEffect(Unit) { vm.toasts.collect { toast(it) } }
|
||||
LaunchedEffect(Unit) {
|
||||
|
@ -99,4 +128,11 @@ class CreateSpaceFragment : BaseBottomSheetComposeFragment() {
|
|||
override fun releaseDependencies() {
|
||||
componentManager().createSpaceComponent.release()
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val ARG_SPACE_TYPE = "arg.space_type"
|
||||
|
||||
const val TYPE_SPACE = "space"
|
||||
const val TYPE_CHAT = "chat"
|
||||
}
|
||||
}
|
|
@ -1,76 +1,98 @@
|
|||
package com.anytypeio.anytype.ui.spaces
|
||||
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.imePadding
|
||||
import androidx.compose.foundation.layout.navigationBars
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||
import androidx.compose.foundation.layout.wrapContentSize
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.text.BasicTextField
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.DropdownMenuItem
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.TextFieldDefaults
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.SwitchDefaults
|
||||
import androidx.compose.material3.TextFieldDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.VisualTransformation
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.DpOffset
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.anytypeio.anytype.BuildConfig
|
||||
import com.anytypeio.anytype.R
|
||||
import com.anytypeio.anytype.core_models.Name
|
||||
import com.anytypeio.anytype.core_models.PRIVATE_SPACE_TYPE
|
||||
import com.anytypeio.anytype.core_models.SystemColor
|
||||
import com.anytypeio.anytype.core_ui.common.DefaultPreviews
|
||||
import com.anytypeio.anytype.core_ui.features.SpaceIconView
|
||||
import com.anytypeio.anytype.core_ui.foundation.Divider
|
||||
import com.anytypeio.anytype.core_ui.foundation.Dragger
|
||||
import com.anytypeio.anytype.core_ui.views.BodyCalloutMedium
|
||||
import com.anytypeio.anytype.core_ui.views.BodyRegular
|
||||
import com.anytypeio.anytype.core_ui.views.BodySemiBold
|
||||
import com.anytypeio.anytype.core_ui.views.ButtonPrimaryLoading
|
||||
import com.anytypeio.anytype.core_ui.views.ButtonSize
|
||||
import com.anytypeio.anytype.core_ui.views.Caption1Regular
|
||||
import com.anytypeio.anytype.core_ui.views.HeadlineHeading
|
||||
import com.anytypeio.anytype.core_ui.views.Title2
|
||||
import com.anytypeio.anytype.core_ui.views.Title1
|
||||
import com.anytypeio.anytype.presentation.spaces.SpaceIconView
|
||||
import com.anytypeio.anytype.ui_settings.space.TypeOfSpace
|
||||
|
||||
@Composable
|
||||
fun CreateSpaceScreen(
|
||||
spaceIconView: SpaceIconView.Placeholder,
|
||||
spaceIconView: SpaceIconView,
|
||||
onCreate: (Name, IsSpaceLevelChatSwitchChecked) -> Unit,
|
||||
onSpaceIconClicked: () -> Unit,
|
||||
isLoading: State<Boolean>
|
||||
onSpaceIconUploadClicked: () -> Unit,
|
||||
onSpaceIconRemoveClicked: () -> Unit,
|
||||
isLoading: State<Boolean>,
|
||||
isChatSpace: Boolean = false
|
||||
) {
|
||||
|
||||
var isSpaceLevelChatSwitchChecked = remember { mutableStateOf(false) }
|
||||
|
||||
val input = remember {
|
||||
mutableStateOf("")
|
||||
var innerValue by rememberSaveable(stateSaver = TextFieldValue.Saver) {
|
||||
mutableStateOf(TextFieldValue(""))
|
||||
}
|
||||
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
val focusManager = LocalFocusManager.current
|
||||
val keyboardController = LocalSoftwareKeyboardController.current
|
||||
|
||||
Box(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(
|
||||
color = colorResource(id = R.color.background_primary),
|
||||
shape = RoundedCornerShape(16.dp)
|
||||
)
|
||||
.imePadding()
|
||||
) {
|
||||
Dragger(
|
||||
modifier = Modifier
|
||||
|
@ -83,44 +105,101 @@ fun CreateSpaceScreen(
|
|||
.verticalScroll(rememberScrollState())
|
||||
.padding(top = 16.dp)
|
||||
) {
|
||||
Header()
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Header(isChatSpace = isChatSpace)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
SpaceIcon(
|
||||
modifier = Modifier.align(Alignment.CenterHorizontally),
|
||||
spaceIconView = spaceIconView.copy(
|
||||
name = input.value.ifEmpty {
|
||||
stringResource(id = R.string.s)
|
||||
}
|
||||
),
|
||||
onSpaceIconClicked = onSpaceIconClicked
|
||||
spaceIconView = when (spaceIconView) {
|
||||
is SpaceIconView.Placeholder -> spaceIconView.copy(
|
||||
name = innerValue.text.ifEmpty {
|
||||
stringResource(id = R.string.u)
|
||||
}
|
||||
)
|
||||
else -> spaceIconView
|
||||
},
|
||||
onSpaceIconUploadClicked = onSpaceIconUploadClicked,
|
||||
onSpaceIconRemoveClicked = onSpaceIconRemoveClicked
|
||||
)
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
SpaceNameInput(
|
||||
input = input,
|
||||
onActionDone = {
|
||||
focusManager.clearFocus()
|
||||
onCreate(input.value, isSpaceLevelChatSwitchChecked.value)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(6.dp))
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterHorizontally),
|
||||
text = stringResource(id = R.string.create_space_change_icon),
|
||||
style = BodyCalloutMedium,
|
||||
color = colorResource(id = R.color.glyph_active)
|
||||
)
|
||||
Divider()
|
||||
Section(title = stringResource(id = R.string.type))
|
||||
TypeOfSpace(spaceType = PRIVATE_SPACE_TYPE)
|
||||
Divider()
|
||||
if (BuildConfig.DEBUG) {
|
||||
DebugCreateSpaceLevelChatToggle(isSpaceLevelChatSwitchChecked)
|
||||
Spacer(modifier = Modifier.height(20.dp))
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
OutlinedTextField(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp)
|
||||
.focusRequester(focusRequester),
|
||||
value = innerValue,
|
||||
onValueChange = {
|
||||
innerValue = it
|
||||
},
|
||||
placeholder = {
|
||||
Text(
|
||||
text = stringResource(id = R.string.untitled),
|
||||
style = BodySemiBold,
|
||||
color = colorResource(id = R.color.text_tertiary)
|
||||
)
|
||||
},
|
||||
label = {
|
||||
Text(
|
||||
text = stringResource(id = R.string.name),
|
||||
style = Caption1Regular,
|
||||
color = colorResource(id = R.color.text_secondary)
|
||||
)
|
||||
},
|
||||
keyboardActions = KeyboardActions(
|
||||
onDone = {
|
||||
focusManager.clearFocus()
|
||||
onCreate(innerValue.text, isSpaceLevelChatSwitchChecked.value)
|
||||
}
|
||||
),
|
||||
textStyle = BodySemiBold.copy(
|
||||
color = colorResource(id = R.color.text_primary)
|
||||
),
|
||||
shape = RoundedCornerShape(size = 12.dp),
|
||||
colors = TextFieldDefaults.colors(
|
||||
cursorColor = colorResource(id = R.color.text_primary),
|
||||
focusedContainerColor = colorResource(id = R.color.transparent),
|
||||
unfocusedContainerColor = colorResource(id = R.color.transparent),
|
||||
focusedIndicatorColor = colorResource(id = R.color.shape_primary),
|
||||
unfocusedIndicatorColor = colorResource(id = R.color.shape_tertiary),
|
||||
)
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(78.dp))
|
||||
}
|
||||
CreateSpaceButton(
|
||||
onCreate = { name ->
|
||||
ButtonPrimaryLoading(
|
||||
onClick = {
|
||||
focusManager.clearFocus()
|
||||
onCreate(name, isSpaceLevelChatSwitchChecked.value)
|
||||
keyboardController?.hide()
|
||||
onCreate(innerValue.text, isSpaceLevelChatSwitchChecked.value)
|
||||
},
|
||||
input = input,
|
||||
modifier = Modifier.align(Alignment.BottomCenter),
|
||||
isLoading = isLoading
|
||||
text = stringResource(id = R.string.create),
|
||||
size = ButtonSize.Large,
|
||||
modifierBox = Modifier
|
||||
.fillMaxWidth()
|
||||
.align(Alignment.BottomCenter)
|
||||
.windowInsetsPadding(WindowInsets.navigationBars)
|
||||
.padding(bottom = 8.dp),
|
||||
modifierButton = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 20.dp),
|
||||
loading = isLoading.value,
|
||||
enabled = innerValue.text.isNotEmpty()
|
||||
)
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
focusRequester.requestFocus()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
@ -147,43 +226,14 @@ private fun DebugCreateSpaceLevelChatToggle(isChatToggleChecked: MutableState<Bo
|
|||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(
|
||||
text = "Enable space-level chat (dev mode)",
|
||||
color = colorResource(id = com.anytypeio.anytype.ui_settings.R.color.text_primary),
|
||||
color = colorResource(id = R.color.text_primary),
|
||||
style = BodyRegular
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CreateSpaceButton(
|
||||
modifier: Modifier,
|
||||
onCreate: (Name) -> Unit,
|
||||
input: State<String>,
|
||||
isLoading: State<Boolean>
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.height(78.dp)
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
ButtonPrimaryLoading(
|
||||
onClick = { onCreate(input.value) },
|
||||
text = stringResource(id = R.string.create),
|
||||
size = ButtonSize.Large,
|
||||
modifierButton = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 20.dp)
|
||||
,
|
||||
modifierBox = Modifier
|
||||
.padding(bottom = 10.dp)
|
||||
.align(Alignment.BottomCenter)
|
||||
,
|
||||
loading = isLoading.value
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Header() {
|
||||
fun Header(isChatSpace: Boolean = false) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(48.dp)
|
||||
|
@ -191,8 +241,8 @@ fun Header() {
|
|||
) {
|
||||
Text(
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
text = stringResource(id = R.string.create_space),
|
||||
style = Title2,
|
||||
text = stringResource(id = if (isChatSpace) R.string.create_chat else R.string.create_space),
|
||||
style = Title1,
|
||||
color = colorResource(id = R.color.text_primary)
|
||||
)
|
||||
}
|
||||
|
@ -202,94 +252,65 @@ fun Header() {
|
|||
fun SpaceIcon(
|
||||
modifier: Modifier,
|
||||
spaceIconView: SpaceIconView,
|
||||
onSpaceIconClicked: () -> Unit
|
||||
onSpaceIconUploadClicked: () -> Unit,
|
||||
onSpaceIconRemoveClicked: () -> Unit,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
|
||||
val isIconMenuExpanded = remember {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
|
||||
Box(modifier = modifier.wrapContentSize()) {
|
||||
SpaceIconView(
|
||||
icon = spaceIconView,
|
||||
onSpaceIconClick = onSpaceIconClicked
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
fun SpaceNameInput(
|
||||
input: MutableState<String>,
|
||||
onActionDone: () -> Unit
|
||||
) {
|
||||
val focusRequester = FocusRequester()
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(72.dp)
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
BasicTextField(
|
||||
value = input.value,
|
||||
onValueChange = { input.value = it },
|
||||
keyboardActions = KeyboardActions(
|
||||
onDone = { onActionDone() }
|
||||
),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 20.dp, bottom = 12.dp)
|
||||
.align(Alignment.BottomStart)
|
||||
.focusRequester(focusRequester)
|
||||
,
|
||||
maxLines = 1,
|
||||
singleLine = true,
|
||||
textStyle = HeadlineHeading.copy(
|
||||
color = colorResource(id = R.color.text_primary)
|
||||
),
|
||||
cursorBrush = SolidColor(
|
||||
colorResource(id = R.color.cursor_color)
|
||||
),
|
||||
decorationBox = @Composable { innerTextField ->
|
||||
TextFieldDefaults.OutlinedTextFieldDecorationBox(
|
||||
value = input.value,
|
||||
innerTextField = innerTextField,
|
||||
singleLine = true,
|
||||
enabled = true,
|
||||
isError = false,
|
||||
placeholder = {
|
||||
Text(
|
||||
text = stringResource(R.string.space_name),
|
||||
style = HeadlineHeading
|
||||
)
|
||||
},
|
||||
colors = TextFieldDefaults.outlinedTextFieldColors(
|
||||
textColor = colorResource(id = R.color.text_primary),
|
||||
backgroundColor = Color.Transparent,
|
||||
disabledBorderColor = Color.Transparent,
|
||||
errorBorderColor = Color.Transparent,
|
||||
focusedBorderColor = Color.Transparent,
|
||||
unfocusedBorderColor = Color.Transparent,
|
||||
placeholderColor = colorResource(id = R.color.text_tertiary)
|
||||
),
|
||||
contentPadding = PaddingValues(
|
||||
start = 0.dp,
|
||||
top = 0.dp,
|
||||
end = 0.dp,
|
||||
bottom = 0.dp
|
||||
),
|
||||
border = {},
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
visualTransformation = VisualTransformation.None
|
||||
)
|
||||
onSpaceIconClick = {
|
||||
isIconMenuExpanded.value = !isIconMenuExpanded.value
|
||||
}
|
||||
)
|
||||
Text(
|
||||
color = colorResource(id = R.color.text_secondary),
|
||||
style = Caption1Regular,
|
||||
modifier = Modifier.padding(
|
||||
start = 20.dp,
|
||||
top = 11.dp
|
||||
),
|
||||
text = stringResource(id = R.string.space_name)
|
||||
)
|
||||
}
|
||||
LaunchedEffect(Unit) {
|
||||
focusRequester.requestFocus()
|
||||
DropdownMenu(
|
||||
modifier = Modifier,
|
||||
expanded = isIconMenuExpanded.value,
|
||||
offset = DpOffset(x = 0.dp, y = 6.dp),
|
||||
onDismissRequest = {
|
||||
isIconMenuExpanded.value = false
|
||||
},
|
||||
shape = RoundedCornerShape(10.dp),
|
||||
containerColor = colorResource(id = R.color.background_secondary)
|
||||
) {
|
||||
if (ActivityResultContracts.PickVisualMedia.isPhotoPickerAvailable(context)) {
|
||||
DropdownMenuItem(
|
||||
onClick = {
|
||||
onSpaceIconUploadClicked()
|
||||
isIconMenuExpanded.value = false
|
||||
},
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.profile_settings_apply_upload_image),
|
||||
style = BodyRegular,
|
||||
color = colorResource(id = R.color.text_primary)
|
||||
)
|
||||
}
|
||||
}
|
||||
if (spaceIconView is SpaceIconView.Image) {
|
||||
Divider(
|
||||
paddingStart = 0.dp,
|
||||
paddingEnd = 0.dp,
|
||||
)
|
||||
DropdownMenuItem(
|
||||
onClick = {
|
||||
isIconMenuExpanded.value = false
|
||||
onSpaceIconRemoveClicked()
|
||||
},
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.remove_image),
|
||||
style = BodyRegular,
|
||||
color = colorResource(id = R.color.text_primary)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -299,9 +320,11 @@ fun Section(
|
|||
color: Color = colorResource(id = R.color.text_secondary),
|
||||
textPaddingStart: Dp = 20.dp
|
||||
) {
|
||||
Box(modifier = Modifier
|
||||
.height(52.dp)
|
||||
.fillMaxWidth()) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(52.dp)
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
|
@ -330,4 +353,21 @@ fun UseCase() {
|
|||
}
|
||||
}
|
||||
|
||||
typealias IsSpaceLevelChatSwitchChecked = Boolean
|
||||
typealias IsSpaceLevelChatSwitchChecked = Boolean
|
||||
|
||||
@DefaultPreviews
|
||||
@Composable
|
||||
fun CreateSpaceScreenPreview() {
|
||||
val state = remember { mutableStateOf(false) }
|
||||
CreateSpaceScreen(
|
||||
spaceIconView = SpaceIconView.Placeholder(
|
||||
color = SystemColor.RED,
|
||||
name = "My Space"
|
||||
),
|
||||
onCreate = { _, _ -> },
|
||||
onSpaceIconUploadClicked = {},
|
||||
onSpaceIconRemoveClicked = {},
|
||||
isChatSpace = true,
|
||||
isLoading = state
|
||||
)
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
package com.anytypeio.anytype.ui.vault
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.material3.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.anytypeio.anytype.R
|
||||
import com.anytypeio.anytype.core_ui.common.DefaultPreviews
|
||||
import com.anytypeio.anytype.core_ui.foundation.Dragger
|
||||
import com.anytypeio.anytype.core_ui.foundation.noRippleThrottledClickable
|
||||
import com.anytypeio.anytype.core_ui.views.Caption1Regular
|
||||
import com.anytypeio.anytype.core_ui.views.Title1
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun ChooseSpaceTypeScreen(
|
||||
onCreateChatClicked: () -> Unit,
|
||||
onCreateSpaceClicked: () -> Unit,
|
||||
onDismiss: () -> Unit = {}
|
||||
) {
|
||||
val sheetState = rememberModalBottomSheetState(
|
||||
skipPartiallyExpanded = true
|
||||
)
|
||||
|
||||
ModalBottomSheet(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
onDismissRequest = onDismiss,
|
||||
sheetState = sheetState,
|
||||
containerColor = Color.Transparent,
|
||||
contentColor = Color.Transparent,
|
||||
dragHandle = null,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 8.dp)
|
||||
.background(shape = RoundedCornerShape(16.dp), color = colorResource(id = R.color.widget_background))
|
||||
) {
|
||||
Dragger(modifier = Modifier
|
||||
.align(Alignment.CenterHorizontally)
|
||||
.padding(vertical = 8.dp))
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(72.dp)
|
||||
.padding(horizontal = 16.dp)
|
||||
.noRippleThrottledClickable {
|
||||
onCreateChatClicked()
|
||||
},
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_space_type_chat),
|
||||
contentDescription = "Create Chat",
|
||||
modifier = Modifier.size(56.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.width(12.dp))
|
||||
Column {
|
||||
Text(
|
||||
text = stringResource(id = R.string.vault_create_chat),
|
||||
style = Title1,
|
||||
color = colorResource(id = R.color.text_primary)
|
||||
)
|
||||
Text(
|
||||
text = stringResource(id = R.string.vault_create_chat_description),
|
||||
style = Caption1Regular,
|
||||
color = colorResource(id = R.color.text_secondary),
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(72.dp)
|
||||
.padding(horizontal = 16.dp)
|
||||
.noRippleThrottledClickable {
|
||||
onCreateSpaceClicked()
|
||||
},
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_space_type_space),
|
||||
contentDescription = "Create Space",
|
||||
modifier = Modifier.size(56.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.width(12.dp))
|
||||
Column {
|
||||
Text(
|
||||
text = stringResource(id = R.string.vault_create_space),
|
||||
style = Title1,
|
||||
color = colorResource(id = R.color.text_primary)
|
||||
)
|
||||
Text(
|
||||
text = stringResource(id = R.string.vault_create_space_description),
|
||||
style = Caption1Regular,
|
||||
color = colorResource(id = R.color.text_secondary),
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@DefaultPreviews
|
||||
@Composable
|
||||
private fun ChooseSpaceTypeScreenPreview() {
|
||||
ChooseSpaceTypeScreen(
|
||||
onCreateChatClicked = {},
|
||||
onCreateSpaceClicked = {}
|
||||
)
|
||||
}
|
|
@ -5,14 +5,18 @@ import android.os.Bundle
|
|||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.compose.ui.platform.ViewCompositionStrategy
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.navigation.NavOptions
|
||||
import androidx.navigation.NavOptions.*
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import com.anytypeio.anytype.R
|
||||
import com.anytypeio.anytype.core_utils.ext.argOrNull
|
||||
|
@ -31,6 +35,10 @@ import com.anytypeio.anytype.ui.home.HomeScreenFragment
|
|||
import com.anytypeio.anytype.ui.multiplayer.RequestJoinSpaceFragment
|
||||
import com.anytypeio.anytype.ui.payments.MembershipFragment
|
||||
import com.anytypeio.anytype.ui.settings.typography
|
||||
import com.anytypeio.anytype.ui.spaces.CreateSpaceFragment
|
||||
import com.anytypeio.anytype.ui.spaces.CreateSpaceFragment.Companion.ARG_SPACE_TYPE
|
||||
import com.anytypeio.anytype.ui.spaces.CreateSpaceFragment.Companion.TYPE_CHAT
|
||||
import com.anytypeio.anytype.ui.spaces.CreateSpaceFragment.Companion.TYPE_SPACE
|
||||
import javax.inject.Inject
|
||||
import timber.log.Timber
|
||||
|
||||
|
@ -51,14 +59,30 @@ class VaultFragment : BaseComposeFragment() {
|
|||
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
|
||||
setContent {
|
||||
MaterialTheme(typography = typography) {
|
||||
VaultScreen(
|
||||
spaces = vm.spaces.collectAsStateWithLifecycle().value,
|
||||
onSpaceClicked = vm::onSpaceClicked,
|
||||
onCreateSpaceClicked = vm::onCreateSpaceClicked,
|
||||
onSettingsClicked = vm::onSettingsClicked,
|
||||
onOrderChanged = vm::onOrderChanged,
|
||||
profile = vm.profileView.collectAsStateWithLifecycle().value
|
||||
)
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
VaultScreen(
|
||||
spaces = vm.spaces.collectAsStateWithLifecycle().value,
|
||||
onSpaceClicked = vm::onSpaceClicked,
|
||||
onCreateSpaceClicked = vm::onChooseSpaceTypeClicked,
|
||||
onSettingsClicked = vm::onSettingsClicked,
|
||||
onOrderChanged = vm::onOrderChanged,
|
||||
profile = vm.profileView.collectAsStateWithLifecycle().value
|
||||
)
|
||||
|
||||
if (vm.showChooseSpaceType.collectAsStateWithLifecycle().value) {
|
||||
ChooseSpaceTypeScreen(
|
||||
onCreateChatClicked = {
|
||||
vm.onCreateChatClicked()
|
||||
},
|
||||
onCreateSpaceClicked = {
|
||||
vm.onCreateSpaceClicked()
|
||||
},
|
||||
onDismiss = {
|
||||
vm.onChooseSpaceTypeDismissed()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
LaunchedEffect(Unit) {
|
||||
vm.commands.collect { command -> proceed(command) }
|
||||
|
@ -100,10 +124,21 @@ class VaultFragment : BaseComposeFragment() {
|
|||
is Command.CreateNewSpace -> {
|
||||
runCatching {
|
||||
findNavController().navigate(
|
||||
R.id.actionCreateSpaceFromVault
|
||||
R.id.actionCreateSpaceFromVault,
|
||||
bundleOf(ARG_SPACE_TYPE to TYPE_SPACE)
|
||||
)
|
||||
}.onFailure {
|
||||
Timber.e(it, "Error while opening create-space screen from vault")
|
||||
Timber.e(it, "Error while opening create space screen from vault")
|
||||
}
|
||||
}
|
||||
Command.CreateChat -> {
|
||||
runCatching {
|
||||
findNavController().navigate(
|
||||
R.id.actionCreateChatFromVault,
|
||||
bundleOf(ARG_SPACE_TYPE to TYPE_CHAT)
|
||||
)
|
||||
}.onFailure {
|
||||
Timber.e(it, "Error while opening create chat screen from vault")
|
||||
}
|
||||
}
|
||||
is Command.OpenProfileSettings -> {
|
||||
|
@ -135,7 +170,7 @@ class VaultFragment : BaseComposeFragment() {
|
|||
findNavController().navigate(
|
||||
R.id.paymentsScreen,
|
||||
MembershipFragment.args(command.tierId),
|
||||
NavOptions.Builder().setLaunchSingleTop(true).build()
|
||||
Builder().setLaunchSingleTop(true).build()
|
||||
)
|
||||
}
|
||||
is Command.Deeplink.DeepLinkToObjectNotWorking -> {
|
||||
|
|
|
@ -320,11 +320,18 @@
|
|||
<action
|
||||
android:id="@+id/actionCreateSpaceFromVault"
|
||||
app:destination="@id/createSpaceScreen" />
|
||||
<action
|
||||
android:id="@+id/actionCreateChatFromVault"
|
||||
app:destination="@id/createSpaceScreen" />
|
||||
</fragment>
|
||||
|
||||
<dialog
|
||||
android:id="@+id/createSpaceScreen"
|
||||
android:name="com.anytypeio.anytype.ui.spaces.CreateSpaceFragment">
|
||||
<argument
|
||||
android:name="arg.space_type"
|
||||
android:defaultValue="space"
|
||||
app:argType="string" />
|
||||
<action
|
||||
android:id="@+id/exitToVaultAction"
|
||||
app:popUpTo="@+id/vaultScreen"
|
||||
|
|
21
core-ui/src/main/res/drawable/ic_space_type_chat.xml
Normal file
21
core-ui/src/main/res/drawable/ic_space_type_chat.xml
Normal file
|
@ -0,0 +1,21 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="56dp"
|
||||
android:height="56dp"
|
||||
android:viewportWidth="56"
|
||||
android:viewportHeight="56">
|
||||
<path
|
||||
android:pathData="M28,28m-28,0a28,28 0,1 1,56 0a28,28 0,1 1,-56 0"
|
||||
android:fillColor="#46AFFF"/>
|
||||
<path
|
||||
android:pathData="M28,14C36.837,14 44,20.044 44,27.5C44,34.956 36.837,41 28,41C27.067,41 26.152,40.931 25.263,40.802C25.01,40.765 24.752,40.821 24.537,40.958C22.361,42.344 19.694,43.345 17.111,43.771C16.205,43.921 15.744,42.832 16.367,42.157C17.128,41.334 17.824,40.486 18.47,39.625C18.825,39.151 18.67,38.479 18.176,38.154C14.418,35.684 12,31.831 12,27.5C12,20.044 19.163,14 28,14Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M28,27.5m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0"
|
||||
android:fillColor="#46BCFF"/>
|
||||
<path
|
||||
android:pathData="M21.5,27.5m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0"
|
||||
android:fillColor="#46BCFF"/>
|
||||
<path
|
||||
android:pathData="M34.5,27.5m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0"
|
||||
android:fillColor="#46BCFF"/>
|
||||
</vector>
|
12
core-ui/src/main/res/drawable/ic_space_type_space.xml
Normal file
12
core-ui/src/main/res/drawable/ic_space_type_space.xml
Normal file
File diff suppressed because one or more lines are too long
|
@ -916,7 +916,9 @@
|
|||
<string name="exiting_please_wait">Exiting... please wait</string>
|
||||
<string name="loading_please_wait">Loading... please wait</string>
|
||||
<string name="personal">Default</string>
|
||||
<string name="create_space">Create a space</string>
|
||||
<string name="create_space">Create space</string>
|
||||
<string name="create_chat">Create chat</string>
|
||||
<string name="create_space_change_icon">Change icon</string>
|
||||
|
||||
<string name="generic_error">Something went wrong. Please try again.</string>
|
||||
<string name="type">Type</string>
|
||||
|
@ -2070,4 +2072,9 @@ Please provide specific details of your needs here.</string>
|
|||
<string name="notifications_modal_success_button">Enable notifications</string>
|
||||
<string name="notifications_modal_cancel_button">Not now</string>
|
||||
|
||||
<string name="vault_create_chat">Chat</string>
|
||||
<string name="vault_create_chat_description">For real-time conversations</string>
|
||||
<string name="vault_create_space">Space</string>
|
||||
<string name="vault_create_space_description">For organized content and data</string>
|
||||
|
||||
</resources>
|
|
@ -481,6 +481,7 @@ interface MiddlewareService {
|
|||
@Throws(Exception::class)
|
||||
fun workspaceOpen(request: Rpc.Workspace.Open.Request): Rpc.Workspace.Open.Response
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun workspaceSetInfo(request: Rpc.Workspace.SetInfo.Request): Rpc.Workspace.SetInfo.Response
|
||||
|
||||
@Throws(Exception::class)
|
||||
|
|
|
@ -8,16 +8,17 @@ import com.anytypeio.anytype.analytics.base.EventsDictionary
|
|||
import com.anytypeio.anytype.analytics.base.EventsPropertiesKey
|
||||
import com.anytypeio.anytype.analytics.base.sendEvent
|
||||
import com.anytypeio.anytype.analytics.props.Props
|
||||
import com.anytypeio.anytype.core_models.Block
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.Relations
|
||||
import com.anytypeio.anytype.core_models.SystemColor
|
||||
import com.anytypeio.anytype.core_models.Url
|
||||
import com.anytypeio.anytype.core_models.primitives.Space
|
||||
import com.anytypeio.anytype.core_models.primitives.SpaceId
|
||||
import com.anytypeio.anytype.domain.base.fold
|
||||
import com.anytypeio.anytype.domain.multiplayer.SpaceViewSubscriptionContainer
|
||||
import com.anytypeio.anytype.domain.media.UploadFile
|
||||
import com.anytypeio.anytype.domain.spaces.CreateSpace
|
||||
import com.anytypeio.anytype.domain.spaces.SetSpaceDetails
|
||||
import com.anytypeio.anytype.domain.workspace.SpaceManager
|
||||
import com.anytypeio.anytype.presentation.BuildConfig
|
||||
import com.anytypeio.anytype.presentation.common.BaseViewModel
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
|
@ -29,14 +30,15 @@ class CreateSpaceViewModel(
|
|||
private val createSpace: CreateSpace,
|
||||
private val spaceManager: SpaceManager,
|
||||
private val analytics: Analytics,
|
||||
private val spaceViewContainer: SpaceViewSubscriptionContainer
|
||||
private val uploadFile: UploadFile,
|
||||
private val setSpaceDetails: SetSpaceDetails
|
||||
) : BaseViewModel() {
|
||||
|
||||
val isInProgress = MutableStateFlow(false)
|
||||
|
||||
val commands = MutableSharedFlow<Command>(replay = 0)
|
||||
|
||||
val spaceIconView : MutableStateFlow<SpaceIconView.Placeholder> = MutableStateFlow(
|
||||
val spaceIconView : MutableStateFlow<SpaceIconView> = MutableStateFlow(
|
||||
SpaceIconView.Placeholder(
|
||||
color = SystemColor.entries.random()
|
||||
)
|
||||
|
@ -50,8 +52,13 @@ class CreateSpaceViewModel(
|
|||
|
||||
val isDismissed = MutableStateFlow(false)
|
||||
|
||||
fun onCreateSpace(name: String, isSpaceLevelChatSwitchChecked: Boolean) {
|
||||
Timber.d("onCreateSpace, isSpaceLevelChatSwitchChecked: $isSpaceLevelChatSwitchChecked")
|
||||
fun onImageSelected(url: Url) {
|
||||
Timber.d("onImageSelected: $url")
|
||||
spaceIconView.value = SpaceIconView.Image(url = url)
|
||||
}
|
||||
|
||||
fun onCreateSpace(name: String, withChat: Boolean) {
|
||||
Timber.d("onCreateSpace, withChat: $withChat")
|
||||
if (isDismissed.value) {
|
||||
return
|
||||
}
|
||||
|
@ -59,55 +66,88 @@ class CreateSpaceViewModel(
|
|||
sendToast("Please wait...")
|
||||
return
|
||||
}
|
||||
val numberOfActiveSpaces = spaceViewContainer.get().filter { it.isActive }.size
|
||||
viewModelScope.launch {
|
||||
createSpace.stream(
|
||||
CreateSpace.Params(
|
||||
details = mapOf(
|
||||
Relations.NAME to name,
|
||||
Relations.ICON_OPTION to spaceIconView.value.color.index.toDouble()
|
||||
),
|
||||
shouldApplyEmptyUseCase = true,
|
||||
withChat = BuildConfig.DEBUG && isSpaceLevelChatSwitchChecked
|
||||
)
|
||||
).collect { result ->
|
||||
val params = CreateSpace.Params(
|
||||
details = mapOf(
|
||||
Relations.NAME to name,
|
||||
Relations.ICON_OPTION to when (val icon = spaceIconView.value) {
|
||||
is SpaceIconView.Placeholder -> icon.color.index.toDouble()
|
||||
else -> SystemColor.SKY.index.toDouble()
|
||||
}
|
||||
),
|
||||
shouldApplyEmptyUseCase = true,
|
||||
withChat = withChat
|
||||
)
|
||||
createSpace.stream(params = params).collect { result ->
|
||||
result.fold(
|
||||
onLoading = { isInProgress.value = true },
|
||||
onSuccess = { response ->
|
||||
val space = response.space.id
|
||||
analytics.sendEvent(
|
||||
eventName = EventsDictionary.createSpace,
|
||||
props = Props(
|
||||
mapOf(EventsPropertiesKey.route to EventsDictionary.Routes.navigation)
|
||||
)
|
||||
)
|
||||
setNewSpaceAsCurrentSpace(space)
|
||||
Timber.d("Successfully created space: $space").also {
|
||||
isInProgress.value = false
|
||||
commands.emit(
|
||||
Command.SwitchSpace(
|
||||
space = Space(space),
|
||||
startingObject = response.startingObject
|
||||
)
|
||||
)
|
||||
}
|
||||
},
|
||||
onFailure = {
|
||||
Timber.e(it, "Error while creating space").also {
|
||||
sendToast("Error while creating space, please try again.")
|
||||
isInProgress.value = false
|
||||
}
|
||||
}
|
||||
onSuccess = { onSpaceCreated(it) },
|
||||
onFailure = { onError(it) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun setNewSpaceAsCurrentSpace(space: Id) {
|
||||
spaceManager.set(space)
|
||||
private suspend fun onSpaceCreated(response: com.anytypeio.anytype.core_models.Command.CreateSpace.Result) {
|
||||
val spaceId = response.space.id
|
||||
analytics.sendEvent(
|
||||
eventName = EventsDictionary.createSpace,
|
||||
props = Props(
|
||||
mapOf(EventsPropertiesKey.route to EventsDictionary.Routes.navigation)
|
||||
)
|
||||
)
|
||||
spaceManager.set(spaceId)
|
||||
|
||||
when (val icon = spaceIconView.value) {
|
||||
is SpaceIconView.Image -> uploadAndSetIcon(
|
||||
url = icon.url,
|
||||
spaceId = spaceId,
|
||||
startingObject = response.startingObject
|
||||
)
|
||||
else -> finishCreation(spaceId, response.startingObject)
|
||||
}
|
||||
}
|
||||
|
||||
fun onSpaceIconClicked() {
|
||||
private suspend fun uploadAndSetIcon(url: Url, spaceId: Id, startingObject: Id?) {
|
||||
uploadFile.async(
|
||||
UploadFile.Params(
|
||||
path = url,
|
||||
space = Space(spaceId),
|
||||
type = Block.Content.File.Type.IMAGE,
|
||||
createTypeWidgetIfMissing = false
|
||||
)
|
||||
).fold(
|
||||
onSuccess = { file ->
|
||||
setSpaceDetails.async(
|
||||
SetSpaceDetails.Params(
|
||||
space = Space(spaceId),
|
||||
details = mapOf(Relations.ICON_IMAGE to file.id)
|
||||
)
|
||||
)
|
||||
finishCreation(spaceId, startingObject)
|
||||
},
|
||||
onFailure = { onError(it) }
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun finishCreation(spaceId: Id, startingObject: Id?) {
|
||||
Timber.d("Space created: %s", spaceId)
|
||||
isInProgress.value = false
|
||||
commands.emit(
|
||||
Command.SwitchSpace(
|
||||
space = Space(spaceId),
|
||||
startingObject = startingObject
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun onError(error: Throwable) {
|
||||
Timber.e(error, "Error creating space")
|
||||
sendToast("Error while creating space, please try again.")
|
||||
isInProgress.value = false
|
||||
}
|
||||
|
||||
fun onSpaceIconRemovedClicked() {
|
||||
proceedWithResettingRandomSpaceGradient()
|
||||
}
|
||||
|
||||
|
@ -121,7 +161,8 @@ class CreateSpaceViewModel(
|
|||
private val createSpace: CreateSpace,
|
||||
private val spaceManager: SpaceManager,
|
||||
private val analytics: Analytics,
|
||||
private val spaceViewContainer: SpaceViewSubscriptionContainer
|
||||
private val uploadFile: UploadFile,
|
||||
private val setSpaceDetails: SetSpaceDetails
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(
|
||||
|
@ -130,7 +171,8 @@ class CreateSpaceViewModel(
|
|||
createSpace = createSpace,
|
||||
spaceManager = spaceManager,
|
||||
analytics = analytics,
|
||||
spaceViewContainer = spaceViewContainer
|
||||
uploadFile = uploadFile,
|
||||
setSpaceDetails = setSpaceDetails
|
||||
) as T
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ import com.anytypeio.anytype.domain.misc.UrlBuilder
|
|||
sealed class SpaceIconView {
|
||||
data object Loading : SpaceIconView()
|
||||
data class Placeholder(
|
||||
val color: SystemColor = SystemColor.YELLOW,
|
||||
val color: SystemColor = SystemColor.SKY,
|
||||
val name: String = ""
|
||||
): SpaceIconView()
|
||||
data class Image(val url: Url) : SpaceIconView()
|
||||
|
|
|
@ -77,6 +77,7 @@ class VaultViewModel(
|
|||
|
||||
val spaces = MutableStateFlow<List<VaultSpaceView>>(emptyList())
|
||||
val commands = MutableSharedFlow<Command>(replay = 0)
|
||||
val showChooseSpaceType = MutableStateFlow(false)
|
||||
|
||||
val profileView = profileContainer.observe().map { obj ->
|
||||
AccountProfile.Data(
|
||||
|
@ -179,8 +180,30 @@ class VaultViewModel(
|
|||
viewModelScope.launch { setVaultSpaceOrder.async(params = order) }
|
||||
}
|
||||
|
||||
fun onChooseSpaceTypeClicked() {
|
||||
viewModelScope.launch {
|
||||
showChooseSpaceType.value = true
|
||||
}
|
||||
}
|
||||
|
||||
fun onCreateSpaceClicked() {
|
||||
viewModelScope.launch { commands.emit(Command.CreateNewSpace) }
|
||||
viewModelScope.launch {
|
||||
showChooseSpaceType.value = false
|
||||
commands.emit(Command.CreateNewSpace)
|
||||
}
|
||||
}
|
||||
|
||||
fun onCreateChatClicked() {
|
||||
viewModelScope.launch {
|
||||
showChooseSpaceType.value = false
|
||||
commands.emit(Command.CreateChat)
|
||||
}
|
||||
}
|
||||
|
||||
fun onChooseSpaceTypeDismissed() {
|
||||
viewModelScope.launch {
|
||||
showChooseSpaceType.value = false
|
||||
}
|
||||
}
|
||||
|
||||
fun onResume(deeplink: DeepLinkResolver.Action? = null) {
|
||||
|
@ -410,6 +433,7 @@ class VaultViewModel(
|
|||
data class EnterSpaceHomeScreen(val space: Space): Command()
|
||||
data class EnterSpaceLevelChat(val space: Space, val chat: Id): Command()
|
||||
data object CreateNewSpace: Command()
|
||||
data object CreateChat: Command()
|
||||
data object OpenProfileSettings: Command()
|
||||
|
||||
sealed class Deeplink : Command() {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue