Add server synchronization feature.
This commit is contained in:
		
							parent
							
								
									53c6aede27
								
							
						
					
					
						commit
						fa49d8ed22
					
				
					 15 changed files with 154 additions and 48 deletions
				
			
		| 
						 | 
				
			
			@ -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<String>,
 | 
			
		||||
    ) {
 | 
			
		||||
        instances.forEach { characterSheetId ->
 | 
			
		||||
            try {
 | 
			
		||||
                characterRepository.updateCharacterSheet(
 | 
			
		||||
                    characterSheetId = characterSheetId,
 | 
			
		||||
                )
 | 
			
		||||
                inventoryRepository.updateInventoryFlow(
 | 
			
		||||
                    characterSheetId = characterSheetId,
 | 
			
		||||
                )
 | 
			
		||||
            } catch (exception: Exception) {
 | 
			
		||||
                println(exception.message) // TODO proper exception handling
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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<ActionPageUio?>,
 | 
			
		||||
    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,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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(
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue