diff --git a/app/src/main/java/com/anytypeio/anytype/di/main/EventModule.kt b/app/src/main/java/com/anytypeio/anytype/di/main/EventModule.kt index 1478b4ba42..a1c70e30ce 100644 --- a/app/src/main/java/com/anytypeio/anytype/di/main/EventModule.kt +++ b/app/src/main/java/com/anytypeio/anytype/di/main/EventModule.kt @@ -17,8 +17,11 @@ import com.anytypeio.anytype.domain.event.interactor.EventChannel import com.anytypeio.anytype.domain.search.SubscriptionEventChannel import com.anytypeio.anytype.domain.status.ThreadStatusChannel import com.anytypeio.anytype.data.auth.event.FileLimitsRemoteChannel +import com.anytypeio.anytype.data.auth.event.NotificationsDateChannel +import com.anytypeio.anytype.data.auth.event.NotificationsRemoteChannel import com.anytypeio.anytype.domain.workspace.EventProcessChannel import com.anytypeio.anytype.domain.workspace.FileLimitsEventChannel +import com.anytypeio.anytype.domain.workspace.NotificationsChannel import com.anytypeio.anytype.middleware.EventProxy import com.anytypeio.anytype.middleware.interactor.* import dagger.Binds @@ -146,6 +149,24 @@ object EventModule { channel: EventProcessRemoteChannel ): EventProcessChannel = EventProcessDateChannel(channel = channel) + @JvmStatic + @Provides + @Singleton + fun provideNotificationsRemoteChannel( + proxy: EventProxy + ): NotificationsRemoteChannel = NotificationsMiddlewareChannel( + eventsProxy = proxy + ) + + @JvmStatic + @Provides + @Singleton + fun provideNotificationsChannel( + channel: NotificationsRemoteChannel + ): NotificationsChannel = NotificationsDateChannel( + channel = channel + ) + @Module interface Bindings { diff --git a/core-models/src/main/java/com/anytypeio/anytype/core_models/ImportErrorCode.kt b/core-models/src/main/java/com/anytypeio/anytype/core_models/ImportErrorCode.kt new file mode 100644 index 0000000000..3a96d47cf1 --- /dev/null +++ b/core-models/src/main/java/com/anytypeio/anytype/core_models/ImportErrorCode.kt @@ -0,0 +1,13 @@ +package com.anytypeio.anytype.core_models + +enum class ImportErrorCode(val code: Int) { + NULL(0), + UNKNOWN_ERROR(1), + BAD_INPUT(2), + INTERNAL_ERROR(3), + NO_OBJECTS_TO_IMPORT(5), + IMPORT_IS_CANCELED(6), + LIMIT_OF_ROWS_OR_RELATIONS_EXCEEDED(7), + FILE_LOAD_ERROR(8), + INSUFFICIENT_PERMISSIONS(9); +} \ No newline at end of file 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 new file mode 100644 index 0000000000..e0a5d6758b --- /dev/null +++ b/core-models/src/main/java/com/anytypeio/anytype/core_models/Notification.kt @@ -0,0 +1,81 @@ +package com.anytypeio.anytype.core_models + +import com.anytypeio.anytype.core_models.multiplayer.SpaceMemberPermissions +import com.anytypeio.anytype.core_models.primitives.SpaceId + +data class Notification( + val id: Id, + val createTime: Long, + val status: NotificationStatus, + val isLocal: Boolean, + val payload: NotificationPayload, + val space: SpaceId, + val aclHeadId: String +) { + + sealed class Event { + data class Update( + val notification: Notification? + ) : Event() + + data class Send( + val notification: Notification? + ) : Event() + } +} + +sealed class NotificationPayload { + data class GalleryImport( + val processId: String, + val errorCode: ImportErrorCode, + val spaceId: SpaceId, + val name: String + ) : NotificationPayload() + + data class RequestToJoin( + val spaceId: SpaceId, + val identity: String, + val identityName: String, + val identityIcon: String + ) : NotificationPayload() + + data class ParticipantRequestApproved( + val spaceId: SpaceId, + val permissions: SpaceMemberPermissions + ) : NotificationPayload() + + data class RequestToLeave( + val spaceId: SpaceId, + 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 + ) : NotificationPayload() + + data class ParticipantPermissionsChange( + val spaceId: SpaceId, + val permissions: SpaceMemberPermissions + ) : NotificationPayload() + + data class Unsupported(val type: String) : NotificationPayload() + + object Test : NotificationPayload() +} + +enum class NotificationStatus { + CREATED, SHOWN, READ, REPLIED +} + +enum class NotificationActionType { + CLOSE +} \ No newline at end of file diff --git a/data/src/main/java/com/anytypeio/anytype/data/auth/event/NotificationsRemoteChannel.kt b/data/src/main/java/com/anytypeio/anytype/data/auth/event/NotificationsRemoteChannel.kt new file mode 100644 index 0000000000..3eeead12a7 --- /dev/null +++ b/data/src/main/java/com/anytypeio/anytype/data/auth/event/NotificationsRemoteChannel.kt @@ -0,0 +1,18 @@ +package com.anytypeio.anytype.data.auth.event + +import com.anytypeio.anytype.core_models.Notification +import com.anytypeio.anytype.domain.workspace.NotificationsChannel +import kotlinx.coroutines.flow.Flow + +interface NotificationsRemoteChannel { + fun observe(): Flow> +} + +class NotificationsDateChannel( + private val channel: NotificationsRemoteChannel +) : NotificationsChannel { + + override fun observe(): Flow> { + return channel.observe() + } +} \ No newline at end of file diff --git a/domain/src/main/java/com/anytypeio/anytype/domain/workspace/NotificationsChannel.kt b/domain/src/main/java/com/anytypeio/anytype/domain/workspace/NotificationsChannel.kt new file mode 100644 index 0000000000..f7e06a6988 --- /dev/null +++ b/domain/src/main/java/com/anytypeio/anytype/domain/workspace/NotificationsChannel.kt @@ -0,0 +1,8 @@ +package com.anytypeio.anytype.domain.workspace + +import com.anytypeio.anytype.core_models.Notification +import kotlinx.coroutines.flow.Flow + +interface NotificationsChannel { + fun observe(): Flow> +} \ No newline at end of file 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 new file mode 100644 index 0000000000..b78a4c6951 --- /dev/null +++ b/middleware/src/main/java/com/anytypeio/anytype/middleware/interactor/NotificationsMiddlewareChannel.kt @@ -0,0 +1,38 @@ +package com.anytypeio.anytype.middleware.interactor + +import com.anytypeio.anytype.core_models.Notification +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.mapNotNull + +class NotificationsMiddlewareChannel( + private val eventsProxy: EventProxy +) : NotificationsRemoteChannel { + + override fun observe(): Flow> { + return eventsProxy.flow() + .mapNotNull { emission -> + emission.messages.mapNotNull { message -> + when { + message.notificationUpdate != null -> { + val event = message.notificationUpdate + checkNotNull(event) + Notification.Event.Update( + notification = event.notification?.toCoreModel() + ) + } + message.notificationSend != null -> { + val event = message.notificationSend + checkNotNull(event) + Notification.Event.Send( + notification = event.notification?.toCoreModel() + ) + } + else -> null + } + } + } + } +} \ No newline at end of file diff --git a/middleware/src/main/java/com/anytypeio/anytype/middleware/mappers/Alias.kt b/middleware/src/main/java/com/anytypeio/anytype/middleware/mappers/Alias.kt index 0ca175ce0a..a2a9b83e31 100644 --- a/middleware/src/main/java/com/anytypeio/anytype/middleware/mappers/Alias.kt +++ b/middleware/src/main/java/com/anytypeio/anytype/middleware/mappers/Alias.kt @@ -75,3 +75,8 @@ typealias MProcess = anytype.Model.Process typealias MProcessType = anytype.Model.Process.Type typealias MProcessState = anytype.Model.Process.State typealias MProcessProgress = anytype.Model.Process.Progress + +typealias MNotification = anytype.model.Notification +typealias MNotificationActionType = anytype.model.Notification.ActionType +typealias MNotificationStatus = anytype.model.Notification.Status +typealias MImportErrorCode = anytype.model.Import.ErrorCode 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 68658b20da..ca7ba81f51 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 @@ -3,6 +3,7 @@ package com.anytypeio.anytype.middleware.mappers import anytype.ResponseEvent import anytype.Rpc import anytype.model.Account +import anytype.model.ParticipantPermissions import anytype.model.Restrictions import com.anytypeio.anytype.core_models.AccountStatus import com.anytypeio.anytype.core_models.Block @@ -24,9 +25,14 @@ 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 import com.anytypeio.anytype.core_models.NodeUsage import com.anytypeio.anytype.core_models.NodeUsageInfo +import com.anytypeio.anytype.core_models.Notification +import com.anytypeio.anytype.core_models.NotificationActionType +import com.anytypeio.anytype.core_models.NotificationPayload +import com.anytypeio.anytype.core_models.NotificationStatus import com.anytypeio.anytype.core_models.ObjectOrder import com.anytypeio.anytype.core_models.ObjectType import com.anytypeio.anytype.core_models.ObjectTypeIds @@ -36,6 +42,8 @@ import com.anytypeio.anytype.core_models.Relation import com.anytypeio.anytype.core_models.RelationFormat import com.anytypeio.anytype.core_models.RelationLink import com.anytypeio.anytype.core_models.SpaceUsage +import com.anytypeio.anytype.core_models.multiplayer.SpaceMemberPermissions +import com.anytypeio.anytype.core_models.primitives.SpaceId import com.anytypeio.anytype.core_models.restrictions.DataViewRestriction import com.anytypeio.anytype.core_models.restrictions.DataViewRestrictions import com.anytypeio.anytype.core_models.restrictions.ObjectRestriction @@ -780,7 +788,7 @@ fun List.toCoreModel(): String { return GsonBuilder().setPrettyPrinting().create().toJson(this) } -fun Account.Info.config() : Config = Config( +fun Account.Info.config(): Config = Config( home = homeObjectId, profile = profileObjectId, gateway = gatewayUrl, @@ -826,7 +834,7 @@ fun MProcessType.toCoreModel(): Process.Type { MProcessType.Export -> Process.Type.EXPORT MProcessType.SaveFile -> Process.Type.SAVE_FILE MProcessType.RecoverAccount -> Process.Type.RECOVER_ACCOUNT - MProcessType.Migration -> Process.Type.MIGRATION + MProcessType.Migration -> Process.Type.MIGRATION } } @@ -846,4 +854,110 @@ fun MProcessProgress.toCoreModel(): Process.Progress { done = done, message = message ) -} \ No newline at end of file +} + +//region IMPORT +fun MImportErrorCode.toCoreModel(): ImportErrorCode { + return when (this) { + MImportErrorCode.NULL -> ImportErrorCode.NULL + MImportErrorCode.UNKNOWN_ERROR -> ImportErrorCode.UNKNOWN_ERROR + MImportErrorCode.BAD_INPUT -> ImportErrorCode.BAD_INPUT + MImportErrorCode.INTERNAL_ERROR -> ImportErrorCode.INTERNAL_ERROR + MImportErrorCode.NO_OBJECTS_TO_IMPORT -> ImportErrorCode.NO_OBJECTS_TO_IMPORT + MImportErrorCode.IMPORT_IS_CANCELED -> ImportErrorCode.IMPORT_IS_CANCELED + MImportErrorCode.LIMIT_OF_ROWS_OR_RELATIONS_EXCEEDED -> ImportErrorCode.LIMIT_OF_ROWS_OR_RELATIONS_EXCEEDED + MImportErrorCode.FILE_LOAD_ERROR -> ImportErrorCode.FILE_LOAD_ERROR + MImportErrorCode.INSUFFICIENT_PERMISSIONS -> ImportErrorCode.INSUFFICIENT_PERMISSIONS + } +} +//endregion + +//region NOTIFICATIONS +fun MNotificationStatus.toCoreModel(): NotificationStatus { + return when (this) { + MNotificationStatus.Created -> NotificationStatus.CREATED + MNotificationStatus.Shown -> NotificationStatus.SHOWN + MNotificationStatus.Read -> NotificationStatus.READ + MNotificationStatus.Replied -> NotificationStatus.REPLIED + } +} + +fun MNotificationActionType.toCoreModel(): NotificationActionType { + return when (this) { + MNotificationActionType.CLOSE -> NotificationActionType.CLOSE + } +} + +fun MParticipantPermission.toCore(): SpaceMemberPermissions { + return when (this) { + ParticipantPermissions.Reader -> SpaceMemberPermissions.READER + ParticipantPermissions.Writer -> SpaceMemberPermissions.WRITER + ParticipantPermissions.Owner -> SpaceMemberPermissions.OWNER + ParticipantPermissions.NoPermissions -> SpaceMemberPermissions.NO_PERMISSIONS + } +} + +fun MNotification.toCoreModel(): Notification { + return Notification( + id = id, + createTime = createTime, + status = status.toCoreModel(), + isLocal = isLocal, + space = SpaceId(space), + aclHeadId = aclHeadId, + payload = when { + participantPermissionsChange != null -> { + NotificationPayload.ParticipantPermissionsChange( + spaceId = SpaceId(participantPermissionsChange!!.spaceId), + permissions = participantPermissionsChange!!.permissions.toCore() + ) + } + requestToJoin != null -> { + NotificationPayload.RequestToJoin( + spaceId = SpaceId(requestToJoin!!.spaceId), + identity = requestToJoin!!.identity, + identityName = requestToJoin!!.identityName, + identityIcon = requestToJoin!!.identityIcon + ) + } + requestToLeave != null -> { + NotificationPayload.RequestToLeave( + spaceId = SpaceId(requestToLeave!!.spaceId), + identity = requestToLeave!!.identity, + identityName = requestToLeave!!.identityName, + identityIcon = requestToLeave!!.identityIcon + ) + } + participantRequestApproved != null -> { + NotificationPayload.ParticipantRequestApproved( + spaceId = SpaceId(participantRequestApproved!!.spaceId), + permissions = participantRequestApproved!!.permissions.toCore() + ) + } + participantRemove != null -> { + NotificationPayload.ParticipantRemove( + identity = participantRemove!!.identity, + identityName = participantRemove!!.identityName, + identityIcon = participantRemove!!.identityIcon, + spaceId = SpaceId(participantRemove!!.spaceId) + ) + } + participantRequestDecline != null -> { + NotificationPayload.ParticipantRequestDecline( + spaceId = SpaceId(participantRequestDecline!!.spaceId) + ) + } + galleryImport != null -> { + NotificationPayload.GalleryImport( + processId = galleryImport!!.processId, + errorCode = galleryImport!!.errorCode.toCoreModel(), + spaceId = SpaceId(galleryImport!!.spaceId), + name = galleryImport!!.name + ) + } + test != null -> NotificationPayload.Test + else -> NotificationPayload.Unsupported("Unsupported notification payload :${this.javaClass.simpleName}") + }, + ) +} +//endregion \ No newline at end of file