diff --git a/composeApp/src/commonMain/composeResources/drawable/ic_sync_24dp.xml b/composeApp/src/commonMain/composeResources/drawable/ic_sync_24dp.xml
new file mode 100644
index 0000000..7e3b3e8
--- /dev/null
+++ b/composeApp/src/commonMain/composeResources/drawable/ic_sync_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/DataSyncViewModel.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/DataSyncViewModel.kt
index ba16d94..14c3bf4 100644
--- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/DataSyncViewModel.kt
+++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/DataSyncViewModel.kt
@@ -9,11 +9,13 @@ import com.pixelized.desktop.lwa.repository.item.ItemRepository
import com.pixelized.desktop.lwa.repository.network.NetworkRepository
import com.pixelized.desktop.lwa.repository.settings.SettingsRepository
import com.pixelized.desktop.lwa.repository.tag.TagRepository
+import com.pixelized.shared.lwa.protocol.websocket.GameAdminEvent
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.launchIn
@@ -45,24 +47,27 @@ class DataSyncViewModel(
@OptIn(ExperimentalCoroutinesApi::class)
suspend fun synchronise() = coroutineScope {
- networkRepository.data.onEach { println(it) }.launchIn(this)
+ networkRepository.data
+ .onEach { println(it) }
+ .launchIn(this)
+
+ networkRepository.status
+ .filter { status -> status == NetworkRepository.Status.CONNECTED }
+ .flatMapLatest { networkRepository.data }
+ .filterIsInstance(GameAdminEvent.ServerSynchronization::class)
+ .flowOn(context = Dispatchers.IO)
+ .onEach {
+ updateRepository()
+ updateCharacterInstance(
+ instances = campaignRepository.campaignFlow().value.instances
+ )
+ }
+ .launchIn(this)
networkRepository.status
.filter { status -> status == NetworkRepository.Status.CONNECTED }
.flowOn(context = Dispatchers.IO)
- .onEach {
- try {
- tagRepository.updateAlterationTags()
- alterationRepository.updateAlterationFlow()
- tagRepository.updateCharacterTags()
- characterRepository.updateCharacterPreviews()
- campaignRepository.updateCampaign()
- tagRepository.updateItemTags()
- itemRepository.updateItemFlow()
- } catch (exception: Exception) {
- println(exception.message) // TODO proper exception handling
- }
- }
+ .onEach { updateRepository() }
.launchIn(this)
networkRepository.status
@@ -70,20 +75,38 @@ class DataSyncViewModel(
.flowOn(context = Dispatchers.IO)
.flatMapLatest { campaignRepository.campaignFlow().map { it.instances } }
.distinctUntilChanged()
- .onEach { instances ->
- instances.forEach { characterSheetId ->
- try {
- characterRepository.updateCharacterSheet(
- characterSheetId = characterSheetId,
- )
- inventoryRepository.updateInventoryFlow(
- characterSheetId = characterSheetId,
- )
- } catch (exception: Exception) {
- println(exception.message) // TODO proper exception handling
- }
- }
- }
+ .onEach { updateCharacterInstance(instances = it) }
.launchIn(this)
}
+
+ private suspend fun updateRepository() {
+ try {
+ tagRepository.updateAlterationTags()
+ alterationRepository.updateAlterationFlow()
+ tagRepository.updateCharacterTags()
+ characterRepository.updateCharacterPreviews()
+ campaignRepository.updateCampaign()
+ tagRepository.updateItemTags()
+ itemRepository.updateItemFlow()
+ } catch (exception: Exception) {
+ println(exception.message) // TODO proper exception handling
+ }
+ }
+
+ private suspend fun updateCharacterInstance(
+ instances: Set,
+ ) {
+ instances.forEach { characterSheetId ->
+ try {
+ characterRepository.updateCharacterSheet(
+ characterSheetId = characterSheetId,
+ )
+ inventoryRepository.updateInventoryFlow(
+ characterSheetId = characterSheetId,
+ )
+ } catch (exception: Exception) {
+ println(exception.message) // TODO proper exception handling
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/text/TextMessageFactory.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/text/TextMessageFactory.kt
index ee9b771..caf7665 100644
--- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/text/TextMessageFactory.kt
+++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/campaign/text/TextMessageFactory.kt
@@ -10,6 +10,7 @@ import com.pixelized.shared.lwa.model.campaign.Campaign
import com.pixelized.shared.lwa.protocol.websocket.ApiSynchronisation
import com.pixelized.shared.lwa.protocol.websocket.CampaignEvent
import com.pixelized.shared.lwa.protocol.websocket.CharacterSheetEvent
+import com.pixelized.shared.lwa.protocol.websocket.GameAdminEvent
import com.pixelized.shared.lwa.protocol.websocket.GameMasterEvent
import com.pixelized.shared.lwa.protocol.websocket.RollEvent
import com.pixelized.shared.lwa.protocol.websocket.SocketMessage
@@ -140,6 +141,8 @@ class TextMessageFactory(
is GameMasterEvent -> null
+ is GameAdminEvent -> null
+
is ApiSynchronisation -> null
}
}
diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/action/GMActionPage.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/action/GMActionPage.kt
index 6ab0a45..fc30ffd 100644
--- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/action/GMActionPage.kt
+++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/action/GMActionPage.kt
@@ -19,6 +19,7 @@ import com.pixelized.desktop.lwa.ui.composable.error.ErrorSnackHandler
import kotlinx.coroutines.launch
import lwacharactersheet.composeapp.generated.resources.Res
import lwacharactersheet.composeapp.generated.resources.ic_camping_24dp
+import lwacharactersheet.composeapp.generated.resources.ic_sync_24dp
import lwacharactersheet.composeapp.generated.resources.ic_visibility_24dp
import lwacharactersheet.composeapp.generated.resources.ic_visibility_off_24dp
import org.koin.compose.viewmodel.koinViewModel
@@ -40,6 +41,11 @@ fun GMActionPage(
GMActionContent(
actions = actions,
scroll = scroll,
+ onServerSync = {
+ scope.launch {
+ viewModel.onServerSync()
+ }
+ },
onPartyHeal = {
scope.launch {
viewModel.onPartyHeal()
@@ -68,6 +74,7 @@ fun GMActionContent(
scroll: ScrollState,
spacing: Dp = 8.dp,
actions: State,
+ onServerSync: () -> Unit,
onPartyHeal: () -> Unit,
onPartyVisibility: () -> Unit,
onNpcVisibility: () -> Unit,
@@ -78,6 +85,12 @@ fun GMActionContent(
.padding(vertical = spacing, horizontal = spacing),
verticalArrangement = Arrangement.spacedBy(space = spacing),
) {
+ GMAction(
+ modifier = Modifier.fillMaxWidth(),
+ icon = Res.drawable.ic_sync_24dp,
+ label = "Syncrhonization du serveur",
+ onAction = onServerSync,
+ )
GMAction(
modifier = Modifier.fillMaxWidth(),
icon = Res.drawable.ic_camping_24dp,
diff --git a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/action/GMActionViewModel.kt b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/action/GMActionViewModel.kt
index 659c3bc..3a980c3 100644
--- a/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/action/GMActionViewModel.kt
+++ b/composeApp/src/commonMain/kotlin/com/pixelized/desktop/lwa/ui/screen/gamemaster/action/GMActionViewModel.kt
@@ -6,6 +6,7 @@ import com.pixelized.desktop.lwa.repository.campaign.CampaignRepository
import com.pixelized.desktop.lwa.repository.characterSheet.CharacterSheetRepository
import com.pixelized.desktop.lwa.repository.network.NetworkRepository
import com.pixelized.desktop.lwa.ui.composable.error.ErrorSnackUio
+import com.pixelized.shared.lwa.protocol.websocket.GameAdminEvent
import com.pixelized.shared.lwa.protocol.websocket.GameMasterEvent
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
@@ -38,6 +39,19 @@ class GMActionViewModel(
initialValue = null,
)
+ suspend fun onServerSync() {
+ try {
+ networkRepository.share(
+ GameAdminEvent.ServerSynchronization(
+ timestamp = System.currentTimeMillis(),
+ )
+ )
+ } catch (exception: Exception) {
+ val message = ErrorSnackUio.from(exception = exception)
+ _error.emit(message)
+ }
+ }
+
suspend fun onPartyHeal() {
campaignRepository.campaignFlow().value.characters.forEach { characterSheetId ->
val sheet = characterRepository.characterDetail(
diff --git a/server/src/main/kotlin/com/pixelized/server/lwa/model/alteration/AlterationStore.kt b/server/src/main/kotlin/com/pixelized/server/lwa/model/alteration/AlterationStore.kt
index ce74f7e..58a1113 100644
--- a/server/src/main/kotlin/com/pixelized/server/lwa/model/alteration/AlterationStore.kt
+++ b/server/src/main/kotlin/com/pixelized/server/lwa/model/alteration/AlterationStore.kt
@@ -41,7 +41,7 @@ class AlterationStore(
fun alterationsFlow(): StateFlow> = alterationFlow
- private fun updateAlterationFlow() {
+ fun updateAlterationFlow() {
alterationFlow.value = try {
load()
} catch (exception: Exception) {
diff --git a/server/src/main/kotlin/com/pixelized/server/lwa/model/campaign/CampaignStore.kt b/server/src/main/kotlin/com/pixelized/server/lwa/model/campaign/CampaignStore.kt
index 5a499b4..f612cea 100644
--- a/server/src/main/kotlin/com/pixelized/server/lwa/model/campaign/CampaignStore.kt
+++ b/server/src/main/kotlin/com/pixelized/server/lwa/model/campaign/CampaignStore.kt
@@ -38,7 +38,7 @@ class CampaignStore(
fun campaignFlow(): StateFlow = campaignFlow
- private fun updateCampaignFlow() {
+ fun updateCampaignFlow() {
campaignFlow.value = try {
load()
} catch (exception: Exception) {
diff --git a/server/src/main/kotlin/com/pixelized/server/lwa/model/character/CharacterSheetStore.kt b/server/src/main/kotlin/com/pixelized/server/lwa/model/character/CharacterSheetStore.kt
index 06ff442..f2b3545 100644
--- a/server/src/main/kotlin/com/pixelized/server/lwa/model/character/CharacterSheetStore.kt
+++ b/server/src/main/kotlin/com/pixelized/server/lwa/model/character/CharacterSheetStore.kt
@@ -40,7 +40,7 @@ class CharacterSheetStore(
fun characterSheetsFlow(): StateFlow> = characterSheetsFlow
- private suspend fun updateCharacterFlow() {
+ fun updateCharacterFlow() {
characterSheetsFlow.value = try {
load()
} catch (exception: Exception) {
@@ -54,7 +54,7 @@ class CharacterSheetStore(
JsonCodingException::class,
JsonConversionException::class,
)
- suspend fun load(
+ fun load(
directory: File = this.directory,
): List {
return directory
diff --git a/server/src/main/kotlin/com/pixelized/server/lwa/model/inventory/InventoryStore.kt b/server/src/main/kotlin/com/pixelized/server/lwa/model/inventory/InventoryStore.kt
index 1bb13a5..b9f3fed 100644
--- a/server/src/main/kotlin/com/pixelized/server/lwa/model/inventory/InventoryStore.kt
+++ b/server/src/main/kotlin/com/pixelized/server/lwa/model/inventory/InventoryStore.kt
@@ -40,7 +40,7 @@ class InventoryStore(
fun inventoryFlow(): StateFlow