Skip to content

Commit cde7767

Browse files
DROID-4182 1-on-1 Spaces Prototype (#2922)
Co-authored-by: Evgenii Kozlov <[email protected]>
1 parent 9a98bcf commit cde7767

File tree

9 files changed

+96
-5
lines changed

9 files changed

+96
-5
lines changed

app/src/main/java/com/anytypeio/anytype/ui/profile/ParticipantFragment.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,14 @@ class ParticipantFragment: BaseBottomSheetComposeFragment() {
6565
Timber.w("Error while opening participant screen")
6666
}
6767
}
68+
69+
is ParticipantViewModel.Command.SwitchToVault -> {
70+
runCatching {
71+
findNavController().popBackStack(R.id.vaultScreen, false)
72+
}.onFailure {
73+
Timber.e(it, "Error while opening space")
74+
}
75+
}
6876
}
6977
}
7078

core-models/src/main/java/com/anytypeio/anytype/core_models/ImportUseCase.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@ enum class SpaceCreationUseCase(val value: Int) {
1111
GET_STARTED_MOBILE(4),
1212
CHAT_SPACE(5),
1313
DATA_SPACE_MOBILE(6),
14+
ONE_TO_ONE_SPACE(7),
1415
}

core-models/src/main/java/com/anytypeio/anytype/core_models/Relations.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ object Relations {
9999
const val PARTICIPANT_STATUS = "participantStatus"
100100
const val PARTICIPANT_PERMISSIONS = "participantPermissions"
101101
const val SPACE_ACCESS_TYPE = "spaceAccessType"
102+
const val SPACE_DASHBOARD_ID = "spaceDashboardId"
103+
const val ONE_TO_ONE_IDENTITY = "oneToOneIdentity"
102104
const val IDENTITY = "identity"
103105
const val GLOBAL_NAME = "globalName"
104106
const val READERS_LIMIT = "readersLimit"

core-models/src/main/java/com/anytypeio/anytype/core_models/multiplayer/Multiplayer.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ enum class SpaceUxType(val code: Int) {
6363
DATA(1),
6464
STREAM(2),
6565
CHAT(3),
66+
ONE_TO_ONE(4)
6667
}
6768

6869
sealed class SpaceInviteError : Exception() {

core-ui/src/main/java/com/anytypeio/anytype/core_ui/features/profile/ParticipantScreen.kt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ import com.anytypeio.anytype.presentation.profile.ParticipantEvent
4242
import com.anytypeio.anytype.presentation.profile.ParticipantViewModel.UiParticipantScreenState
4343
import com.anytypeio.anytype.presentation.profile.ProfileIconView
4444
import coil3.compose.AsyncImage
45+
import com.anytypeio.anytype.core_ui.views.ButtonSecondaryLoading
46+
import com.anytypeio.anytype.core_ui.views.ButtonSize
4547

4648
@OptIn(ExperimentalMaterial3Api::class)
4749
@Composable
@@ -136,6 +138,19 @@ fun ParticipantScreen(
136138
description = uiState.description!!
137139
)
138140
}
141+
if (!uiState.isOwner) {
142+
Spacer(modifier = Modifier.height(24.dp))
143+
ButtonSecondaryLoading(
144+
text = stringResource(R.string.participant_btn_connect),
145+
onClick = { onEvent(ParticipantEvent.OnConnectClicked) },
146+
enabled = !uiState.isConnecting,
147+
size = ButtonSize.Large,
148+
loading = uiState.isConnecting,
149+
modifierButton = Modifier
150+
.fillMaxWidth()
151+
.padding(horizontal = 24.dp)
152+
)
153+
}
139154
val h = spacer + 16.dp
140155
Spacer(
141156
modifier = Modifier.height(h)

localization/src/main/res/values/strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2364,5 +2364,6 @@ Please provide specific details of your needs here.</string>
23642364
<string name="toast_moved_to_bin">Object moved to bin.</string>
23652365
<string name="media_object_in_bin">This object is in the bin. Restore it?</string>
23662366
<string name="toast_restored">Object restored.</string>
2367+
<string name="participant_btn_connect">Connect</string>
23672368

23682369
</resources>

middleware/src/main/java/com/anytypeio/anytype/middleware/mappers/ToMiddlewareModelMappers.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -666,6 +666,7 @@ fun SpaceCreationUseCase.toMiddlewareModel(): Rpc.Object.ImportUseCase.Request.U
666666
SpaceCreationUseCase.GET_STARTED_MOBILE -> Rpc.Object.ImportUseCase.Request.UseCase.GET_STARTED_MOBILE
667667
SpaceCreationUseCase.CHAT_SPACE -> Rpc.Object.ImportUseCase.Request.UseCase.CHAT_SPACE
668668
SpaceCreationUseCase.DATA_SPACE_MOBILE -> Rpc.Object.ImportUseCase.Request.UseCase.DATA_SPACE_MOBILE
669+
SpaceCreationUseCase.ONE_TO_ONE_SPACE -> Rpc.Object.ImportUseCase.Request.UseCase.CHAT_SPACE
669670
}
670671

671672
fun NotificationState.toMiddlewareModel(): Rpc.PushNotification.Mode = when (this) {

presentation/src/main/java/com/anytypeio/anytype/presentation/profile/ParticipantEvent.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ package com.anytypeio.anytype.presentation.profile
33
sealed class ParticipantEvent{
44
data object OnDismiss: ParticipantEvent()
55
data object OnCardClicked: ParticipantEvent()
6+
data object OnConnectClicked: ParticipantEvent()
67
}

presentation/src/main/java/com/anytypeio/anytype/presentation/profile/ParticipantViewModel.kt

Lines changed: 66 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,19 @@ import com.anytypeio.anytype.analytics.base.EventsDictionary
88
import com.anytypeio.anytype.analytics.base.sendEvent
99
import com.anytypeio.anytype.core_models.Id
1010
import com.anytypeio.anytype.core_models.Relations
11+
import com.anytypeio.anytype.core_models.SpaceCreationUseCase
1112
import com.anytypeio.anytype.core_models.membership.MembershipStatus
13+
import com.anytypeio.anytype.core_models.multiplayer.SpaceAccessType
14+
import com.anytypeio.anytype.core_models.multiplayer.SpaceUxType
1215
import com.anytypeio.anytype.core_models.primitives.SpaceId
1316
import com.anytypeio.anytype.domain.config.ConfigStorage
1417
import com.anytypeio.anytype.domain.library.StoreSearchByIdsParams
1518
import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer
1619
import com.anytypeio.anytype.domain.misc.UrlBuilder
1720
import com.anytypeio.anytype.domain.multiplayer.UserPermissionProvider
21+
import com.anytypeio.anytype.domain.base.suspendFold
1822
import com.anytypeio.anytype.domain.primitives.FieldParser
23+
import com.anytypeio.anytype.domain.spaces.CreateSpace
1924
import com.anytypeio.anytype.presentation.membership.provider.MembershipProvider
2025
import com.anytypeio.anytype.presentation.search.ObjectSearchConstants
2126
import javax.inject.Inject
@@ -31,7 +36,8 @@ class ParticipantViewModel(
3136
private val subscriptionContainer: StorelessSubscriptionContainer,
3237
private val fieldsParser: FieldParser,
3338
private val userPermissionProvider: UserPermissionProvider,
34-
private val configStorage: ConfigStorage
39+
private val configStorage: ConfigStorage,
40+
private val createSpace: CreateSpace
3541
) : ViewModel() {
3642

3743
val uiState = MutableStateFlow<UiParticipantScreenState>(UiParticipantScreenState.EMPTY)
@@ -116,6 +122,56 @@ class ParticipantViewModel(
116122
}
117123
}
118124
}
125+
126+
ParticipantEvent.OnConnectClicked -> {
127+
proceedWithCreatingOneToOneSpace()
128+
}
129+
}
130+
}
131+
132+
private fun proceedWithCreatingOneToOneSpace() {
133+
val state = uiState.value
134+
val participantIdentity = state.identity
135+
if (participantIdentity.isNullOrBlank()) {
136+
viewModelScope.launch {
137+
commands.emit(Command.Toast.Error("Unable to connect: participant identity not found"))
138+
}
139+
return
140+
}
141+
142+
viewModelScope.launch {
143+
// Set loading state
144+
uiState.value = state.copy(isConnecting = true)
145+
146+
// Create the 1-on-1 space
147+
val params = CreateSpace.Params(
148+
details = mapOf(
149+
Relations.ONE_TO_ONE_IDENTITY to participantIdentity,
150+
Relations.SPACE_UX_TYPE to SpaceUxType.ONE_TO_ONE.code.toDouble(),
151+
Relations.SPACE_ACCESS_TYPE to SpaceAccessType.SHARED.code.toDouble(),
152+
Relations.SPACE_DASHBOARD_ID to "chat" // Chat dashboard ID
153+
),
154+
useCase = SpaceCreationUseCase.ONE_TO_ONE_SPACE
155+
)
156+
157+
createSpace.async(params).suspendFold(
158+
onSuccess = { response ->
159+
// Reset loading state
160+
uiState.value = state.copy(isConnecting = false)
161+
// Switch to the new space
162+
commands.emit(Command.SwitchToVault(response.space.id))
163+
},
164+
onFailure = { error ->
165+
// Reset loading state
166+
uiState.value = state.copy(isConnecting = false)
167+
// Show error
168+
commands.emit(
169+
Command.Toast.Error(
170+
error.message ?: "Failed to create 1-on-1 space"
171+
)
172+
)
173+
}
174+
)
119175
}
120176
}
121177

@@ -124,13 +180,15 @@ class ParticipantViewModel(
124180
val icon: ProfileIconView,
125181
val description: String? = null,
126182
val identity: String? = null,
127-
val isOwner: Boolean
183+
val isOwner: Boolean,
184+
val isConnecting: Boolean = false
128185
) {
129186
companion object {
130187
val EMPTY = UiParticipantScreenState(
131188
name = "",
132189
icon = ProfileIconView.Loading,
133-
isOwner = false
190+
isOwner = false,
191+
isConnecting = false
134192
)
135193
}
136194
}
@@ -147,6 +205,7 @@ class ParticipantViewModel(
147205

148206
data object Dismiss : Command()
149207
data object OpenSettingsProfile : Command()
208+
data class SwitchToVault(val spaceId: Id) : Command()
150209
}
151210

152211
companion object {
@@ -161,7 +220,8 @@ class ParticipantViewModel(
161220
private val subscriptionContainer: StorelessSubscriptionContainer,
162221
private val fieldsParser: FieldParser,
163222
private val userPermissionProvider: UserPermissionProvider,
164-
private val configStorage: ConfigStorage
223+
private val configStorage: ConfigStorage,
224+
private val createSpace: CreateSpace
165225
) : ViewModelProvider.Factory {
166226
@Suppress("UNCHECKED_CAST")
167227
override fun <T : ViewModel> create(modelClass: Class<T>): T {
@@ -173,7 +233,8 @@ class ParticipantViewModel(
173233
subscriptionContainer = subscriptionContainer,
174234
fieldsParser = fieldsParser,
175235
userPermissionProvider = userPermissionProvider,
176-
configStorage = configStorage
236+
configStorage = configStorage,
237+
createSpace = createSpace
177238
) as T
178239
}
179240
}

0 commit comments

Comments
 (0)