import { Action , Dispatch , ActionCreator } from 'redux' ; import { ThunkAction } from 'redux-thunk' ; import * as signalR from '@microsoft/signalr' ; import * as signalRMsgPack from '@microsoft/signalr-protocol-msgpack' ; import * as JSZip from 'jszip' ; import * as Rusha from 'rusha' ; import * as Actions from './Actions' ; import State from './State' ; import Sex from '../model/enums/Sex' ; import Role from '../model/enums/Role' ; import DataContext from '../model/DataContext' ; import 'es6-promise/auto' ; import { saveState } from './SavedState' ; import OnlineMode from '../model/enums/OnlineMode' ; import GamesFilter from '../model/enums/GamesFilter' ; import localization from '../model/resources/localization' ; import ChatMode from '../model/enums/ChatMode' ; import GameType from '../model/enums/GameType' ; import * as GameErrorsHelper from '../utils/GameErrorsHelper' ; import GameInfo from '../model/server/GameInfo' ; import { attachListeners , detachListeners , activeConnections } from '../utils/ConnectionHelpers' ; import MainView from '../model/enums/MainView' ; import runActionCreators from './run/runActionCreators' ; import Slice from '../model/server/Slice' ; import PackageType from '../model/enums/PackageType' ; import PackageKey from '../model/server/PackageKey' ; import Constants from '../model/enums/Constants' ; import GameServerClient from '../client/GameServerClient' ; import TimeSettings from '../model/server/TimeSettings' ; import ServerAppSettings from '../model/server/ServerAppSettings' ; import AccountSettings from '../model/server/AccountSettings' ; import GameSettings from '../model/server/GameSettings' ; import IGameServerClient from '../client/IGameServerClient' ; import { PackageFilters } from '../model/PackageFilters' ; import { SIPackageInfo } from '../model/SIPackageInfo' ; import { SearchEntity } from '../model/SearchEntity' ; import getErrorMessage from '../utils/ErrorHelpers' ; const isConnectedChanged = ( isConnected ) => ( { type : Actions . ActionTypes . IsConnectedChanged , isConnected } ) ; const onConnectionChanged = ( isConnected , message ) => async ( dispatch , getState , dataContext ) => { dispatch ( isConnectedChanged ( isConnected ) ) ; const state = getState ( ) ; if ( state . ui . mainView === MainView . Game ) { dispatch ( runActionCreators . chatMessageAdded ( { sender : '' , text : message } ) ) ; } else { dispatch ( receiveMessage ( '' , message ) ) ; } if ( ! isConnected ) { return ; } if ( state . ui . mainView === MainView . Game ) { await dataContext . gameClient . sendMessageToServerAsync ( 'INFO' ) ; } else if ( state . ui . mainView === MainView . Lobby ) { navigateToLobby ( - 1 ) ( dispatch , getState , dataContext ) ; } } ; const computerAccountsChanged = ( computerAccounts ) => ( { type : Actions . ActionTypes . ComputerAccountsChanged , computerAccounts } ) ; const navigateToLogin = ( ) => ( { type : Actions . ActionTypes . NavigateToLogin } ) ; const showSettings = ( show ) => ( { type : Actions . ActionTypes . ShowSettings , show } ) ; const navigateToHowToPlay = ( ) => ( { type : Actions . ActionTypes . NavigateToHowToPlay } ) ; const navigateBack = ( ) => ( { type : Actions . ActionTypes . NavigateBack } ) ; const onLoginChanged = ( newLogin ) => ( { type : Actions . ActionTypes . LoginChanged , newLogin } ) ; const loginStart = ( ) => ( { type : Actions . ActionTypes . LoginStart } ) ; const loginEnd = ( error = null ) => ( { type : Actions . ActionTypes . LoginEnd , error } ) ; const saveStateToStorage = ( state ) => { saveState ( { login : state . user . login , game : { name : state . game . name , password : state . game . password , role : state . game . role , type : state . game . type , playersCount : state . game . playersCount } , settings : state . settings } ) ; } ; function getLoginErrorByCode ( response ) { switch ( response . status ) { case 0 : return localization . cannotConnectToServer ; case 403 : return localization . forbiddenNickname ; case 409 : return localization . duplicateUserName ; default : return response . statusText ; } } const navigateToWelcome = ( ) => ( { type : Actions . ActionTypes . NavigateToWelcome } ) ; const serverNameChanged = ( serverName ) => ( { type : Actions . ActionTypes . ServerNameChanged , serverName } ) ; async function loadHostInfoAsync ( dispatch , dataContext ) { const hostInfo = await dataContext . gameClient . getGameHostInfoAsync ( ) ; dataContext . contentUris = hostInfo . contentPublicBaseUrls ; dispatch ( serverNameChanged ( hostInfo . name ) ) ; } const login = ( ) => async ( dispatch , getState , dataContext ) => { dispatch ( loginStart ( ) ) ; const state = getState ( ) ; try { const response = await fetch ( `${ dataContext . serverUri }/api/Account/LogOn` , { method : 'POST' , credentials : 'include' , body : `login=${ state . user . login }&password=` , headers : { 'Content-Type' : 'application/x-www-form-urlencoded' } } ) ; if ( response . ok ) { saveStateToStorage ( state ) ; const token = await response . text ( ) ; const queryString = `?token=${ encodeURIComponent ( token ) }` ; let connectionBuilder = new signalR . HubConnectionBuilder ( ) . withAutomaticReconnect ( ) . withUrl ( `${ dataContext . serverUri }/sionline${ queryString }` ) ; if ( dataContext . config . useMessagePackProtocol ) { connectionBuilder = connectionBuilder . withHubProtocol ( new signalRMsgPack . MessagePackHubProtocol ( ) ) ; } const connection = connectionBuilder . build ( ) ; dataContext . connection = connection ; dataContext . gameClient = new GameServerClient ( connection ) ; try { await dataContext . connection . start ( ) ; if ( dataContext . connection . connectionId ) { activeConnections . push ( dataContext . connection . connectionId ) ; } attachListeners ( dataContext . connection , dispatch ) ; const computerAccounts = await dataContext . gameClient . getComputerAccountsAsync ( ) ; dispatch ( computerAccountsChanged ( computerAccounts ) ) ; dispatch ( loginEnd ( ) ) ; dispatch ( onLoginChanged ( state . user . login . trim ( ) ) ) ; await loadHostInfoAsync ( dispatch , dataContext ) ; dispatch ( navigateToWelcome ( ) ) ; } catch ( error ) { dispatch ( loginEnd ( `${ localization . cannotConnectToServer }: ${ getErrorMessage ( error ) }` ) ) ; } } else { const errorText = getLoginErrorByCode ( response ) ; dispatch ( loginEnd ( errorText ) ) ; } } catch ( err ) { dispatch ( loginEnd ( `${ localization . cannotConnectToServer }: ${ getErrorMessage ( err ) }` ) ) ; } } ; const singlePlay = ( ) => ( { type : Actions . ActionTypes . NavigateToNewGame } ) ; const friendsPlayInternal = ( ) => ( { type : Actions . ActionTypes . NavigateToGames } ) ; const friendsPlay = ( ) => async ( dispatch , getState , dataContext ) => { dispatch ( friendsPlayInternal ( ) ) ; try { await loadGamesAsync ( dispatch , dataContext . gameClient ) ; dispatch ( onlineLoadFinish ( ) ) ; } catch ( error ) { dispatch ( onlineLoadError ( getErrorMessage ( error ) ) ) ; } } ; const navigateToLobbyInternal = ( ) => ( { type : Actions . ActionTypes . NavigateToLobby } ) ; const navigateToLobby = ( gameId , showInfo ? ) => async ( dispatch , getState , dataContext ) => { dispatch ( navigateToLobbyInternal ( ) ) ; if ( gameId > - 1 ) { dispatch ( selectGame ( gameId , showInfo ) ) ; } else if ( dataContext . config . rewriteUrl ) { window . history . pushState ( { } , '' , dataContext . config . rootUri ) ; } try { await loadGamesAsync ( dispatch , dataContext . gameClient ) ; const users = await dataContext . gameClient . getUsersAsync ( ) ; const sortedUsers = users . sort ( ( user1 , user2 ) => user1 . localeCompare ( user2 ) ) ; dispatch ( receiveUsers ( sortedUsers ) ) ; const news = await dataContext . gameClient . getNewsAsync ( ) ; if ( news !== null ) { dispatch ( receiveMessage ( localization . news , news ) ) ; } dispatch ( onlineLoadFinish ( ) ) ; } catch ( error ) { dispatch ( onlineLoadError ( getErrorMessage ( error ) ) ) ; } } ; const clearGames = ( ) => ( { type : Actions . ActionTypes . ClearGames } ) ; const receiveGames = ( games ) => ( { type : Actions . ActionTypes . ReceiveGames , games } ) ; const receiveUsers = ( users ) => ( { type : Actions . ActionTypes . ReceiveUsers , users } ) ; const receiveMessage = ( sender , message ) => ( { type : Actions . ActionTypes . ReceiveMessage , sender , message } ) ; const onlineLoadFinish = ( ) => ( { type : Actions . ActionTypes . OnlineLoadFinished } ) ; const onlineLoadError = ( error ) => ( { type : Actions . ActionTypes . OnlineLoadError , error } ) ; const onOnlineModeChanged = ( mode ) => ( { type : Actions . ActionTypes . OnlineModeChanged , mode } ) ; const onExit = ( ) => async ( dispatch , getState , dataContext ) => { const server = dataContext . connection ; if ( ! server ) { return ; } try { await dataContext . gameClient . logOutAsync ( ) ; if ( server . connectionId ) { activeConnections . splice ( activeConnections . indexOf ( server . connectionId ) , 1 ) ; } detachListeners ( server ) ; await server . stop ( ) ; dispatch ( navigateToLogin ( ) ) ; } catch ( error ) { alert ( getErrorMessage ( error ) ) ; } } ; const onGamesFilterToggle = ( filter ) => ( { type : Actions . ActionTypes . GamesFilterToggle , filter } ) ; const onGamesSearchChanged = ( search ) => ( { type : Actions . ActionTypes . GamesSearchChanged , search } ) ; const selectGame = ( gameId , showInfo ) => ( { type : Actions . ActionTypes . SelectGame , gameId , showInfo } ) ; const closeGameInfo = ( ) => ( { type : Actions . ActionTypes . CloseGameInfo } ) ; const unselectGame = ( ) => ( { type : Actions . ActionTypes . UnselectGame } ) ; const newAutoGame = ( ) => ( { type : Actions . ActionTypes . NewAutoGame } ) ; const newGame = ( ) => ( { type : Actions . ActionTypes . NewGame } ) ; const newGameCancel = ( ) => ( { type : Actions . ActionTypes . NewGameCancel } ) ; const joinGameStarted = ( ) => ( { type : Actions . ActionTypes . JoinGameStarted } ) ; const joinGameFinished = ( error ) => ( { type : Actions . ActionTypes . JoinGameFinished , error } ) ; const joinGame = ( gameId , role ) => async ( dispatch , getState , dataContext ) => { dispatch ( joinGameStarted ( ) ) ; try { const state = getState ( ) ; const result = await dataContext . gameClient . joinGameAsync ( gameId , role , state . settings . sex === Sex . Male , state . online . password ) ; if ( result . errorMessage ) { dispatch ( joinGameFinished ( `${ localization . joinError }: ${ result . errorMessage }` ) ) ; return ; } dispatch ( gameSet ( gameId , false , role ) ) ; await gameInit ( gameId , dataContext , role ) ; saveStateToStorage ( state ) ; dispatch ( joinGameFinished ( null ) ) ; } catch ( error ) { dispatch ( joinGameFinished ( getErrorMessage ( error ) ) ) ; } } ; const passwordChanged = ( newPassword ) => ( { type : Actions . ActionTypes . PasswordChanged , newPassword } ) ; const chatModeChanged = ( chatMode ) => ( { type : Actions . ActionTypes . ChatModeChanged , chatMode } ) ; const gameCreated = ( game ) => ( { type : Actions . ActionTypes . GameCreated , game } ) ; const gameChanged = ( game ) => ( { type : Actions . ActionTypes . GameChanged , game } ) ; const gameDeleted = ( gameId ) => ( { type : Actions . ActionTypes . GameDeleted , gameId } ) ; const userJoined = ( login ) => ( { type : Actions . ActionTypes . UserJoined , login } ) ; const userLeaved = ( login ) => ( { type : Actions . ActionTypes . UserLeaved , login } ) ; const messageChanged = ( message ) => ( { type : Actions . ActionTypes . MessageChanged , message } ) ; const sendMessage = ( ) => ( dispatch , getState , dataContext ) => { const state = getState ( ) ; const text = state . online . currentMessage . trim ( ) ; if ( text . length > 0 ) { dataContext . gameClient . sayInLobbyAsync ( text ) ; } dispatch ( messageChanged ( '' ) ) ; } ; const windowWidthChanged = ( width ) => ( { type : Actions . ActionTypes . WindowWidthChanged , width } ) ; const gameNameChanged = ( gameName ) => ( { type : Actions . ActionTypes . GameNameChanged , gameName } ) ; const gamePasswordChanged = ( gamePassword ) => ( { type : Actions . ActionTypes . GamePasswordChanged , gamePassword } ) ; const gamePackageTypeChanged = ( packageType ) => ( { type : Actions . ActionTypes . GamePackageTypeChanged , packageType } ) ; const gamePackageDataChanged = ( packageName , packageData ) => ( { type : Actions . ActionTypes . GamePackageDataChanged , packageName , packageData } ) ; const gamePackageLibraryChanged = ( id , name ) => ( { type : Actions . ActionTypes . GamePackageLibraryChanged , name , id } ) ; const gameTypeChanged = ( gameType ) => ( { type : Actions . ActionTypes . GameTypeChanged , gameType } ) ; const gameRoleChanged = ( gameRole ) => ( { type : Actions . ActionTypes . GameRoleChanged , gameRole } ) ; const showmanTypeChanged = ( isHuman ) => ( { type : Actions . ActionTypes . ShowmanTypeChanged , isHuman } ) ; const playersCountChanged = ( playersCount ) => ( { type : Actions . ActionTypes . PlayersCountChanged , playersCount } ) ; const humanPlayersCountChanged = ( humanPlayersCount ) => ( { type : Actions . ActionTypes . HumanPlayersCountChanged , humanPlayersCount } ) ; const gameCreationStart = ( ) => ( { type : Actions . ActionTypes . GameCreationStart } ) ; const gameCreationEnd = ( error = null ) => ( { type : Actions . ActionTypes . GameCreationEnd , error } ) ; const uploadPackageStarted = ( ) => ( { type : Actions . ActionTypes . UploadPackageStarted } ) ; const uploadPackageFinished = ( ) => ( { type : Actions . ActionTypes . UploadPackageFinished } ) ; const uploadPackageProgress = ( progress ) => ( { type : Actions . ActionTypes . UploadPackageProgress , progress } ) ; async function loadGamesAsync ( dispatch , gameClient ) { dispatch ( clearGames ( ) ) ; let gamesSlice = { data : [ ] , isLastSlice : false } ; let whileGuard = 100 ; do { const fromId = gamesSlice . data . length > 0 ? gamesSlice . data [ gamesSlice . data . length - 1 ] . gameID + 1 : 0 ; gamesSlice = await gameClient . getGamesSliceAsync ( fromId ) ; dispatch ( receiveGames ( gamesSlice . data ) ) ; whileGuard -- ; } while ( ! gamesSlice . isLastSlice && whileGuard > 0 ) ; } function uploadPackageAsync ( packageHash , packageData , serverUri , dispatch ) { dispatch ( uploadPackageStarted ( ) ) ; const formData = new FormData ( ) ; formData . append ( 'file' , packageData , packageData . name ) ; return new Promise < boolean > ( ( resolve , reject ) => { const xhr = new XMLHttpRequest ( ) ; xhr . onload = ( ) => { dispatch ( uploadPackageFinished ( ) ) ; if ( xhr . status >= 200 && xhr . status < 300 ) { resolve ( true ) ; } else { reject ( new Error ( xhr . response ) ) ; } } ; xhr . onerror = ( ) => { dispatch ( uploadPackageFinished ( ) ) ; reject ( new Error ( xhr . statusText || xhr . responseText || localization . unknownError ) ) ; } ; xhr . upload . onprogress = ( e ) => { dispatch ( uploadPackageProgress ( e . loaded / e . total ) ) ; } ; xhr . open ( 'post' , `${ serverUri }/api/upload/package` , true ) ; xhr . setRequestHeader ( 'Content-MD5' , packageHash ) ; xhr . withCredentials = true ; xhr . send ( formData ) ; } ) ; } async function hashData ( data ) { if ( location . protocol === 'https:' ) { await crypto . subtle . digest ( 'SHA-1' , data ) ; } return Rusha . createHash ( ) . update ( data ) . digest ( ) ; } async function checkAndUploadPackageAsync ( gameClient , serverUri , packageData , dispatch ) { const zip = new JSZip ( ) ; await zip . loadAsync ( packageData ) ; const contentFile = zip . file ( 'content.xml' ) ; if ( ! contentFile ) { throw new Error ( localization . corruptedPackage ) ; } const content = await contentFile . async ( 'text' ) ; const parser = new DOMParser ( ) ; const xmlDoc = parser . parseFromString ( content . substring ( 39 ) , 'application/xml' ) ; const id = xmlDoc . getElementsByTagName ( 'package' ) [ 0 ] . getAttribute ( 'id' ) ; const hash = await hashData ( await packageData . arrayBuffer ( ) ) ; const hashArray = new Uint8Array ( hash ) ; const hashArrayEncoded = btoa ( String . fromCharCode . apply ( null , hashArray as any ) ) ; const packageKey = { name : packageData . name , hash : hashArrayEncoded , id } ; const hasPackage = await gameClient . hasPackageAsync ( packageKey ) ; if ( ! hasPackage ) { await uploadPackageAsync ( hashArrayEncoded , packageData , serverUri , dispatch ) ; } return packageKey ; } function getRandomValue ( ) { const array = new Uint32Array ( 1 ) ; crypto . getRandomValues ( array ) ; return array [ 0 ] ; } const createNewGame = ( isSingleGame ) => async ( dispatch , getState , dataContext ) => { const state = getState ( ) ; if ( state . game . name . length === 0 || state . common . computerAccounts === null ) { dispatch ( gameCreationEnd ( localization . gameNameMustBeSpecified ) ) ; return ; } dispatch ( gameCreationStart ( ) ) ; const game = isSingleGame ? { ... state . game , password : getRandomValue ( ) . toString ( ) , isShowmanHuman : false , humanPlayersCount : 0 } : state . game ; const { playersCount , humanPlayersCount , role } = game ; const me = { Name : state . user . login , IsHuman : true , IsMale : state . settings . sex === Sex . Male } ; const showman = role === Role . Showman ? me : game . isShowmanHuman ? { Name : Constants . ANY_NAME , IsHuman : true } : { Name : localization . defaultShowman } ; const players = [ ] ; const viewers = [ ] ; if ( role === Role . Viewer ) { viewers . push ( me ) ; } else if ( role === Role . Player ) { players . push ( me ) ; } const compPlayersCount = playersCount - humanPlayersCount - ( role === Role . Player ? 1 : 0 ) ; const compIndicies = [ ] ; for ( let i = 0 ; i < state . common . computerAccounts . length ; i ++ ) { compIndicies . push ( i ) ; } for ( let i = 0 ; i < humanPlayersCount ; i ++ ) { players . push ( { Name : Constants . ANY_NAME , IsHuman : true } ) ; } for ( let i = 0 ; i < compPlayersCount ; i ++ ) { const ind = Math . floor ( Math . random ( ) * compIndicies . length ) ; players . push ( { Name : state . common . computerAccounts [ compIndicies [ ind ] ] , IsHuman : false } ) ; compIndicies . splice ( ind , 1 ) ; } const timeSettings = { TimeForChoosingQuestion : 30 , TimeForThinkingOnQuestion : 5 , TimeForPrintingAnswer : 25 , TimeForGivingACat : 30 , TimeForMakingStake : 30 , TimeForThinkingOnSpecial : 25 , TimeOfRound : 660 , TimeForChoosingFinalTheme : 30 , TimeForFinalThinking : 45 , TimeForShowmanDecisions : 30 , TimeForRightAnswer : 2 , TimeForMediaDelay : 0 } ; const gameMode = game . type ; const appSettings = { TimeSettings : timeSettings , ReadingSpeed : 20 , FalseStart : state . settings . appSettings . falseStart , HintShowman : state . settings . appSettings . hintShowman , Oral : state . settings . appSettings . oral , IgnoreWrong : false , GameMode : gameMode . toString ( ) , RandomQuestionsBasePrice : gameMode === GameType . Simple ? 10 : 100 , RandomRoundsCount : gameMode === GameType . Simple ? 1 : 3 , RandomThemesCount : gameMode === GameType . Simple ? 5 : 6 , Culture : 'ru-RU' } ; const gameSettings = { HumanPlayerName : state . user . login , RandomSpecials : game . package . type === PackageType . Random , NetworkGameName : game . name , NetworkGamePassword : game . password , AllowViewers : true , Showman : showman , Players : players , Viewers : viewers , AppSettings : appSettings } ; try { const packageKey = await ( async ( ) : Promise < PackageKey | null > => { switch ( game . package . type ) { case PackageType . Random : return { name : '' , hash : null , id : null } ; case PackageType . File : return game . package . data ? checkAndUploadPackageAsync ( dataContext . gameClient , dataContext . serverUri , game . package . data , dispatch ) : null ; case PackageType . SIStorage : return { name : null , hash : null , id : game . package . id } ; default : return null ; } } ) ( ) ; if ( ! packageKey ) { dispatch ( gameCreationEnd ( localization . badPackage ) ) ; return ; } const result = await dataContext . gameClient . createAndJoinGameAsync ( gameSettings , packageKey , state . settings . sex === Sex . Male ) ; saveStateToStorage ( state ) ; dispatch ( gameCreationEnd ( ) ) ; if ( result . code > 0 ) { dispatch ( gameCreationEnd ( GameErrorsHelper . getMessage ( result . code ) + ( result . errorMessage || '' ) ) ) ; } else { dispatch ( newGameCancel ( ) ) ; dispatch ( gameSet ( result . gameId , false , role ) ) ; await gameInit ( result . gameId , dataContext , role ) ; } } catch ( error ) { dispatch ( gameCreationEnd ( getErrorMessage ( error ) ) ) ; } } ; const createNewAutoGame = ( ) => async ( dispatch , getState , dataContext ) => { const state = getState ( ) ; dispatch ( gameCreationStart ( ) ) ; try { const result = await dataContext . gameClient . createAutomaticGameAsync ( state . user . login , state . settings . sex === Sex . Male ) ; saveStateToStorage ( state ) ; dispatch ( gameCreationEnd ( ) ) ; if ( result . code > 0 ) { alert ( GameErrorsHelper . getMessage ( result . code ) + ( result . errorMessage || '' ) ) ; } else { dispatch ( gameSet ( result . gameId , true , Role . Player ) ) ; await gameInit ( result . gameId , dataContext , Role . Player ) ; } } catch ( message ) { dispatch ( gameCreationEnd ( message ) ) ; } } ; const gameSet = ( id , isAutomatic , role ) => ( { type : Actions . ActionTypes . GameSet , id , isAutomatic , role } ) ; async function gameInit ( gameId , dataContext , role ) { if ( dataContext . config . rewriteUrl ) { window . history . pushState ( { } , `${ localization . game } ${ gameId }` , `${ dataContext . config . rootUri }?gameId=${ gameId }` ) ; } await dataContext . gameClient . sendMessageToServerAsync ( 'INFO' ) ; if ( role === Role . Player || role === Role . Showman ) { await dataContext . gameClient . sendMessageToServerAsync ( 'READY' ) ; } } const searchPackages = ( ) => ( { type : Actions . ActionTypes . SearchPackages } ) ; const searchPackagesFinished = ( packages ) => ( { type : Actions . ActionTypes . SearchPackagesFinished , packages } ) ; const receiveAuthors = ( ) => ( { type : Actions . ActionTypes . ReceiveAuthors } ) ; const receiveAuthorsFinished = ( authors ) => ( { type : Actions . ActionTypes . ReceiveAuthorsFinished , authors } ) ; const receiveTags = ( ) => ( { type : Actions . ActionTypes . ReceiveTags } ) ; const receiveTagsFinished = ( tags ) => ( { type : Actions . ActionTypes . ReceiveTagsFinished , tags } ) ; const receivePublishers = ( ) => ( { type : Actions . ActionTypes . ReceivePublishers } ) ; const receivePublishersFinished = ( publishers ) => ( { type : Actions . ActionTypes . ReceivePublishersFinished , publishers } ) ; const receiveAuthorsThunk = ( ) => async ( dispatch , getState , dataContext ) => { try { dispatch ( receiveAuthors ( ) ) ; const { apiUri } = dataContext . config ; const response = await fetch ( `${ apiUri }/Authors` ) ; const data = await response . json ( ) ; dispatch ( receiveAuthorsFinished ( data ) ) ; } catch ( error ) { console . error ( error ) ; } } ; const receiveTagsThunk = ( ) => async ( dispatch , getState , dataContext ) => { try { dispatch ( receiveTags ( ) ) ; const { apiUri } = dataContext . config ; const response = await fetch ( `${ apiUri }/Tags` ) ; const data = await response . json ( ) ; dispatch ( receiveTagsFinished ( data ) ) ; } catch ( error ) { console . error ( error ) ; } } ; const receivePublishersThunk = ( ) => async ( dispatch , getState , dataContext ) => { try { dispatch ( receivePublishers ( ) ) ; const { apiUri } = dataContext . config ; const response = await fetch ( `${ apiUri }/Publishers` ) ; const data = await response . json ( ) ; dispatch ( receivePublishersFinished ( data ) ) ; } catch ( error ) { console . error ( error ) ; } } ; const searchPackagesThunk = ( filters = { difficultyRelation : 0 , difficulty : 1 , sortMode : 0 , sortAscending : true , authorId : null , publisherId : null , tagId : null , restriction : null } ) => async ( dispatch , getState , dataContext ) => { try { dispatch ( searchPackages ( ) ) ; const { apiUri } = dataContext . config ; const response = await fetch ( `${ apiUri }/FilteredPackages?${ new URLSearchParams ( Object . entries ( filters ) . filter ( ( [ , value ] ) => value ?? false ) . reduce ( ( acc , [ key , value ] ) => ( { ... acc , [ key ] : value } ) , { } ) ) }` ) ; const data = await response . json ( ) ; dispatch ( searchPackagesFinished ( data ) ) ; } catch ( error ) { console . error ( error ) ; } } ; const actionCreators = { saveStateToStorage , onConnectionChanged , computerAccountsChanged , navigateToLogin , showSettings , navigateToHowToPlay , navigateBack , onLoginChanged , login , navigateToWelcome , singlePlay , friendsPlay , navigateToLobby , onOnlineModeChanged , onExit , onGamesFilterToggle , onGamesSearchChanged , selectGame , closeGameInfo , unselectGame , newAutoGame , newGame , newGameCancel , joinGame , passwordChanged , chatModeChanged , gameCreated , gameChanged , gameDeleted , userJoined , userLeaved , messageChanged , sendMessage , receiveMessage , windowWidthChanged , gameNameChanged , gamePasswordChanged , gamePackageTypeChanged , gamePackageDataChanged , gameTypeChanged , gameRoleChanged , showmanTypeChanged , playersCountChanged , humanPlayersCountChanged , createNewGame , createNewAutoGame , searchPackagesThunk , receiveAuthorsThunk , receiveTagsThunk , receivePublishersThunk , gamePackageLibraryChanged } ; export default actionCreators ;