1
0
Fork 0
mirror of https://github.com/anyproto/anytype-kotlin.git synced 2025-06-07 21:37:02 +09:00

DROID-2838 Sync status | P2P status, design update (#1574)

This commit is contained in:
Konstantin Ivanov 2024-09-24 10:44:50 +02:00 committed by GitHub
parent d02bdb5bd8
commit 30f91ccbc7
Signed by: github
GPG key ID: B5690EEEBB952194
6 changed files with 435 additions and 54 deletions

View file

@ -4,7 +4,6 @@ import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@ -231,7 +230,7 @@ private fun SpaceSyncStatusItem(
.padding(start = 16.dp)
.align(Alignment.CenterStart),
painter = networkCardSettings.icon,
contentDescription = "dfas",
contentDescription = "sync status icon",
alpha = networkCardSettings.alpha
)
Column(
@ -274,13 +273,14 @@ private fun getP2PCardSettings(
P2PStatus.NOT_CONNECTED -> {
CardSettings(
icon = painterResource(R.drawable.ic_sync_p2p_default),
mainText = stringResource(id = R.string.sync_status_p2p_connecting),
mainText = stringResource(id = R.string.sync_status_p2p),
secondaryText = stringResource(id = R.string.sync_status_p2p_not_connected)
)
}
P2PStatus.NOT_POSSIBLE, P2PStatus.RESTRICTED -> {
CardSettings(
icon = painterResource(R.drawable.ic_sync_p2p_error),
icon = painterResource(R.drawable.ic_sync_p2p_default),
mainText = stringResource(id = R.string.sync_status_p2p),
secondaryText = stringResource(id = R.string.sync_status_p2p_disabled)
)
@ -307,54 +307,78 @@ private fun getNetworkCardSettings(
error: SpaceSyncError,
syncingObjectsCounter: Long
): CardSettings {
return when (network) {
SpaceSyncNetwork.ANYTYPE -> when (syncStatus) {
SpaceSyncStatus.SYNCED -> {
CardSettings(
icon = painterResource(R.drawable.ic_sync_net_connected),
mainText = stringResource(id = R.string.sync_status_anytype_network),
secondaryText = stringResource(id = R.string.sync_status_anytype_end_to_end)
)
}
SpaceSyncStatus.SYNCING -> {
CardSettings(
icon = painterResource(R.drawable.ic_sync_net_connected),
alpha = 0.5f,
withAnimation = true,
mainText = stringResource(id = R.string.sync_status_anytype_network),
secondaryText = pluralStringResource(
id = R.plurals.sync_status_network_items,
count = syncingObjectsCounter.toInt(),
formatArgs = arrayOf(syncingObjectsCounter.toInt())
)
)
}
SpaceSyncStatus.ERROR -> {
val errorText = getErrorText(error)
CardSettings(
when (network) {
SpaceSyncNetwork.ANYTYPE -> {
if (error != SpaceSyncError.NULL) {
return CardSettings(
icon = painterResource(R.drawable.ic_sync_net_error),
mainText = stringResource(id = R.string.sync_status_anytype_network),
secondaryText = stringResource(id = errorText)
secondaryText = stringResource(id = getErrorText(error))
)
}
SpaceSyncStatus.OFFLINE -> {
CardSettings(
icon = painterResource(R.drawable.ic_sync_net_default),
mainText = stringResource(id = R.string.sync_status_anytype_network),
secondaryText = stringResource(id = R.string.sync_status_anytype_network_no_connecting)
)
}
SpaceSyncStatus.NETWORK_UPDATE_NEEDED -> {
CardSettings(
icon = painterResource(R.drawable.ic_sync_limitations),
mainText = stringResource(id = R.string.sync_status_anytype_network),
secondaryText = stringResource(id = R.string.sync_status_anytype_sync_slow)
)
return when (syncStatus) {
SpaceSyncStatus.SYNCED -> {
CardSettings(
icon = painterResource(R.drawable.ic_sync_net_connected),
mainText = stringResource(id = R.string.sync_status_anytype_network),
secondaryText = stringResource(id = R.string.sync_status_anytype_end_to_end)
)
}
SpaceSyncStatus.SYNCING -> {
CardSettings(
icon = painterResource(R.drawable.ic_sync_net_connected),
alpha = 0.5f,
withAnimation = true,
mainText = stringResource(id = R.string.sync_status_anytype_network),
secondaryText = pluralStringResource(
id = R.plurals.sync_status_network_items,
count = syncingObjectsCounter.toInt(),
formatArgs = arrayOf(syncingObjectsCounter.toInt())
)
)
}
SpaceSyncStatus.ERROR -> {
val errorText = getErrorText(error)
CardSettings(
icon = painterResource(R.drawable.ic_sync_net_error),
mainText = stringResource(id = R.string.sync_status_anytype_network),
secondaryText = stringResource(id = errorText)
)
}
SpaceSyncStatus.OFFLINE -> {
CardSettings(
icon = painterResource(R.drawable.ic_sync_net_default),
mainText = stringResource(id = R.string.sync_status_anytype_network),
secondaryText = stringResource(id = R.string.sync_status_anytype_network_no_connecting)
)
}
SpaceSyncStatus.NETWORK_UPDATE_NEEDED -> {
CardSettings(
icon = painterResource(R.drawable.ic_sync_limitations),
mainText = stringResource(id = R.string.sync_status_anytype_network),
secondaryText = stringResource(id = R.string.sync_status_anytype_sync_slow)
)
}
}
}
SpaceSyncNetwork.SELF_HOST -> {
when (syncStatus) {
if (error != SpaceSyncError.NULL) {
return CardSettings(
icon = painterResource(R.drawable.ic_sync_net_error),
mainText = stringResource(id = R.string.sync_status_self_host),
secondaryText = stringResource(id = getErrorText(error))
)
}
return when (syncStatus) {
SpaceSyncStatus.SYNCED -> {
CardSettings(
icon = painterResource(R.drawable.ic_sync_self_connected),
@ -402,8 +426,18 @@ private fun getNetworkCardSettings(
}
}
}
SpaceSyncNetwork.LOCAL_ONLY -> {
CardSettings(
if (error != SpaceSyncError.NULL) {
return CardSettings(
icon = painterResource(R.drawable.ic_sync_net_error),
mainText = stringResource(id = R.string.sync_status_local_only_title),
secondaryText = stringResource(id = getErrorText(error))
)
}
return CardSettings(
icon = painterResource(R.drawable.ic_sync_local_only),
mainText = stringResource(id = R.string.sync_status_local_only_title),
secondaryText = stringResource(id = R.string.sync_status_data_backup)
@ -557,7 +591,7 @@ fun SpaceSyncStatusPreview10() {
P2PStatusItem(p2pStatus = p2pStatus)
}
@Preview(name = "P2PNotPossible", showBackground = true)
@Preview(name = "P2PNotPossible and P2PRestricted", showBackground = true)
@Composable
fun SpaceSyncStatusPreview11() {
val p2pStatus = P2PStatusUpdate.Update(

View file

@ -1721,9 +1721,9 @@ Please provide specific details of your needs here.</string>
<item quantity="one">%1$d device connected</item>
<item quantity="other">%1$d devices connected</item>
</plurals>
<string name="sync_status_p2p_connecting">P2P Connecting...</string>
<string name="sync_status_p2p_disabled">Disabled</string>
<string name="sync_status_p2p_disabled">Connection not possible</string>
<string name="sync_status_p2p_restricted">Restricted. Check device settings.</string>
<string name="sync_status_p2p_not_connected">Not connected</string>
<string name="sync_status_anytype_network">Anytype Network</string>
<string name="sync_status_anytype_end_to_end">End-to-end encrypted</string>

View file

@ -23,6 +23,8 @@ dependencies {
testImplementation libs.junit
testImplementation libs.kotlinTest
testImplementation libs.mockitoKotlin
testImplementation libs.coroutineTesting
testImplementation libs.turbine
}
android {

View file

@ -25,8 +25,8 @@ class SyncAndP2PStatusEventsStoreImpl(
private val dispatcher: CoroutineDispatcher = Dispatchers.IO
) : SyncAndP2PStatusEventsStore {
private val _p2pStatus = MutableStateFlow<MutableMap<Id, P2PStatusUpdate>>(mutableMapOf())
private val _syncStatus = MutableStateFlow<MutableMap<Id, SpaceSyncUpdate>>(mutableMapOf())
private val _p2pStatus = MutableStateFlow<Map<Id, P2PStatusUpdate>>(emptyMap())
private val _syncStatus = MutableStateFlow<Map<Id, SpaceSyncUpdate>>(emptyMap())
override val p2pStatus: Flow<Map<Id, P2PStatusUpdate>> get() = _p2pStatus
override val syncStatus: Flow<Map<Id, SpaceSyncUpdate>> get() = _syncStatus
@ -62,8 +62,7 @@ class SyncAndP2PStatusEventsStoreImpl(
devicesCounter = update.devicesCounter
)
_p2pStatus.update { currentMap ->
currentMap[update.spaceId] = p2pUpdate
currentMap
currentMap + (update.spaceId to p2pUpdate)
}
}
@ -77,8 +76,7 @@ class SyncAndP2PStatusEventsStoreImpl(
)
_syncStatus.update { currentMap ->
currentMap[update.id] = syncUpdate
currentMap
currentMap + (update.id to syncUpdate)
}
}
}

View file

@ -0,0 +1,50 @@
package com.anytypeio.anytype.middleware.interactor
import app.cash.turbine.test
import app.cash.turbine.turbineScope
import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
import org.junit.Test
class EventHandlerChannelImplTest {
@Test
fun `channel should receive events`() = runTest {
turbineScope {
val event1 = anytype.Event(
messages = listOf(
anytype.Event.Message(
p2pStatusUpdate = anytype.Event.P2PStatus.Update(
spaceId = "spaceId1",
status = anytype.Event.P2PStatus.Status.Connected,
devicesCounter = 145
)
)
)
)
val event2 = anytype.Event(
messages = listOf(
anytype.Event.Message(
spaceSyncStatusUpdate = anytype.Event.Space.SyncStatus.Update(
id = "id1",
status = anytype.Event.Space.Status.Syncing,
network = anytype.Event.Space.Network.Anytype,
syncingObjectsCounter = 999
)
)
)
)
val eventHandlerChannelImpl = EventHandlerChannelImpl()
eventHandlerChannelImpl.flow().test {
eventHandlerChannelImpl.emit(event1)
assertEquals(event1, awaitItem())
eventHandlerChannelImpl.emit(event2)
assertEquals(event2, awaitItem())
}
}
}
}

View file

@ -0,0 +1,297 @@
package com.anytypeio.anytype.middleware.interactor
import app.cash.turbine.test
import app.cash.turbine.turbineScope
import com.anytypeio.anytype.core_models.multiplayer.P2PStatus
import com.anytypeio.anytype.core_models.multiplayer.P2PStatusUpdate
import com.anytypeio.anytype.core_models.multiplayer.SpaceSyncError
import com.anytypeio.anytype.core_models.multiplayer.SpaceSyncNetwork
import com.anytypeio.anytype.core_models.multiplayer.SpaceSyncStatus
import com.anytypeio.anytype.core_models.multiplayer.SpaceSyncUpdate
import kotlin.test.assertEquals
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
class SyncAndP2PStatusEventsStoreImplTest {
private val dispatcher = StandardTestDispatcher(name = "Default test dispatcher")
private val spaceId1 = "spaceId1"
private val spaceId2 = "spaceId2"
private val spaceId3 = "spaceId3"
private lateinit var channel: EventHandlerChannelImpl
@Before
fun setUp() {
channel = EventHandlerChannelImpl()
}
@Test
fun `should update spaces p2p statuses`() = runTest(dispatcher) {
turbineScope {
val store = SyncAndP2PStatusEventsStoreImpl(
channel = channel,
dispatcher = dispatcher,
scope = backgroundScope
)
val initialEvent = anytype.Event(
messages = listOf(
anytype.Event.Message(
p2pStatusUpdate = anytype.Event.P2PStatus.Update(
spaceId = spaceId1,
)
),
anytype.Event.Message(
p2pStatusUpdate = anytype.Event.P2PStatus.Update(
spaceId = spaceId2,
status = anytype.Event.P2PStatus.Status.Connected,
devicesCounter = 145
)
)
)
)
val event1 = anytype.Event(
messages = listOf(
anytype.Event.Message(
p2pStatusUpdate = anytype.Event.P2PStatus.Update(
spaceId = spaceId2,
status = anytype.Event.P2PStatus.Status.Connected,
devicesCounter = 233
)
)
)
)
val event2 = anytype.Event(
messages = listOf(
anytype.Event.Message(
p2pStatusUpdate = anytype.Event.P2PStatus.Update(
spaceId = spaceId1,
status = anytype.Event.P2PStatus.Status.NotPossible
)
)
)
)
store.start()
dispatcher.scheduler.advanceTimeBy(100)
store.p2pStatus.test {
val firstItem = awaitItem()
assertEquals(mapOf(), firstItem)
channel.emit(initialEvent)
assertEquals(
expected = mapOf(
spaceId1 to P2PStatusUpdate.Update(
spaceId = spaceId1,
status = P2PStatus.NOT_CONNECTED,
devicesCounter = 0
)
),
actual = awaitItem()
)
assertEquals(
expected = mapOf(
spaceId1 to P2PStatusUpdate.Update(
spaceId = spaceId1,
status = P2PStatus.NOT_CONNECTED,
devicesCounter = 0
),
spaceId2 to P2PStatusUpdate.Update(
spaceId = spaceId2,
status = P2PStatus.CONNECTED,
devicesCounter = 145
)
),
actual = awaitItem()
)
channel.emit(event1)
assertEquals(
expected = mapOf(
spaceId1 to P2PStatusUpdate.Update(
spaceId = spaceId1,
status = P2PStatus.NOT_CONNECTED,
devicesCounter = 0
),
spaceId2 to P2PStatusUpdate.Update(
spaceId = spaceId2,
status = P2PStatus.CONNECTED,
devicesCounter = 233
)
),
actual = awaitItem()
)
channel.emit(event2)
assertEquals(
expected = mapOf(
spaceId1 to P2PStatusUpdate.Update(
spaceId = spaceId1,
status = P2PStatus.NOT_POSSIBLE,
devicesCounter = 0
),
spaceId2 to P2PStatusUpdate.Update(
spaceId = spaceId2,
status = P2PStatus.CONNECTED,
devicesCounter = 233
)
),
actual = awaitItem()
)
}
}
}
@Test
fun `should update spaces sync statuses`() = runTest(dispatcher) {
turbineScope {
val store = SyncAndP2PStatusEventsStoreImpl(
channel = channel,
dispatcher = dispatcher,
scope = backgroundScope
)
val initialEvent = anytype.Event(
messages = listOf(
anytype.Event.Message(
spaceSyncStatusUpdate = anytype.Event.Space.SyncStatus.Update(
id = spaceId1,
status = anytype.Event.Space.Status.Offline,
network = anytype.Event.Space.Network.Anytype
)
),
anytype.Event.Message(
spaceSyncStatusUpdate = anytype.Event.Space.SyncStatus.Update(
id = spaceId2,
status = anytype.Event.Space.Status.Syncing,
network = anytype.Event.Space.Network.SelfHost
)
)
)
)
val event1 = anytype.Event(
messages = listOf(
anytype.Event.Message(
spaceSyncStatusUpdate = anytype.Event.Space.SyncStatus.Update(
id = spaceId2,
status = anytype.Event.Space.Status.Error,
network = anytype.Event.Space.Network.SelfHost,
error = anytype.Event.Space.SyncError.StorageLimitExceed
)
),
anytype.Event.Message(
spaceSyncStatusUpdate = anytype.Event.Space.SyncStatus.Update(
id = spaceId3,
status = anytype.Event.Space.Status.Synced,
network = anytype.Event.Space.Network.Anytype,
syncingObjectsCounter = 2345
)
)
)
)
store.start()
dispatcher.scheduler.advanceTimeBy(100)
store.syncStatus.test {
val firstItem = awaitItem()
assertEquals(mapOf(), firstItem)
channel.emit(initialEvent)
assertEquals(
expected = mapOf(
spaceId1 to SpaceSyncUpdate.Update(
id = spaceId1,
status = SpaceSyncStatus.OFFLINE,
network = SpaceSyncNetwork.ANYTYPE,
syncingObjectsCounter = 0,
error = SpaceSyncError.NULL
)
),
actual = awaitItem()
)
assertEquals(
expected = mapOf(
spaceId1 to SpaceSyncUpdate.Update(
id = spaceId1,
status = SpaceSyncStatus.OFFLINE,
network = SpaceSyncNetwork.ANYTYPE,
syncingObjectsCounter = 0,
error = SpaceSyncError.NULL
),
spaceId2 to SpaceSyncUpdate.Update(
id = spaceId2,
status = SpaceSyncStatus.SYNCING,
network = SpaceSyncNetwork.SELF_HOST,
syncingObjectsCounter = 0,
error = SpaceSyncError.NULL
)
),
actual = awaitItem()
)
channel.emit(event1)
assertEquals(
expected = mapOf(
spaceId1 to SpaceSyncUpdate.Update(
id = spaceId1,
status = SpaceSyncStatus.OFFLINE,
network = SpaceSyncNetwork.ANYTYPE,
syncingObjectsCounter = 0,
error = SpaceSyncError.NULL
),
spaceId2 to SpaceSyncUpdate.Update(
id = spaceId2,
status = SpaceSyncStatus.ERROR,
network = SpaceSyncNetwork.SELF_HOST,
syncingObjectsCounter = 0,
error = SpaceSyncError.STORAGE_LIMIT_EXCEED
)
),
actual = awaitItem()
)
assertEquals(
expected = mapOf(
spaceId1 to SpaceSyncUpdate.Update(
id = spaceId1,
status = SpaceSyncStatus.OFFLINE,
network = SpaceSyncNetwork.ANYTYPE,
syncingObjectsCounter = 0,
error = SpaceSyncError.NULL
),
spaceId2 to SpaceSyncUpdate.Update(
id = spaceId2,
status = SpaceSyncStatus.ERROR,
network = SpaceSyncNetwork.SELF_HOST,
syncingObjectsCounter = 0,
error = SpaceSyncError.STORAGE_LIMIT_EXCEED
),
spaceId3 to SpaceSyncUpdate.Update(
id = spaceId3,
status = SpaceSyncStatus.SYNCED,
network = SpaceSyncNetwork.ANYTYPE,
syncingObjectsCounter = 2345,
error = SpaceSyncError.NULL
)
),
actual = awaitItem()
)
}
}
}
}