Major updates

Automatic type conversion
Support struct data
Custom error handler
This commit is contained in:
Lea Anthony 2020-08-31 20:00:39 +10:00
commit 43a5f410d9
6 changed files with 139 additions and 20 deletions

File diff suppressed because one or more lines are too long

View file

@ -20,36 +20,53 @@ export function New(name, optionalDefault) {
var data;
// Check we are initialised
if( !window.wails) {
throw Error('Wails is not initialised');
}
// Store for the callbacks
let callbacks = [];
// Subscribe to updates by providing a callback
this.subscribe = (callback) => {
callbacks.push(callback);
};
// sets the store data to the provided `newdata` value
this.set = (newdata) => {
data = newdata;
// Emit the data
window.wails.Events.Emit('wails:sync:store:updated:'+name, data);
// Emit a notification to back end
window.wails.Events.Emit('wails:sync:store:updatedbyfrontend:'+name, JSON.stringify(data));
// Notify callbacks
callbacks.forEach( function(callback) {
callback(data);
});
};
// update mutates the value in the store by calling the
// provided method with the current value. The value returned
// by the updater function will be set as the new store value
this.update = (updater) => {
var newValue = updater(data);
this.set(newValue);
};
// Setup event listener
window.wails.Events.On('wails:sync:store:updated:'+name, function(result) {
// Setup event callback
window.wails.Events.On('wails:sync:store:updatedbybackend:'+name, function(result) {
// Parse data
result = JSON.parse(result);
// Todo: Potential preprocessing?
// Save data
data = result;
// Notify listeners
// Notify callbacks
callbacks.forEach( function(callback) {
callback(data);
});

View file

@ -1,5 +1,13 @@
// Package runtime contains all the methods and data structures related to the
// runtime library of Wails. This includes both Go and JS runtimes.
package runtime
import (
"bytes"
"encoding/json"
"reflect"
)
// Options defines the optional data that may be used
// when creating a Store
type Options struct {
@ -16,10 +24,12 @@ type Options struct {
NotifySynchronously bool
}
// StoreProvider is a struct that creates Stores
type StoreProvider struct {
runtime *Runtime
}
// NewStoreProvider creates new stores using the provided Runtime reference.
func NewStoreProvider(runtime *Runtime) *StoreProvider {
return &StoreProvider{
runtime: runtime,
@ -30,21 +40,28 @@ func NewStoreProvider(runtime *Runtime) *StoreProvider {
type Store struct {
name string
data interface{}
dataType reflect.Type
structType bool
eventPrefix string
callbacks []func(interface{})
runtime *Runtime
notifySynchronously bool
// Error handler
errorHandler func(error)
}
// New creates a new store
func (p *StoreProvider) New(name string) *Store {
func (p *StoreProvider) New(name string, defaultValue interface{}) *Store {
result := Store{
name: name,
runtime: p.runtime,
data: defaultValue,
}
result.setupListner()
// initialise the store
result.init()
return &result
}
@ -60,12 +77,90 @@ func (p *StoreProvider) NewWithOptions(options Options) *Store {
return &result
}
func (s *Store) setupListner() {
// Setup listener
s.runtime.Events.On("wails:sync:store:updated:"+s.name, func(data ...interface{}) {
// OnError takes a function that will be called
// whenever an error occurs
func (s *Store) OnError(callback func(error)) {
s.errorHandler = callback
}
// store the data
s.data = data[0]
// init the store
func (s *Store) init() {
// Get the type of the data
s.dataType = reflect.TypeOf(s.data)
// Determine if this is a struct type
s.structType = s.dataType.Kind() == reflect.Ptr
// Setup the sync listener
s.setupListener()
}
// processUpdatedScalar will process the given scalar json
func (s *Store) processUpdatedScalar(data json.RawMessage) error {
// Unmarshall the value
var decodedVal interface{}
err := json.Unmarshal(data, &decodedVal)
if err != nil {
return err
}
// Convert to correct type
if decodedVal == nil {
s.data = reflect.Zero(s.dataType).Interface()
} else {
s.data = reflect.ValueOf(decodedVal).Convert(s.dataType).Interface()
}
return nil
}
// processUpdatedStruct will process the given struct json
func (s *Store) processUpdatedStruct(data json.RawMessage) error {
newData := reflect.New(s.dataType.Elem()).Interface()
err := json.Unmarshal(data, &newData)
if err != nil {
return err
}
s.data = newData
return nil
}
// Processes the updates sent by the front end
func (s *Store) processUpdatedData(data string) error {
var rawdata json.RawMessage
d := json.NewDecoder(bytes.NewBufferString(data))
err := d.Decode(&rawdata)
if err != nil {
return err
}
// If it's a struct process it differently
if s.structType {
return s.processUpdatedStruct(rawdata)
}
return s.processUpdatedScalar(rawdata)
}
// Setup listener for front end changes
func (s *Store) setupListener() {
// Listen for updates from the front end
s.runtime.Events.On("wails:sync:store:updatedbyfrontend:"+s.name, func(data ...interface{}) {
// Process the incoming data
err := s.processUpdatedData(data[0].(string))
if err != nil {
if s.errorHandler != nil {
s.errorHandler(err)
}
}
// Notify listeners
s.notify()
@ -94,10 +189,18 @@ func (s *Store) Set(data interface{}) {
// Save data
s.data = data
// Emit event
s.runtime.Events.Emit("wails:sync:store:updated:"+s.name, s.data)
// Stringify data
newdata, err := json.Marshal(s.data)
if err != nil {
if s.errorHandler != nil {
s.errorHandler(err)
}
}
// Notify listeners
// Emit event to front end
s.runtime.Events.Emit("wails:sync:store:updatedbybackend:"+s.name, string(newdata))
// Notify subscribers
s.notify()
}