diff --git a/app/src/main/java/com/anytypeio/anytype/app/DefaultFeatureToggles.kt b/app/src/main/java/com/anytypeio/anytype/app/DefaultFeatureToggles.kt
index 7aa3f129cc..4cc444dab3 100644
--- a/app/src/main/java/com/anytypeio/anytype/app/DefaultFeatureToggles.kt
+++ b/app/src/main/java/com/anytypeio/anytype/app/DefaultFeatureToggles.kt
@@ -16,8 +16,7 @@ class DefaultFeatureToggles @Inject constructor(
override val isLogFromGoProcess =
BuildConfig.LOG_FROM_MW_LIBRARY && buildProvider.isDebug()
- override val isLogMiddlewareInteraction =
- BuildConfig.LOG_MW_INTERACTION && buildProvider.isDebug()
+ override val isLogMiddlewareInteraction = BuildConfig.LOG_MW_INTERACTION && buildProvider.isDebug()
override val excludeThreadStatusLogging: Boolean = true
diff --git a/core-models/src/main/java/com/anytypeio/anytype/core_models/Notification.kt b/core-models/src/main/java/com/anytypeio/anytype/core_models/Notification.kt
index ecbac08944..3590bf343c 100644
--- a/core-models/src/main/java/com/anytypeio/anytype/core_models/Notification.kt
+++ b/core-models/src/main/java/com/anytypeio/anytype/core_models/Notification.kt
@@ -27,7 +27,7 @@ data class Notification(
sealed class NotificationPayload {
data class GalleryImport(
- val processId: String,
+ val processId: Id,
val errorCode: ImportErrorCode,
val spaceId: SpaceId,
val name: String
@@ -35,6 +35,15 @@ sealed class NotificationPayload {
data class RequestToJoin(
val spaceId: SpaceId,
+ val spaceName: String,
+ val identity: Id,
+ val identityName: String,
+ val identityIcon: String
+ ) : NotificationPayload()
+
+ data class RequestToLeave(
+ val spaceId: SpaceId,
+ val spaceName: String,
val identity: String,
val identityName: String,
val identityIcon: String
@@ -42,29 +51,27 @@ sealed class NotificationPayload {
data class ParticipantRequestApproved(
val spaceId: SpaceId,
+ val spaceName: String,
val permissions: SpaceMemberPermissions
) : NotificationPayload()
- data class RequestToLeave(
+
+ data class ParticipantRemove(
val spaceId: SpaceId,
+ val spaceName: String,
val identity: String,
val identityName: String,
val identityIcon: String
) : NotificationPayload()
- data class ParticipantRemove(
- val identity: String,
- val identityName: String,
- val identityIcon: String,
- val spaceId: SpaceId
- ) : NotificationPayload()
-
data class ParticipantRequestDecline(
- val spaceId: SpaceId
+ val spaceId: SpaceId,
+ val spaceName: String
) : NotificationPayload()
data class ParticipantPermissionsChange(
val spaceId: SpaceId,
+ val spaceName: String,
val permissions: SpaceMemberPermissions
) : NotificationPayload()
diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/notifications/MemberNotifications.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/notifications/MemberNotifications.kt
new file mode 100644
index 0000000000..8f71819200
--- /dev/null
+++ b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/notifications/MemberNotifications.kt
@@ -0,0 +1,80 @@
+package com.anytypeio.anytype.core_ui.notifications
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.colorResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.anytypeio.anytype.core_ui.R
+import com.anytypeio.anytype.core_ui.views.Caption1Regular
+
+@Composable
+fun MemberRequestApprovedNotification(
+ spaceName: String,
+ isReadOnly: Boolean
+) {
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .background(
+ color = colorResource(id = R.color.background_primary),
+ )
+ .padding(horizontal = 20.dp, vertical = 16.dp)
+ ) {
+ val placeholder = stringResource(id = R.string.untitled)
+ val msg = if (isReadOnly)
+ stringResource(
+ id = R.string.multiplayer_notification_member_request_approved_with_read_only_rights,
+ spaceName.ifEmpty { placeholder }
+ )
+ else
+ stringResource(
+ id = R.string.multiplayer_notification_member_request_approved_with_read_only_rights,
+ spaceName.ifEmpty { placeholder }
+ )
+ Text(
+ text = msg,
+ modifier = Modifier
+ .align(Alignment.CenterStart),
+ color = colorResource(id = R.color.text_secondary),
+ style = Caption1Regular,
+ overflow = TextOverflow.Ellipsis
+ )
+ }
+}
+
+@Composable
+fun MemberSpaceRemovedNotification() {
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .background(color = colorResource(id = R.color.background_primary),)
+ .padding(horizontal = 20.dp, vertical = 16.dp)
+ ) {
+ Text(
+ text = stringResource(id = R.string.multiplayer_notification_member_removed_from_space),
+ modifier = Modifier
+ .align(Alignment.CenterStart),
+ color = colorResource(id = R.color.text_secondary),
+ style = Caption1Regular,
+ overflow = TextOverflow.Ellipsis
+ )
+ }
+}
+
+@Composable
+@Preview
+fun MemberRequestApprovedWithAccessRightsNotificationPreview() {
+ MemberRequestApprovedNotification(
+ spaceName = "Art historians",
+ isReadOnly = true
+ )
+}
\ No newline at end of file
diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/notifications/NotificationWidgets.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/notifications/NotificationWidgets.kt
index 982af3648a..2cc18c5e70 100644
--- a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/notifications/NotificationWidgets.kt
+++ b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/notifications/NotificationWidgets.kt
@@ -1,32 +1,46 @@
package com.anytypeio.anytype.core_ui.notifications
import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
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.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Text
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.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.anytypeio.anytype.core_models.ImportErrorCode
-import com.anytypeio.anytype.core_models.ImportErrorCode.*
+import com.anytypeio.anytype.core_models.ImportErrorCode.BAD_INPUT
+import com.anytypeio.anytype.core_models.ImportErrorCode.FILE_LOAD_ERROR
+import com.anytypeio.anytype.core_models.ImportErrorCode.IMPORT_IS_CANCELED
+import com.anytypeio.anytype.core_models.ImportErrorCode.INSUFFICIENT_PERMISSIONS
+import com.anytypeio.anytype.core_models.ImportErrorCode.INTERNAL_ERROR
+import com.anytypeio.anytype.core_models.ImportErrorCode.LIMIT_OF_ROWS_OR_RELATIONS_EXCEEDED
+import com.anytypeio.anytype.core_models.ImportErrorCode.NO_OBJECTS_TO_IMPORT
+import com.anytypeio.anytype.core_models.ImportErrorCode.NULL
+import com.anytypeio.anytype.core_models.ImportErrorCode.UNKNOWN_ERROR
import com.anytypeio.anytype.core_models.primitives.SpaceId
import com.anytypeio.anytype.core_ui.R
import com.anytypeio.anytype.core_ui.foundation.AlertConfig
import com.anytypeio.anytype.core_ui.foundation.AlertIcon
import com.anytypeio.anytype.core_ui.foundation.GRADIENT_TYPE_GREEN
import com.anytypeio.anytype.core_ui.foundation.GRADIENT_TYPE_RED
+import com.anytypeio.anytype.core_ui.foundation.noRippleClickable
import com.anytypeio.anytype.core_ui.views.BodyCalloutRegular
import com.anytypeio.anytype.core_ui.views.ButtonPrimary
import com.anytypeio.anytype.core_ui.views.ButtonSecondary
import com.anytypeio.anytype.core_ui.views.ButtonSize
+import com.anytypeio.anytype.core_ui.views.Caption1Regular
import com.anytypeio.anytype.core_ui.views.HeadlineSubheading
import com.anytypeio.anytype.presentation.notifications.NotificationsScreenState
@@ -66,7 +80,29 @@ fun NotificationsScreen(
onButtonClick = onErrorButtonClick
)
}
-
+ is NotificationsScreenState.Multiplayer.RequestToJoin -> {
+ OwnerUserRequestToJoin(
+ name = state.identityName,
+ spaceName = state.spaceName,
+ onManageClicked = {}
+ )
+ }
+ is NotificationsScreenState.Multiplayer.RequestToLeave -> {
+ OwnerUserRequestToLeave(
+ name = state.name,
+ spaceName = state.spaceName,
+ onManageClicked = {}
+ )
+ }
+ is NotificationsScreenState.Multiplayer.MemberRequestApproved -> {
+ MemberRequestApprovedNotification(
+ spaceName = state.spaceName,
+ isReadOnly = state.isReadOnly
+ )
+ }
+ is NotificationsScreenState.Multiplayer.MemberSpaceRemove -> {
+ MemberSpaceRemovedNotification()
+ }
NotificationsScreenState.Hidden -> {}
}
}
@@ -204,4 +240,64 @@ fun NotificationsScreenPreviewError() {
onActionButtonClick = {},
onErrorButtonClick = {}
)
+}
+
+@Composable
+fun UserJoinedSpaceWithAccessRightsNotification(
+ name: String,
+ spaceName: String,
+ onManageClicked: () -> Unit,
+ isReadOnly: Boolean
+) {
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .background(
+ color = colorResource(id = R.color.background_notification_primary),
+ shape = RoundedCornerShape(8.dp)
+ )
+ .padding(horizontal = 20.dp, vertical = 16.dp)
+ .noRippleClickable { onManageClicked() }
+ ) {
+ Box(
+ modifier = Modifier
+ .size(32.dp)
+ .background(
+ color = Color.Red,
+ shape = CircleShape
+ )
+ )
+ val placeholder = stringResource(id = R.string.untitled)
+ val msg = if (isReadOnly)
+ stringResource(
+ id = R.string.multiplayer_notification_member_joined_space_with_read_only_rights,
+ name.ifEmpty { placeholder },
+ spaceName.ifEmpty { placeholder }
+ )
+ else
+ stringResource(
+ id = R.string.multiplayer_notification_member_joined_space_with_read_only_rights,
+ name.ifEmpty { placeholder },
+ spaceName.ifEmpty { placeholder }
+ )
+ Text(
+ text = msg,
+ modifier = Modifier
+ .align(Alignment.CenterStart)
+ .padding(start = 44.dp),
+ color = colorResource(id = R.color.text_secondary),
+ style = Caption1Regular
+ )
+ }
+}
+
+@Composable
+@Preview
+private fun UserJoinedSpaceWithAccessRightsNotificationPreview() {
+ UserJoinedSpaceWithAccessRightsNotification(
+ name = "Carl Einstein",
+ spaceName = "Art historians",
+ isReadOnly = true,
+ onManageClicked = {}
+ )
}
\ No newline at end of file
diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/notifications/OwnerNotifications.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/notifications/OwnerNotifications.kt
new file mode 100644
index 0000000000..255e2f6762
--- /dev/null
+++ b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/notifications/OwnerNotifications.kt
@@ -0,0 +1,136 @@
+package com.anytypeio.anytype.core_ui.notifications
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.material.Text
+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.stringResource
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.anytypeio.anytype.core_ui.R
+import com.anytypeio.anytype.core_ui.foundation.noRippleClickable
+import com.anytypeio.anytype.core_ui.views.Caption1Medium
+import com.anytypeio.anytype.core_ui.views.Caption1Regular
+
+@Composable
+fun OwnerUserRequestToJoin(
+ name: String,
+ spaceName: String,
+ onManageClicked: () -> Unit
+) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .background(
+ color = colorResource(id = R.color.background_primary)
+ )
+ .padding(horizontal = 20.dp, vertical = 16.dp)
+ .noRippleClickable { onManageClicked() },
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Box(
+ modifier = Modifier
+ .size(32.dp)
+ .background(
+ color = Color.Red,
+ shape = CircleShape
+ )
+ )
+ val placeholder = stringResource(id = R.string.untitled)
+ Text(
+ text = stringResource(
+ id = R.string.multiplayer_notification_member_user_sends_join_request,
+ name.ifEmpty { placeholder },
+ spaceName.ifEmpty { placeholder }
+ ),
+ modifier = Modifier
+ .padding(start = 12.dp)
+ .weight(1f),
+ color = colorResource(id = R.color.text_secondary),
+ style = Caption1Regular
+ )
+ Text(
+ text = stringResource(id = R.string.multiplayer_notification_view_request),
+ modifier = Modifier
+ .padding(start = 12.dp),
+ color = colorResource(id = R.color.text_primary),
+ style = Caption1Medium
+ )
+ }
+}
+
+@Composable
+fun OwnerUserRequestToLeave(
+ name: String,
+ spaceName: String,
+ onManageClicked: () -> Unit
+) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .background(
+ color = colorResource(id = R.color.background_primary)
+ )
+ .padding(horizontal = 20.dp, vertical = 16.dp)
+ .noRippleClickable { onManageClicked() },
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Box(
+ modifier = Modifier
+ .size(32.dp)
+ .background(
+ color = Color.Red,
+ shape = CircleShape
+ )
+ )
+ val placeholder = stringResource(id = R.string.untitled)
+ Text(
+ text = stringResource(
+ id = R.string.multiplayer_notification_member_user_sends_leave_request,
+ name.ifEmpty { placeholder },
+ spaceName.ifEmpty { placeholder }
+ ),
+ modifier = Modifier
+ .padding(start = 12.dp)
+ .weight(1f),
+ color = colorResource(id = R.color.text_secondary),
+ style = Caption1Regular
+ )
+ Text(
+ text = stringResource(id = R.string.multiplayer_notification_view_request),
+ modifier = Modifier
+ .padding(start = 12.dp),
+ color = colorResource(id = R.color.text_primary),
+ style = Caption1Medium
+ )
+ }
+}
+
+@Composable
+@Preview
+private fun OwnerUserRequestToJoinPreview() {
+ OwnerUserRequestToJoin(
+ name = "Carl Einstein",
+ spaceName = "Art historians",
+ onManageClicked = {}
+ )
+}
+
+@Composable
+@Preview
+private fun OwnerUserRequestToLeavePreview() {
+ OwnerUserRequestToLeave(
+ name = "Aby Warburg",
+ spaceName = "Art historians",
+ onManageClicked = {}
+ )
+}
\ No newline at end of file
diff --git a/core-ui/src/main/res/values-night/colors.xml b/core-ui/src/main/res/values-night/colors.xml
index 4f0e4cbf6a..906d1a606d 100644
--- a/core-ui/src/main/res/values-night/colors.xml
+++ b/core-ui/src/main/res/values-night/colors.xml
@@ -10,6 +10,7 @@
#000000
#000000
+ #FFFFFF
#1F1E1D
#24DAD7CA
diff --git a/core-ui/src/main/res/values/colors.xml b/core-ui/src/main/res/values/colors.xml
index 6719598ea8..5002cbddc2 100644
--- a/core-ui/src/main/res/values/colors.xml
+++ b/core-ui/src/main/res/values/colors.xml
@@ -117,6 +117,7 @@
#FFFFFF
#FFFFFF
+ #000000
#FFFFFF
#144F4F4F
diff --git a/localization/src/main/res/values/strings.xml b/localization/src/main/res/values/strings.xml
index 3b0c46f391..76bcfb6b05 100644
--- a/localization/src/main/res/values/strings.xml
+++ b/localization/src/main/res/values/strings.xml
@@ -1354,6 +1354,15 @@
Space deleted
Space not found
Read-only access. Contact space owner for changes.
+ %1$s requested to join the %2$s space.
+ %1$s requested to leave the %2$s space.
+ New join request
+ %1$s joined %2$s space with read-only access rights
+ %1$s joined %2$s space with edit access rights
+ Your request to join the %1$s space has been approved with read-only access rights. The space will be available on your device soon.
+ Your request to join the %1$s space has been approved with edit access rights. The space will be available on your device soon.
+ You have been removed from the space, or the space was deleted by the owner.
+ View
diff --git a/middleware/src/main/java/com/anytypeio/anytype/middleware/interactor/NotificationsMiddlewareChannel.kt b/middleware/src/main/java/com/anytypeio/anytype/middleware/interactor/NotificationsMiddlewareChannel.kt
index b78a4c6951..6eba3c3921 100644
--- a/middleware/src/main/java/com/anytypeio/anytype/middleware/interactor/NotificationsMiddlewareChannel.kt
+++ b/middleware/src/main/java/com/anytypeio/anytype/middleware/interactor/NotificationsMiddlewareChannel.kt
@@ -5,6 +5,7 @@ import com.anytypeio.anytype.data.auth.event.NotificationsRemoteChannel
import com.anytypeio.anytype.middleware.EventProxy
import com.anytypeio.anytype.middleware.mappers.toCoreModel
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.mapNotNull
class NotificationsMiddlewareChannel(
@@ -33,6 +34,6 @@ class NotificationsMiddlewareChannel(
else -> null
}
}
- }
+ }.filter { events -> events.isNotEmpty() }
}
}
\ No newline at end of file
diff --git a/middleware/src/main/java/com/anytypeio/anytype/middleware/mappers/ToCoreModelMappers.kt b/middleware/src/main/java/com/anytypeio/anytype/middleware/mappers/ToCoreModelMappers.kt
index ca7ba81f51..aea539757e 100644
--- a/middleware/src/main/java/com/anytypeio/anytype/middleware/mappers/ToCoreModelMappers.kt
+++ b/middleware/src/main/java/com/anytypeio/anytype/middleware/mappers/ToCoreModelMappers.kt
@@ -23,7 +23,6 @@ import com.anytypeio.anytype.core_models.DVViewerCardSize
import com.anytypeio.anytype.core_models.DVViewerRelation
import com.anytypeio.anytype.core_models.DVViewerType
import com.anytypeio.anytype.core_models.Event
-import com.anytypeio.anytype.core_models.Process
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.ImportErrorCode
import com.anytypeio.anytype.core_models.ManifestInfo
@@ -38,6 +37,7 @@ import com.anytypeio.anytype.core_models.ObjectType
import com.anytypeio.anytype.core_models.ObjectTypeIds
import com.anytypeio.anytype.core_models.ObjectView
import com.anytypeio.anytype.core_models.Payload
+import com.anytypeio.anytype.core_models.Process
import com.anytypeio.anytype.core_models.Relation
import com.anytypeio.anytype.core_models.RelationFormat
import com.anytypeio.anytype.core_models.RelationLink
@@ -909,12 +909,14 @@ fun MNotification.toCoreModel(): Notification {
participantPermissionsChange != null -> {
NotificationPayload.ParticipantPermissionsChange(
spaceId = SpaceId(participantPermissionsChange!!.spaceId),
+ spaceName = participantPermissionsChange!!.spaceName,
permissions = participantPermissionsChange!!.permissions.toCore()
)
}
requestToJoin != null -> {
NotificationPayload.RequestToJoin(
spaceId = SpaceId(requestToJoin!!.spaceId),
+ spaceName = requestToJoin!!.spaceName,
identity = requestToJoin!!.identity,
identityName = requestToJoin!!.identityName,
identityIcon = requestToJoin!!.identityIcon
@@ -923,6 +925,7 @@ fun MNotification.toCoreModel(): Notification {
requestToLeave != null -> {
NotificationPayload.RequestToLeave(
spaceId = SpaceId(requestToLeave!!.spaceId),
+ spaceName = requestToLeave!!.spaceName,
identity = requestToLeave!!.identity,
identityName = requestToLeave!!.identityName,
identityIcon = requestToLeave!!.identityIcon
@@ -931,7 +934,8 @@ fun MNotification.toCoreModel(): Notification {
participantRequestApproved != null -> {
NotificationPayload.ParticipantRequestApproved(
spaceId = SpaceId(participantRequestApproved!!.spaceId),
- permissions = participantRequestApproved!!.permissions.toCore()
+ permissions = participantRequestApproved!!.permissions.toCore(),
+ spaceName = participantRequestApproved!!.spaceName
)
}
participantRemove != null -> {
@@ -939,12 +943,14 @@ fun MNotification.toCoreModel(): Notification {
identity = participantRemove!!.identity,
identityName = participantRemove!!.identityName,
identityIcon = participantRemove!!.identityIcon,
- spaceId = SpaceId(participantRemove!!.spaceId)
+ spaceId = SpaceId(participantRemove!!.spaceId),
+ spaceName = participantRemove!!.spaceName
)
}
participantRequestDecline != null -> {
NotificationPayload.ParticipantRequestDecline(
- spaceId = SpaceId(participantRequestDecline!!.spaceId)
+ spaceId = SpaceId(participantRequestDecline!!.spaceId),
+ spaceName = participantRequestDecline!!.spaceName
)
}
galleryImport != null -> {
diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/main/MainViewModel.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/main/MainViewModel.kt
index 3a4fefe7e0..d318c300d9 100644
--- a/presentation/src/main/java/com/anytypeio/anytype/presentation/main/MainViewModel.kt
+++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/main/MainViewModel.kt
@@ -96,13 +96,31 @@ class MainViewModel(
}
private suspend fun handleNotification(event: Notification.Event) {
- val payload = event.notification?.payload
- if (payload is NotificationPayload.GalleryImport) {
- delay(DELAY_BEFORE_SHOWING_NOTIFICATION_SCREEN)
- commands.emit(Command.Notifications)
- } else {
- viewModelScope.launch {
- toasts.emit(payload.toString())
+ when (val payload = event.notification?.payload) {
+ is NotificationPayload.GalleryImport -> {
+ delay(DELAY_BEFORE_SHOWING_NOTIFICATION_SCREEN)
+ commands.emit(Command.Notifications)
+ }
+ is NotificationPayload.RequestToJoin -> {
+ delay(DELAY_BEFORE_SHOWING_NOTIFICATION_SCREEN)
+ commands.emit(Command.Notifications)
+ }
+ is NotificationPayload.RequestToLeave -> {
+ delay(DELAY_BEFORE_SHOWING_NOTIFICATION_SCREEN)
+ commands.emit(Command.Notifications)
+ }
+ is NotificationPayload.ParticipantRequestApproved -> {
+ delay(DELAY_BEFORE_SHOWING_NOTIFICATION_SCREEN)
+ commands.emit(Command.Notifications)
+ }
+ is NotificationPayload.ParticipantRemove -> {
+ delay(DELAY_BEFORE_SHOWING_NOTIFICATION_SCREEN)
+ commands.emit(Command.Notifications)
+ }
+ else -> {
+ viewModelScope.launch {
+ toasts.emit(payload.toString())
+ }
}
}
}
diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/notifications/NotificationsProvider.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/notifications/NotificationsProvider.kt
index 8f46252b19..1b2b54dba5 100644
--- a/presentation/src/main/java/com/anytypeio/anytype/presentation/notifications/NotificationsProvider.kt
+++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/notifications/NotificationsProvider.kt
@@ -4,6 +4,7 @@ import com.anytypeio.anytype.core_models.Notification
import com.anytypeio.anytype.domain.account.AwaitAccountStartManager
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
import com.anytypeio.anytype.domain.workspace.NotificationsChannel
+import com.anytypeio.anytype.presentation.BuildConfig
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -13,6 +14,7 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.launch
+import timber.log.Timber
interface NotificationsProvider {
val events: StateFlow>
@@ -30,6 +32,9 @@ interface NotificationsProvider {
init {
scope.launch(dispatchers.io) {
observe().collect { events ->
+ if (BuildConfig.DEBUG) {
+ Timber.d("New notifications: $events")
+ }
_events.value = events
}
}
diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/notifications/NotificationsViewModel.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/notifications/NotificationsViewModel.kt
index 7f958f357f..b8816351db 100644
--- a/presentation/src/main/java/com/anytypeio/anytype/presentation/notifications/NotificationsViewModel.kt
+++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/notifications/NotificationsViewModel.kt
@@ -4,6 +4,7 @@ import androidx.lifecycle.viewModelScope
import com.anytypeio.anytype.analytics.base.Analytics
import com.anytypeio.anytype.analytics.base.EventsDictionary
import com.anytypeio.anytype.analytics.base.sendEvent
+import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.ImportErrorCode
import com.anytypeio.anytype.core_models.Notification
import com.anytypeio.anytype.core_models.NotificationPayload
@@ -38,17 +39,51 @@ class NotificationsViewModel(
private fun handleNotification(event: Notification.Event) {
val payload = event.notification?.payload
- if (payload is NotificationPayload.GalleryImport) {
- if (payload.errorCode != ImportErrorCode.NULL) {
- state.value = NotificationsScreenState.GalleryInstalledError(
- errorCode = payload.errorCode
+ when (payload) {
+ is NotificationPayload.GalleryImport -> {
+ if (payload.errorCode != ImportErrorCode.NULL) {
+ state.value = NotificationsScreenState.GalleryInstalledError(
+ errorCode = payload.errorCode
+ )
+ } else {
+ state.value = NotificationsScreenState.GalleryInstalled(
+ spaceId = payload.spaceId,
+ galleryName = payload.name
+ )
+ }
+ }
+ is NotificationPayload.RequestToJoin -> {
+ state.value = NotificationsScreenState.Multiplayer.RequestToJoin(
+ space = payload.spaceId,
+ spaceName = payload.spaceName,
+ identity = payload.identity,
+ identityName = payload.identityName
)
- } else {
- state.value = NotificationsScreenState.GalleryInstalled(
- spaceId = payload.spaceId,
- galleryName = payload.name
+ }
+ is NotificationPayload.RequestToLeave -> {
+ state.value = NotificationsScreenState.Multiplayer.RequestToLeave(
+ space = payload.spaceId,
+ spaceName = payload.spaceName,
+ identity = payload.identity,
+ name = payload.identityName
)
}
+ is NotificationPayload.ParticipantRequestApproved -> {
+ state.value = NotificationsScreenState.Multiplayer.MemberRequestApproved(
+ space = payload.spaceId,
+ spaceName = payload.spaceName,
+ isReadOnly = !payload.permissions.isOwnerOrEditor()
+ )
+ }
+ is NotificationPayload.ParticipantRemove -> {
+ state.value = NotificationsScreenState.Multiplayer.MemberSpaceRemove(
+ spaceName = payload.spaceName,
+ identityName = payload.identityName
+ )
+ }
+ else -> {
+ Timber.w("Ignored notification: $payload")
+ }
}
}
@@ -80,7 +115,7 @@ class NotificationsViewModel(
}
sealed class Command {
- object Dismiss : Command()
+ data object Dismiss : Command()
data class NavigateToSpace(val spaceId: SpaceId) : Command()
}
}
@@ -88,7 +123,7 @@ class NotificationsViewModel(
sealed class NotificationsScreenState {
- object Hidden : NotificationsScreenState()
+ data object Hidden : NotificationsScreenState()
data class GalleryInstalled(
val spaceId: SpaceId,
val galleryName: String
@@ -96,4 +131,31 @@ sealed class NotificationsScreenState {
data class GalleryInstalledError(
val errorCode: ImportErrorCode
) : NotificationsScreenState()
+ sealed class Multiplayer : NotificationsScreenState() {
+ // Owner
+ data class RequestToJoin(
+ val space: SpaceId,
+ val spaceName: String,
+ val identity: Id,
+ val identityName: String
+ ) : Multiplayer()
+ // Owner
+ data class RequestToLeave(
+ val space: SpaceId,
+ val spaceName: String,
+ val identity: Id,
+ val name: String
+ ) : Multiplayer()
+ // Member
+ data class MemberRequestApproved(
+ val space: SpaceId,
+ val spaceName: String,
+ val isReadOnly: Boolean
+ ) : Multiplayer()
+ // Member
+ data class MemberSpaceRemove(
+ val spaceName: String,
+ val identityName: String
+ ) : Multiplayer()
+ }
}
\ No newline at end of file