Skip to content

Commit bcfb5ac

Browse files
authored
Sync operation refactor (#394)
THE GREAT REFACTOR IS COMPLETE!!! Syncing operation has been decomposed into individual Operations which are all now unit tested
1 parent 5eb2815 commit bcfb5ac

File tree

56 files changed

+3966
-948
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+3966
-948
lines changed

android/src/main/kotlin/com/darkrockstudios/apps/hammer/android/widgets/AddNoteWorker.kt

+3-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import com.darkrockstudios.apps.hammer.common.data.isSuccess
88
import com.darkrockstudios.apps.hammer.common.data.notesrepository.NotesRepository
99
import com.darkrockstudios.apps.hammer.common.data.projectsrepository.ProjectsRepository
1010
import com.darkrockstudios.apps.hammer.common.data.sync.projectsync.ClientProjectSynchronizer
11+
import com.darkrockstudios.apps.hammer.common.data.sync.projectsync.SyncDataRepository
1112
import com.darkrockstudios.apps.hammer.common.data.temporaryProjectTask
1213
import io.github.aakira.napier.Napier
1314
import kotlinx.coroutines.sync.Mutex
@@ -47,8 +48,9 @@ class AddNoteWorker(
4748
Napier.d { "Note create in proj: ${projectDef.name} result: $result" }
4849

4950
if (isSuccess(result) && isInternetConnected(context)) {
51+
val syncDataRepository: SyncDataRepository = projectScope.get()
5052
val synchronizer: ClientProjectSynchronizer = projectScope.get()
51-
if (synchronizer.isServerSynchronized()) {
53+
if (syncDataRepository.isServerSynchronized()) {
5254
val success = synchronizer.sync(
5355
onProgress = { _, log ->
5456
log?.let {

common/src/commonMain/kotlin/com/darkrockstudios/apps/hammer/common/components/projectroot/ProjectRootComponent.kt

+5-5
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import com.darkrockstudios.apps.hammer.common.data.MenuItemDescriptor
1515
import com.darkrockstudios.apps.hammer.common.data.ProjectDef
1616
import com.darkrockstudios.apps.hammer.common.data.projectInject
1717
import com.darkrockstudios.apps.hammer.common.data.sceneeditorrepository.SceneEditorRepository
18-
import com.darkrockstudios.apps.hammer.common.data.sync.projectsync.ClientProjectSynchronizer
18+
import com.darkrockstudios.apps.hammer.common.data.sync.projectsync.SyncDataRepository
1919
import kotlinx.coroutines.launch
2020
import kotlinx.coroutines.withContext
2121

@@ -26,7 +26,7 @@ class ProjectRootComponent(
2626
private val removeMenu: (id: String) -> Unit,
2727
) : ProjectComponentBase(projectDef, componentContext), ProjectRoot {
2828

29-
private val synchronizer: ClientProjectSynchronizer by projectInject()
29+
private val syncDataRepository: SyncDataRepository by projectInject()
3030
private val sceneEditor: SceneEditorRepository by projectInject()
3131

3232
private val _backEnabled = MutableValue(true)
@@ -145,7 +145,7 @@ class ProjectRootComponent(
145145

146146
list.addAll(router.shouldConfirmClose())
147147

148-
if (synchronizer.shouldAutoSync()) {
148+
if (syncDataRepository.shouldAutoSync()) {
149149
list.add(CloseConfirm.Sync)
150150
}
151151

@@ -171,7 +171,7 @@ class ProjectRootComponent(
171171
}
172172

173173
private fun addMenuItems() {
174-
if (synchronizer.isServerSynchronized()) {
174+
if (syncDataRepository.isServerSynchronized()) {
175175
addMenu(
176176
MenuDescriptor(
177177
id = "project-root-sync",
@@ -191,7 +191,7 @@ class ProjectRootComponent(
191191
}
192192

193193
private fun removeMenuItems() {
194-
if (synchronizer.isServerSynchronized()) {
194+
if (syncDataRepository.isServerSynchronized()) {
195195
removeMenu("project-root-sync")
196196
}
197197
}

common/src/commonMain/kotlin/com/darkrockstudios/apps/hammer/common/data/drafts/SceneDraftRepository.kt

+7-4
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import com.darkrockstudios.apps.hammer.common.data.SceneItem
99
import com.darkrockstudios.apps.hammer.common.data.id.IdRepository
1010
import com.darkrockstudios.apps.hammer.common.data.projectInject
1111
import com.darkrockstudios.apps.hammer.common.data.sceneeditorrepository.SceneEditorRepository
12-
import com.darkrockstudios.apps.hammer.common.data.sync.projectsync.ClientProjectSynchronizer
12+
import com.darkrockstudios.apps.hammer.common.data.sync.projectsync.SyncDataRepository
1313
import com.darkrockstudios.apps.hammer.common.dependencyinjection.ProjectDefScope
1414
import io.github.aakira.napier.Napier
1515
import kotlinx.datetime.Clock
@@ -23,7 +23,7 @@ class SceneDraftRepository(
2323
override val projectScope = ProjectDefScope(projectDef)
2424

2525
private val idRepository: IdRepository by projectInject()
26-
private val projectSynchronizer: ClientProjectSynchronizer by projectInject()
26+
private val syncDataRepository: SyncDataRepository by projectInject()
2727

2828
suspend fun getAllDrafts(): Set<DraftDef> = datasource.getAllDrafts()
2929
fun getSceneIdsThatHaveDrafts(): List<Int> = datasource.getSceneIdsThatHaveDrafts()
@@ -76,14 +76,17 @@ class SceneDraftRepository(
7676
* But I'm leaving it here just in case we need it at some point.
7777
*/
7878
protected suspend fun markForSynchronization(originalDef: DraftDef, originalContent: String) {
79-
if (projectSynchronizer.isServerSynchronized() && !projectSynchronizer.isEntityDirty(originalDef.id)) {
79+
if (syncDataRepository.isServerSynchronized() && !syncDataRepository.isEntityDirty(
80+
originalDef.id
81+
)
82+
) {
8083
val hash = EntityHasher.hashSceneDraft(
8184
id = originalDef.id,
8285
created = originalDef.draftTimestamp,
8386
name = originalDef.draftName,
8487
content = originalContent,
8588
)
86-
projectSynchronizer.markEntityAsDirty(originalDef.id, hash)
89+
syncDataRepository.markEntityAsDirty(originalDef.id, hash)
8790
}
8891
}
8992
}

common/src/commonMain/kotlin/com/darkrockstudios/apps/hammer/common/data/encyclopediarepository/EncyclopediaRepository.kt

+5-5
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import com.darkrockstudios.apps.hammer.common.data.encyclopediarepository.entry.
1010
import com.darkrockstudios.apps.hammer.common.data.encyclopediarepository.entry.EntryDef
1111
import com.darkrockstudios.apps.hammer.common.data.encyclopediarepository.entry.EntryType
1212
import com.darkrockstudios.apps.hammer.common.data.id.IdRepository
13-
import com.darkrockstudios.apps.hammer.common.data.sync.projectsync.ClientProjectSynchronizer
13+
import com.darkrockstudios.apps.hammer.common.data.sync.projectsync.SyncDataRepository
1414
import com.darkrockstudios.apps.hammer.common.dependencyinjection.DISPATCHER_DEFAULT
1515
import com.darkrockstudios.apps.hammer.common.dependencyinjection.ProjectDefScope
1616
import com.darkrockstudios.apps.hammer.common.fileio.HPath
@@ -32,7 +32,7 @@ class EncyclopediaRepository(
3232
private val projectDef: ProjectDef,
3333
private val idRepository: IdRepository,
3434
private val datasource: EncyclopediaDatasource,
35-
private val projectSynchronizer: ClientProjectSynchronizer
35+
private val syncDataRepository: SyncDataRepository,
3636
) : ScopeCallback, ProjectScoped, KoinComponent {
3737

3838
override val projectScope = ProjectDefScope(projectDef)
@@ -123,7 +123,7 @@ class EncyclopediaRepository(
123123
}
124124

125125
private suspend fun markForSynchronization(entryDef: EntryDef) {
126-
if (projectSynchronizer.isServerSynchronized() && !projectSynchronizer.isEntityDirty(
126+
if (syncDataRepository.isServerSynchronized() && !syncDataRepository.isEntityDirty(
127127
entryDef.id
128128
)
129129
) {
@@ -148,7 +148,7 @@ class EncyclopediaRepository(
148148
tags = entry.tags,
149149
image = image
150150
)
151-
projectSynchronizer.markEntityAsDirty(entryDef.id, hash)
151+
syncDataRepository.markEntityAsDirty(entryDef.id, hash)
152152
}
153153
}
154154

@@ -188,7 +188,7 @@ class EncyclopediaRepository(
188188

189189
suspend fun deleteEntry(entryDef: EntryDef): Boolean {
190190
datasource.deleteEntry(entryDef)
191-
projectSynchronizer.recordIdDeletion(entryDef.id)
191+
syncDataRepository.recordIdDeletion(entryDef.id)
192192
return true
193193
}
194194

common/src/commonMain/kotlin/com/darkrockstudios/apps/hammer/common/data/globalsettings/GlobalSettingsRepository.kt

+7
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ class GlobalSettingsRepository(
6464
}
6565
}
6666

67+
fun isServerSynchronized(): Boolean = (serverSettings != null)
68+
6769
private fun dispatchSettingsUpdate(settings: GlobalSettings) {
6870
globalSettings = settings
6971
_globalSettingsUpdates.tryEmit(settings)
@@ -88,6 +90,11 @@ class GlobalSettingsRepository(
8890
_serverSettingsUpdates.tryEmit(settings)
8991
}
9092

93+
suspend fun userIdOrThrow(): Long {
94+
return serverSettingsUpdates.first()?.userId
95+
?: throw IllegalStateException("Server settings missing")
96+
}
97+
9198
companion object {
9299
const val DEFAULT_PROJECTS_DIR = "HammerProjects"
93100

common/src/commonMain/kotlin/com/darkrockstudios/apps/hammer/common/data/id/IdRepository.kt

+6-6
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import com.darkrockstudios.apps.hammer.common.data.id.datasources.SceneDraftIdDa
1010
import com.darkrockstudios.apps.hammer.common.data.id.datasources.SceneIdDatasource
1111
import com.darkrockstudios.apps.hammer.common.data.id.datasources.TimeLineEventIdDatasource
1212
import com.darkrockstudios.apps.hammer.common.data.projectInject
13-
import com.darkrockstudios.apps.hammer.common.data.sync.projectsync.ClientProjectSynchronizer
13+
import com.darkrockstudios.apps.hammer.common.data.sync.projectsync.SyncDataRepository
1414
import com.darkrockstudios.apps.hammer.common.dependencyinjection.ProjectDefScope
1515
import kotlinx.atomicfu.locks.reentrantLock
1616
import kotlinx.atomicfu.locks.withLock
@@ -19,7 +19,7 @@ import kotlin.math.max
1919

2020
class IdRepository(private val projectDef: ProjectDef) : ProjectScoped {
2121
override val projectScope = ProjectDefScope(projectDef)
22-
private val projectSynchronizer: ClientProjectSynchronizer by projectInject()
22+
private val syncDataRepository: SyncDataRepository by projectInject()
2323

2424
private val idDatasources: Set<IdDatasource> by lazy {
2525
EntityType.entries
@@ -50,8 +50,8 @@ class IdRepository(private val projectDef: ProjectDef) : ProjectScoped {
5050
lastId = max(lastId, highestId)
5151
}
5252

53-
if (projectSynchronizer.isServerSynchronized()) {
54-
projectSynchronizer.deletedIds().maxOrNull()?.let { maxDeletedId ->
53+
if (syncDataRepository.isServerSynchronized()) {
54+
syncDataRepository.deletedIds().maxOrNull()?.let { maxDeletedId ->
5555
lastId = max(lastId, maxDeletedId)
5656
}
5757
}
@@ -65,8 +65,8 @@ class IdRepository(private val projectDef: ProjectDef) : ProjectScoped {
6565
}
6666

6767
private suspend fun recordNewId(claimedId: Int) {
68-
if (projectSynchronizer.isServerSynchronized()) {
69-
projectSynchronizer.recordNewId(claimedId)
68+
if (syncDataRepository.isServerSynchronized()) {
69+
syncDataRepository.recordNewId(claimedId)
7070
}
7171
}
7272

common/src/commonMain/kotlin/com/darkrockstudios/apps/hammer/common/data/notesrepository/NotesRepository.kt

+5-5
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import com.darkrockstudios.apps.hammer.common.data.ProjectScoped
77
import com.darkrockstudios.apps.hammer.common.data.id.IdRepository
88
import com.darkrockstudios.apps.hammer.common.data.notesrepository.note.NoteContainer
99
import com.darkrockstudios.apps.hammer.common.data.notesrepository.note.NoteContent
10-
import com.darkrockstudios.apps.hammer.common.data.sync.projectsync.ClientProjectSynchronizer
10+
import com.darkrockstudios.apps.hammer.common.data.sync.projectsync.SyncDataRepository
1111
import com.darkrockstudios.apps.hammer.common.dependencyinjection.DISPATCHER_DEFAULT
1212
import com.darkrockstudios.apps.hammer.common.dependencyinjection.ProjectDefScope
1313
import kotlinx.coroutines.CoroutineScope
@@ -27,7 +27,7 @@ import kotlin.coroutines.CoroutineContext
2727
class NotesRepository(
2828
projectDef: ProjectDef,
2929
private val idRepository: IdRepository,
30-
private val projectSynchronizer: ClientProjectSynchronizer,
30+
private val syncDataRepository: SyncDataRepository,
3131
private val notesDatasource: NotesDatasource,
3232
) : ScopeCallback, ProjectScoped {
3333

@@ -73,7 +73,7 @@ class NotesRepository(
7373
}
7474

7575
private suspend fun markForSync(id: Int, originalHash: String? = null) {
76-
if (projectSynchronizer.isServerSynchronized() && !projectSynchronizer.isEntityDirty(id)) {
76+
if (syncDataRepository.isServerSynchronized() && !syncDataRepository.isEntityDirty(id)) {
7777
val hash = if (originalHash != null) {
7878
originalHash
7979
} else {
@@ -84,7 +84,7 @@ class NotesRepository(
8484
content = noteContainer.note.content,
8585
)
8686
}
87-
projectSynchronizer.markEntityAsDirty(id, hash)
87+
syncDataRepository.markEntityAsDirty(id, hash)
8888
}
8989
}
9090

@@ -115,7 +115,7 @@ class NotesRepository(
115115

116116
suspend fun deleteNote(id: Int) {
117117
notesDatasource.deleteNote(id)
118-
projectSynchronizer.recordIdDeletion(id)
118+
syncDataRepository.recordIdDeletion(id)
119119
}
120120

121121
suspend fun updateNote(noteContent: NoteContent, markForSync: Boolean = true) {

common/src/commonMain/kotlin/com/darkrockstudios/apps/hammer/common/data/projectmetadata/ProjectMetadataDatasource.kt

+6-6
Original file line numberDiff line numberDiff line change
@@ -77,12 +77,12 @@ class ProjectMetadataDatasource(
7777

7878
return newMetadata
7979
}
80-
}
8180

82-
fun ProjectMetadataDatasource.loadProjectId(projectDef: ProjectDef): ProjectId? {
83-
return loadMetadata(projectDef).info.serverProjectId
84-
}
81+
fun loadProjectId(projectDef: ProjectDef): ProjectId? {
82+
return loadMetadata(projectDef).info.serverProjectId
83+
}
8584

86-
fun ProjectMetadataDatasource.requireProjectId(projectDef: ProjectDef): ProjectId {
87-
return loadProjectId(projectDef) ?: error("Project has no server project id")
85+
fun requireProjectId(projectDef: ProjectDef): ProjectId {
86+
return loadProjectId(projectDef) ?: error("Project has no server project id")
87+
}
8888
}

common/src/commonMain/kotlin/com/darkrockstudios/apps/hammer/common/data/projectsrepository/ProjectsRepository.kt

-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import com.darkrockstudios.apps.hammer.common.data.globalsettings.GlobalSettings
1010
import com.darkrockstudios.apps.hammer.common.data.isSuccess
1111
import com.darkrockstudios.apps.hammer.common.data.migrator.PROJECT_DATA_VERSION
1212
import com.darkrockstudios.apps.hammer.common.data.projectmetadata.ProjectMetadataDatasource
13-
import com.darkrockstudios.apps.hammer.common.data.projectmetadata.loadProjectId
1413
import com.darkrockstudios.apps.hammer.common.data.toMsg
1514
import com.darkrockstudios.apps.hammer.common.dependencyinjection.DISPATCHER_DEFAULT
1615
import com.darkrockstudios.apps.hammer.common.fileio.HPath

common/src/commonMain/kotlin/com/darkrockstudios/apps/hammer/common/data/sceneeditorrepository/SceneEditorRepository.kt

+11-11
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import com.darkrockstudios.apps.hammer.common.data.projectmetadata.ProjectMetada
1717
import com.darkrockstudios.apps.hammer.common.data.projectsrepository.ProjectsRepository
1818
import com.darkrockstudios.apps.hammer.common.data.sceneeditorrepository.scenemetadata.SceneMetadata
1919
import com.darkrockstudios.apps.hammer.common.data.sceneeditorrepository.scenemetadata.SceneMetadataDatasource
20-
import com.darkrockstudios.apps.hammer.common.data.sync.projectsync.ClientProjectSynchronizer
20+
import com.darkrockstudios.apps.hammer.common.data.sync.projectsync.SyncDataRepository
2121
import com.darkrockstudios.apps.hammer.common.data.sync.projectsync.toApiType
2222
import com.darkrockstudios.apps.hammer.common.data.tree.ImmutableTree
2323
import com.darkrockstudios.apps.hammer.common.data.tree.Tree
@@ -51,7 +51,7 @@ import kotlin.time.Duration.Companion.milliseconds
5151
class SceneEditorRepository(
5252
val projectDef: ProjectDef,
5353
private val idRepository: IdRepository,
54-
private val projectSynchronizer: ClientProjectSynchronizer,
54+
private val syncDataRepository: SyncDataRepository,
5555
private val projectMetadataDatasource: ProjectMetadataDatasource,
5656
private val sceneMetadataDatasource: SceneMetadataDatasource,
5757
private val sceneDatasource: SceneDatasource,
@@ -111,7 +111,7 @@ class SceneEditorRepository(
111111
get() = sceneTree
112112

113113
private suspend fun markForSynchronization(scene: SceneItem) {
114-
if (projectSynchronizer.isServerSynchronized() && !projectSynchronizer.isEntityDirty(scene.id)) {
114+
if (syncDataRepository.isServerSynchronized() && !syncDataRepository.isEntityDirty(scene.id)) {
115115
val metadata = sceneMetadataDatasource.loadMetadata(scene.id)
116116
val pathSegments = getPathSegments(scene)
117117
val content = loadSceneMarkdownRaw(scene)
@@ -125,7 +125,7 @@ class SceneEditorRepository(
125125
outline = metadata?.outline ?: "",
126126
notes = metadata?.notes ?: "",
127127
)
128-
projectSynchronizer.markEntityAsDirty(scene.id, hash)
128+
syncDataRepository.markEntityAsDirty(scene.id, hash)
129129
}
130130
}
131131

@@ -496,7 +496,7 @@ class SceneEditorRepository(
496496
}
497497

498498
private suspend fun markForSynchronization(scene: SceneItem, content: String) {
499-
if (projectSynchronizer.isServerSynchronized() && !projectSynchronizer.isEntityDirty(scene.id)) {
499+
if (syncDataRepository.isServerSynchronized() && !syncDataRepository.isEntityDirty(scene.id)) {
500500
val metadata = sceneMetadataDatasource.loadMetadata(scene.id)
501501
val pathSegments = getPathSegments(scene)
502502
val hash = EntityHasher.hashScene(
@@ -509,7 +509,7 @@ class SceneEditorRepository(
509509
outline = metadata?.outline ?: "",
510510
notes = metadata?.notes ?: "",
511511
)
512-
projectSynchronizer.markEntityAsDirty(scene.id, hash)
512+
syncDataRepository.markEntityAsDirty(scene.id, hash)
513513
}
514514
}
515515

@@ -624,7 +624,7 @@ class SceneEditorRepository(
624624
// Must grab a copy of the children before they are modified
625625
// we'll need this if we need to calculate their original hash
626626
// down below for markForSynchronization()
627-
val originalChildren = if (projectSynchronizer.isServerSynchronized()) {
627+
val originalChildren = if (syncDataRepository.isServerSynchronized()) {
628628
parent.children().map { child -> child.value.copy() }
629629
} else {
630630
null
@@ -810,8 +810,8 @@ class SceneEditorRepository(
810810
updateSceneOrder(parentId)
811811
Napier.w("Scene ${scene.id} deleted")
812812

813-
if (projectSynchronizer.isServerSynchronized()) {
814-
projectSynchronizer.recordIdDeletion(scene.id)
813+
if (syncDataRepository.isServerSynchronized()) {
814+
syncDataRepository.recordIdDeletion(scene.id)
815815
}
816816

817817
reloadScenes()
@@ -830,8 +830,8 @@ class SceneEditorRepository(
830830
val deleted = sceneDatasource.deleteGroup(scene)
831831

832832
return if (deleted) {
833-
if (projectSynchronizer.isServerSynchronized()) {
834-
projectSynchronizer.recordIdDeletion(scene.id)
833+
if (syncDataRepository.isServerSynchronized()) {
834+
syncDataRepository.recordIdDeletion(scene.id)
835835
}
836836

837837
val sceneNode = getSceneNodeFromId(scene.id)

0 commit comments

Comments
 (0)