KeyValueTest.qml Example File

examples/gamenetwork/GameNetworkTest/qml/KeyValueTest.qml
 import Felgo 3.0
 import QtQuick 2.0

 // required so the WebStorage is known, but only during development
 import "."

 TestBase {
   id: keyValueTest

   property string applicationStorageKey: "key1"
   property string applicationStorageValueStringified: "unset"

   property string userStorageKey: "myHighscoreKey"
   property string userStorageValueStringified: "unset"

   // can either be userStorage, applicationStorage, userStorageValue, applicationStorageValue
   property string textInputMode

   WebStorage {
     id: userStorage

     Component.onCompleted: {
       // we can clear all local values and simulate a fresh WebStorage by calling clearAllLocally() initially here - the server values are not reset
       // alternatively, if you want to clear all webstorage values, use the VPGN property clearAllUserDataAtStartup
       //clearAllLocally()

       // initially, set the slider value to the cached (offline) value of webstorage
       // this is also valid - only if the data was written somewhere else, it is not known until the WebStorage gets initialized

       // reading the value at onCompleted does not hold the latest server value, but the last locally cached value
       var readValue = userStorage.getValue(userStorageKey)
       console.debug("readValue in onCompleted:", readValue)

       // we set the slider value to the cached value here
       setUserStorageValueValue(readValue)

       // this tests setting a value BEFORE initiallyInServerSync is true
       // so this request is sent before a synchWithServer() call
       // this request always takes TWO requests (first the value 3 is writen, and then the allKeys value is written with the updated updatedAt)
       // thus rather call setValue() calls not in onComplted, but only when in sync, because then the value does not need to be re-sent when the values is the same
       // this is also problematic when no connection error exists, because the set_user_data request is not the same as a set_data as it was sent before!
       // it does work though, because we cannot assume that when writing a value initially the user has internet connection and the data is synced
       // calling this is now fixed in WebStorage as of 18.12.2016. internally, we must not overwrite the allkeys before it is not initially synced! thus queue the request until the WS is synced, only then we accept the set_data calls.
       userStorage.setValue("keyBeforeInitiallyInServerSync", 3)

       // if first read and never written before, this is undefined
       if(typeof readValue === "number")
         slider.value = readValue
     }

     onInitiallyInServerSyncChanged: {
       if(initiallyInServerSync) {
         // set the slider value to the now synced key
         var readValue = userStorage.getValue(userStorageKey)

         console.debug("KeyValueTest: initiallyInServerSync, read user value:", readValue)

         setUserStorageValueValue(readValue)
         if(typeof readValue === "number") {
           slider.value = userStorage.getValue(userStorageKey)
         }
       }
     }

     onInServerSyncChanged: {
       console.debug("KeyValueTest: WebStorage.onInServerSyncChanged to", inServerSync)
     }

     onInitiallyInServerSyncOrErrorChanged: {
       console.debug("onInitiallyInServerSync changed to:", initiallyInServerSyncOrError)
     }

     onWriteValueFinished: console.debug("KeyValueTest: writeValueFinished:", JSON.stringify(data))
     onReadValueFinished: console.debug("KeyValueTest: readValueFinished:", JSON.stringify(data))
     onClearValueFinished: console.debug("KeyValueTest: clearValueFinished:", JSON.stringify(data))

     onWriteConflict: {
       console.debug("KeyValueTest: writeConflict:", JSON.stringify(data))

       // it might also be another key, but we update the local key anyways here
       // users need to call a re-read of the value when a write conflict is detected
       var readValue = userStorage.getValue(userStorageKey)
       console.debug("UserStorage readValue stringified after write conflict:", JSON.stringify(readValue))
       setUserStorageValueValue(readValue)
     }

   }

   Item {
     anchors.fill: parent
     clip: true

     SimpleButton {
       // position it a bit below, because the loading rect is at y:0
       y: 30
       anchors.right: parent.right
       color: userStorage.inServerSync ? "green" : "red"
       text: userStorage.inServerSync ? "sync" : "busy"
     }

     Flickable {
       anchors.fill: parent
       contentWidth: column.width
       contentHeight: column.height
       flickableDirection: Flickable.VerticalFlick

       Flow {
         id: column
         spacing: 5
         width: scene.width

         GameSlider {
           id: slider
           minimumValue: 0
           maximumValue: 100
           width: 200
           height: 20
         }

         Text {
           color: "white"
           text: " --- UserStorage, key: " + userStorageKey + " --- "
         }

         SimpleButton {
           text: "Set key"
           onClicked: {
             textInputMode = "userStorage"
             nativeUtils.displayTextInput("UserStorageKey", "", userStorageKey)
           }
         }
         SimpleButton {
           text: "Set value"
           onClicked: {
             textInputMode = "userStorageValue"
             nativeUtils.displayTextInput("UserStorageValue", "", userStorageValueStringified)
           }
         }

         SimpleButton {
           text: "Get value"
           onClicked: {
             var readValue = userStorage.getValue(userStorageKey)
             console.debug("UserStorage readValue stringified:", JSON.stringify(readValue))

             setUserStorageValueValue(readValue)
           }
         }

         SimpleButton {
           text: "Get serverValue"
           onClicked: {
             userStorage.getServerValue(userStorageKey, keyValueTest.myCallback)
           }
         }

         SimpleButton {
           text: "ServerSynch"
           onClicked: {
             // checks if any keys from the server were updated, and uploads any local keys that were not on the server yet
             userStorage.synchWithServer()
           }
         }

         SimpleButton {
           text: "Clear value"
           color: "red"
           onClicked: {
             // useful for testing if clearing the data from the server works - try a sync or readServerValue afterwards and it should return undefined!
             userStorage.clearValue(userStorageKey)
           }
         }

         SimpleButton {
           text: "Clear userStorage"
           color: "red"
           onClicked: {
             // useful for testing how a clean client looks like - logout and login again (or deactivate network which is the same), to simulate a new device
             // at a login then the server data gets synced back to the local userStorage
             userStorage.clearAll()
           }
         }

         SimpleButton {
           text: "Clear local userStorage"
           color: "red"
           onClicked: {
             // useful for testing connecting of a new device, or when the client is logged out and all local data is removed
             // at a login then the server data gets synced back to the local userStorage
             userStorage.clearAllLocally()
           }
         }

         // ------------ User Storage ------------

         SimpleButton {
           text: "Set value to " + Math.round(slider.value)
           onClicked: {
             //gameNetwork.api.setValue(userStorageKey, highscore)
             userStorage.setValue(userStorageKey, Math.round(slider.value), keyValueTest.myCallback)
             setUserStorageValueValue(Math.round(slider.value))
           }
         }
         SimpleButton {
           text: "Increase value by 1 to " + Math.round(slider.value + 1)
           onClicked: {
             // this allows testing multiple requests shortly after each other
             slider.value++
             userStorage.setValue(userStorageKey, Math.round(slider.value), keyValueTest.myCallback)
             setUserStorageValueValue(Math.round(slider.value))
           }
         }

         SimpleButton {
           text: "Set complex value"
           onClicked: {

             /*
         set these values for testing

         1.) press this button, so the data is written to the client and also to the server
         2.) change the server data (from another logged in user or from web backend) to the serverValue below
         3.) the merged data should look as below!

         serverValue: {"key1":"value1","key2":2,"array1":[1,2]}
         clientValue: {"key1":"clientValue1","key3":3,"array1":[5,6,7,8]}
         expected merge: {"key1":"value1","key3":3,"array1":[1,2,7,8],"key2":2}
        */

             userStorage.setValue(userStorageKey, {"key1":"value1","key2":2,"array1":[5,6]}, keyValueTest.myCallback)
           }
         }

         Text {
           color: "white"
           text: "userStorageValue: " + userStorageValueStringified
         }

         // ------------ ApplicationStorage ------------

         /*
   WebStorage {
     id: applicationStorage
     // this marks the storage as an application-wide store
     // this is useful e.g. for storing balancing values that can be read remotely without re-distributing the app
     perUserStore: false
     // set the slider to the userStorage values, not to the applicationStorage values
     databaseName: "applicationStorage"

   }

   Text {
     color: "white"
     text: " --- ApplicationStorage, key: " + applicationStorageKey + " --- "
   }

   SimpleButton {
     text: "Set value to " + Math.round(slider.value)
     onClicked: {
       applicationStorage.setValue(applicationStorageKey, Math.round(slider.value), keyValueTest.myCallback)
     }
   }
   SimpleButton {
     text: "Increase value by 1 to " + Math.round(slider.value + 1)
     onClicked: {
       // this allows testing multiple requests shortly after each other
       slider.value++
       applicationStorage.setValue(applicationStorageKey, Math.round(slider.value))
     }
   }
   SimpleButton {
     text: "Get value"
     onClicked: {
       var readValue = applicationStorage.getValue(applicationStorageKey)
       console.debug("ApplicationStorage readValue:", readValue)
       applicationStorageValueStringified = JSON.stringify(readValue)
     }
   }

   Text {
     color: "white"
     text: "applStorageValue: " + applicationStorageValueStringified
   }
 */

       }// Flow
     }// Flickable
   }// Clipping

   Connections {
     target: nativeUtils

     onTextInputFinished: {

       if(!accepted) {
         textInputMode = ""
         return;
       }

       if(textInputMode === "userStorage") {
         userStorageKey = enteredText
       } else if(textInputMode === "userStorageValue") {

         // the enteredText is always of type string!
         // the read data should be validated! see log for this
         setUserStorageValueValue(enteredText)
         var parsedInput
         try {
           parsedInput = JSON.parse(userStorageValueStringified)
         } catch(e) {
           console.debug("caught parse exception, happens e.g. when a string was entered:", e)
           parsedInput = userStorageValueStringified
         }

         // NOTE: the text is a string, so we need to parse its results to set the value properly! otherwis another parsing of the contents would be needed!
         userStorage.setValue(userStorageKey, parsedInput, keyValueTest.myCallback)
       }

       textInputMode = ""
     }
   }

   function myCallback(data) {
     console.debug("myCallback is called:", JSON.stringify(data))

     if(data["mergedData"]) {
       // update the value, to the merged data that got read
       // if not updated here, we would need to re-read the local value to update its content
       setUserStorageValueValue(data.mergedData)
     }
   }

   function setUserStorageValueValue(value) {

     if(value === undefined) {
       // we could also set it to "undefined"
       userStorageValueStringified = "unset"
     } else if(typeof value === "string") {
       userStorageValueStringified = value
     } else {
       userStorageValueStringified = JSON.stringify(value)
     }

   }
 }

Voted #1 for:

  • Easiest to learn
  • Most time saving
  • Best support

Develop Cross-Platform Apps and Games 50% Faster!

  • Voted the best supported, most time-saving and easiest to learn cross-platform development tool
  • Based on the Qt framework, with native performance and appearance on all platforms including iOS and Android
  • Offers a variety of plugins to monetize, analyze and engage users
FREE!
create apps
create games
cross platform
native performance
3rd party services
game network
multiplayer
level editor
easiest to learn
biggest time saving
best support