diff --git a/examples/texteditor/src/main/java/com/example/texteditor/EditorViewModel.kt b/examples/texteditor/src/main/java/com/example/texteditor/EditorViewModel.kt index 74a9ddf36..4dc6f4b2e 100644 --- a/examples/texteditor/src/main/java/com/example/texteditor/EditorViewModel.kt +++ b/examples/texteditor/src/main/java/com/example/texteditor/EditorViewModel.kt @@ -29,8 +29,8 @@ class EditorViewModel(private val client: Client) : ViewModel(), YorkieEditText. private val _content = MutableSharedFlow() val content = _content.asSharedFlow() - private val _textChangeInfos = MutableSharedFlow>() - val textChangeInfos = _textChangeInfos.asSharedFlow() + private val _textOpInfos = MutableSharedFlow>() + val textOpInfos = _textOpInfos.asSharedFlow() val removedPeers = client.events.filterIsInstance() .map { it.result } @@ -59,19 +59,18 @@ class EditorViewModel(private val client: Client) : ViewModel(), YorkieEditText. if (event is Document.Event.Snapshot) { syncText() } else if (event is Document.Event.RemoteChange) { - emitTextChanges(event.changeInfos) + emitTextOpInfos(event.changeInfo) } } } } - private suspend fun emitTextChanges(changeInfos: List) { - val clientID = client.requireClientId() - changeInfos.filterNot { it.actorID == clientID } - .flatMap { (_, ops, actor) -> - ops.filterIsInstance().map { op -> actor to op } - }.forEach { - _textChangeInfos.emit(it) + private suspend fun emitTextOpInfos(changeInfo: Document.Event.ChangeInfo) { + if (changeInfo.actorID == client.requireClientId()) return + + changeInfo.operations.filterIsInstance() + .forEach { opInfo -> + _textOpInfos.emit(changeInfo.actorID to opInfo) } } diff --git a/examples/texteditor/src/main/java/com/example/texteditor/MainActivity.kt b/examples/texteditor/src/main/java/com/example/texteditor/MainActivity.kt index a60b063d2..5b8062eb7 100644 --- a/examples/texteditor/src/main/java/com/example/texteditor/MainActivity.kt +++ b/examples/texteditor/src/main/java/com/example/texteditor/MainActivity.kt @@ -51,7 +51,7 @@ class MainActivity : AppCompatActivity() { } launch { - viewModel.textChangeInfos.collect { (actor, opInfo) -> + viewModel.textOpInfos.collect { (actor, opInfo) -> when (opInfo) { is OperationInfo.EditOpInfo -> opInfo.handleContentChange() is OperationInfo.SelectOpInfo -> opInfo.handleSelectChange(actor) diff --git a/yorkie/src/androidTest/kotlin/dev/yorkie/core/ClientTest.kt b/yorkie/src/androidTest/kotlin/dev/yorkie/core/ClientTest.kt index bb2ed880f..aa4c8807f 100644 --- a/yorkie/src/androidTest/kotlin/dev/yorkie/core/ClientTest.kt +++ b/yorkie/src/androidTest/kotlin/dev/yorkie/core/ClientTest.kt @@ -109,15 +109,15 @@ class ClientTest { val localSetEvent = assertIs(document1Events.first()) val localSetOperation = assertIs( - localSetEvent.changeInfos.first().operations.first(), + localSetEvent.changeInfo.operations.first(), ) assertEquals("k1", localSetOperation.key) - assertEquals("$", localSetEvent.changeInfos.first().operations.first().path) + assertEquals("$", localSetEvent.changeInfo.operations.first().path) document1Events.clear() val remoteSetEvent = assertIs(document2Events.first()) val remoteSetOperation = assertIs( - remoteSetEvent.changeInfos.first().operations.first(), + remoteSetEvent.changeInfo.operations.first(), ) assertEquals("k1", remoteSetOperation.key) document2Events.clear() @@ -145,13 +145,13 @@ class ClientTest { val remoteRemoveEvent = assertIs(document1Events.first()) val remoteRemoveOperation = assertIs( - remoteRemoveEvent.changeInfos.first().operations.first(), + remoteRemoveEvent.changeInfo.operations.first(), ) assertEquals(localSetOperation.executedAt, remoteRemoveOperation.executedAt) val localRemoveEvent = assertIs(document2Events.first()) val localRemoveOperation = assertIs( - localRemoveEvent.changeInfos.first().operations.first(), + localRemoveEvent.changeInfo.operations.first(), ) assertEquals(remoteSetOperation.executedAt, localRemoveOperation.executedAt) @@ -365,7 +365,7 @@ class ClientTest { }, launch(start = CoroutineStart.UNDISPATCHED) { document3.events.filterIsInstance().collect { event -> - document3Ops.addAll(event.changeInfos.flatMap { it.operations }) + document3Ops.addAll(event.changeInfo.operations) } }, ) @@ -392,8 +392,8 @@ class ClientTest { // 03. c1 and c2 sync with push-only mode. So, the changes of c1 and c2 // are not reflected to each other. // But, c3 can get the changes of c1 and c2, because c3 sync with pull-pull mode. - client1.pauseRemoteChange(document1) - client2.pauseRemoteChange(document2) + client1.pauseRemoteChanges(document1) + client2.pauseRemoteChanges(document2) document1.updateAsync { it["c1"] = 1 }.await() diff --git a/yorkie/src/androidTest/kotlin/dev/yorkie/core/DocumentTest.kt b/yorkie/src/androidTest/kotlin/dev/yorkie/core/DocumentTest.kt index a18a2ac2e..9e9520eb7 100644 --- a/yorkie/src/androidTest/kotlin/dev/yorkie/core/DocumentTest.kt +++ b/yorkie/src/androidTest/kotlin/dev/yorkie/core/DocumentTest.kt @@ -5,7 +5,6 @@ import dev.yorkie.assertJsonContentEquals import dev.yorkie.document.Document import dev.yorkie.document.Document.DocumentStatus import dev.yorkie.document.Document.Event -import dev.yorkie.document.Document.Event.ChangeInfo import dev.yorkie.document.crdt.TextWithAttributes import dev.yorkie.document.json.JsonArray import dev.yorkie.document.json.JsonCounter @@ -232,13 +231,13 @@ class DocumentTest { launch(start = CoroutineStart.UNDISPATCHED) { document1.events.filterIsInstance() .collect { - document1Ops.addAll(it.changeInfos.flatMap(ChangeInfo::operations)) + document1Ops.addAll(it.changeInfo.operations) } }, launch(start = CoroutineStart.UNDISPATCHED) { document2.events.filterIsInstance() .collect { - document2Ops.addAll(it.changeInfos.flatMap(ChangeInfo::operations)) + document2Ops.addAll(it.changeInfo.operations) } }, ) @@ -359,21 +358,19 @@ class DocumentTest { "events" to launch(start = CoroutineStart.UNDISPATCHED) { document1.events.filterIsInstance() .collect { - document1Ops.addAll(it.changeInfos.flatMap(ChangeInfo::operations)) + document1Ops.addAll(it.changeInfo.operations) } }, "todos" to launch(start = CoroutineStart.UNDISPATCHED) { document1.events("$.todos").filterIsInstance() .collect { - document1TodosOps.addAll(it.changeInfos.flatMap(ChangeInfo::operations)) + document1TodosOps.addAll(it.changeInfo.operations) } }, "counter" to launch(start = CoroutineStart.UNDISPATCHED) { document1.events("$.counter").filterIsInstance() .collect { - document1CounterOps.addAll( - it.changeInfos.flatMap(ChangeInfo::operations), - ) + document1CounterOps.addAll(it.changeInfo.operations) } }, ) @@ -488,19 +485,19 @@ class DocumentTest { "events" to launch(start = CoroutineStart.UNDISPATCHED) { document1.events.filterIsInstance() .collect { - document1Ops.addAll(it.changeInfos.flatMap(ChangeInfo::operations)) + document1Ops.addAll(it.changeInfo.operations) } }, "todos" to launch(start = CoroutineStart.UNDISPATCHED) { document1.events("$.todos.0").filterIsInstance() .collect { - document1TodosOps.addAll(it.changeInfos.flatMap(ChangeInfo::operations)) + document1TodosOps.addAll(it.changeInfo.operations) } }, "obj" to launch(start = CoroutineStart.UNDISPATCHED) { document1.events("$.obj.c1").filterIsInstance() .collect { - document1ObjOps.addAll(it.changeInfos.flatMap(ChangeInfo::operations)) + document1ObjOps.addAll(it.changeInfo.operations) } }, ) diff --git a/yorkie/src/main/kotlin/dev/yorkie/core/Client.kt b/yorkie/src/main/kotlin/dev/yorkie/core/Client.kt index 173e21a77..4135fc132 100644 --- a/yorkie/src/main/kotlin/dev/yorkie/core/Client.kt +++ b/yorkie/src/main/kotlin/dev/yorkie/core/Client.kt @@ -608,7 +608,7 @@ public class Client @VisibleForTesting internal constructor( * Pauses the synchronization of remote changes, * allowing only local changes to be applied. */ - public fun pauseRemoteChange(document: Document) { + public fun pauseRemoteChanges(document: Document) { changeSyncMode(document, SyncMode.PushOnly) } diff --git a/yorkie/src/main/kotlin/dev/yorkie/document/Document.kt b/yorkie/src/main/kotlin/dev/yorkie/document/Document.kt index 1656e76b0..e713abd5e 100644 --- a/yorkie/src/main/kotlin/dev/yorkie/document/Document.kt +++ b/yorkie/src/main/kotlin/dev/yorkie/document/Document.kt @@ -97,8 +97,8 @@ public class Document(public val key: Key) { val operationInfos = change.execute(root) localChanges += change changeID = change.id - val changeInfos = listOf(change.toChangeInfo(operationInfos)) - eventStream.emit(Event.LocalChange(changeInfos)) + val changeInfo = change.toChangeInfo(operationInfos) + eventStream.emit(Event.LocalChange(changeInfo)) true } } @@ -112,33 +112,27 @@ public class Document(public val key: Key) { when (event) { is Event.Snapshot -> event is Event.RemoteChange -> { - event.changeInfos.filterTargetChangeInfos(targetPath) + event.changeInfo.operations.filterTargetOpInfos(targetPath) .takeIf { it.isNotEmpty() } ?.let { - Event.RemoteChange(it) + Event.RemoteChange(event.changeInfo.copy(operations = it)) } } is Event.LocalChange -> { - event.changeInfos.filterTargetChangeInfos(targetPath) + event.changeInfo.operations.filterTargetOpInfos(targetPath) .takeIf { it.isNotEmpty() } ?.let { - Event.LocalChange(it) + Event.LocalChange(event.changeInfo.copy(operations = it)) } } } } } - private fun List.filterTargetChangeInfos(targetPath: String) = - mapNotNull { (message, operations, actor) -> - val targetOps = operations.filter { isSameElementOrChildOf(it.path, targetPath) } - if (targetOps.isEmpty()) { - null - } else { - Event.ChangeInfo(message, targetOps, actor) - } - } + private fun List.filterTargetOpInfos(targetPath: String): List { + return filter { isSameElementOrChildOf(it.path, targetPath) } + } private fun isSameElementOrChildOf(element: String, parent: String): Boolean { return if (parent == element) { @@ -220,7 +214,9 @@ public class Document(public val key: Key) { if (changesInfo.isEmpty()) { return } - eventStream.emit(Event.RemoteChange(changesInfo)) + changesInfo.forEach { changeInfo -> + eventStream.emit(Event.RemoteChange(changeInfo)) + } } private suspend fun ensureClone(): CrdtRoot = withContext(dispatcher) { @@ -292,14 +288,14 @@ public class Document(public val key: Key) { * An event that occurs when the document is changed by local changes. */ public class LocalChange internal constructor( - public val changeInfos: List, + public val changeInfo: ChangeInfo, ) : Event /** * An event that occurs when the document is changed by remote changes. */ public class RemoteChange internal constructor( - public val changeInfos: List, + public val changeInfo: ChangeInfo, ) : Event /** diff --git a/yorkie/src/test/kotlin/dev/yorkie/document/DocumentTest.kt b/yorkie/src/test/kotlin/dev/yorkie/document/DocumentTest.kt index d6e0b8c15..1055ae76b 100644 --- a/yorkie/src/test/kotlin/dev/yorkie/document/DocumentTest.kt +++ b/yorkie/src/test/kotlin/dev/yorkie/document/DocumentTest.kt @@ -141,7 +141,7 @@ class DocumentTest { assertEquals(1, events.size) var event = events.first() assertIs(event) - var operations = event.changeInfos.first().operations + var operations = event.changeInfo.operations assertEquals(2, operations.size) assertTrue(operations.all { it is SetOpInfo }) @@ -159,7 +159,7 @@ class DocumentTest { assertEquals(2, events.size) event = events.last() assertIs(event) - operations = event.changeInfos.first().operations + operations = event.changeInfo.operations assertEquals(2, operations.size) assertTrue(operations.all { it is RemoveOpInfo })