Skip to content
Snippets Groups Projects
Select Git revision
  • ec5523a0e403b555b1373b425c268a27e2cd292f
  • release default
  • 11-22-implement-kv-interface-defined-in-collectiveversionedkvgo
  • master protected
  • XX-4688/DbEncoding
  • hotfix/update
  • @XX-4682/Files
  • hotfix/XX-4655
  • dev protected
  • project/HavenNotifications
  • XX-4602/SilentMessageType
  • jono/npmTest
  • wasmTest2
  • XX-4461/FileUpload
  • XX-4505/blockuser
  • XX-4441
  • Jakub/Emoji-CI-Test
  • testing/websockets
  • fastReg
  • fast-registration
  • NewHostPool
  • v0.3.22
  • v0.3.21
  • v0.3.20
  • v0.3.18
  • v0.3.17
  • v0.3.16
  • v0.3.15
  • v0.3.14
  • v0.3.13
  • v0.3.12
  • v0.3.11
  • v0.3.10
  • v0.3.9
  • v0.3.8
  • v0.3.7
  • v0.3.6
  • v0.3.5
  • v0.3.4
  • 812b395df518ce096d01d5292596ca26f8fe92d9c4487ddfa515e190a51aa1a1
  • 76ba08e2dfa1798412a265404fa271840b52c035869111fce8e8cdb23a036a5a
41 results

localStorage.go

Blame
  • localStorage.go 6.81 KiB
    ////////////////////////////////////////////////////////////////////////////////
    // Copyright © 2022 xx foundation                                             //
    //                                                                            //
    // Use of this source code is governed by a license that can be found in the  //
    // LICENSE file.                                                              //
    ////////////////////////////////////////////////////////////////////////////////
    
    //go:build js && wasm
    
    package storage
    
    import (
    	"encoding/base64"
    	"encoding/json"
    	jww "github.com/spf13/jwalterweatherman"
    	"gitlab.com/elixxir/xxdk-wasm/utils"
    	"os"
    	"strings"
    	"syscall/js"
    )
    
    // localStorageWasmPrefix is prefixed to every keyName saved to local storage by
    // LocalStorage. It allows the identifications and deletion of keys only created
    // by this WASM binary while ignoring keys made by other scripts on the same
    // page.
    const localStorageWasmPrefix = "xxdkWasmStorage/"
    
    // LocalStorage contains the js.Value representation of localStorage.
    type LocalStorage struct {
    	// The Javascript value containing the localStorage object
    	v js.Value
    
    	// The prefix appended to each key name. This is so that all keys created by
    	// this structure can be deleted without affecting other keys in local
    	// storage.
    	prefix string
    }
    
    // jsStorage is the global that stores Javascript as window.localStorage.
    //
    //   - Specification:
    //     https://html.spec.whatwg.org/multipage/webstorage.html#dom-localstorage-dev
    //   - Documentation:
    //     https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage
    var jsStorage = newLocalStorage(localStorageWasmPrefix)
    
    // newLocalStorage creates a new LocalStorage object with the specified prefix.
    func newLocalStorage(prefix string) *LocalStorage {
    	return &LocalStorage{
    		v:      js.Global().Get("localStorage"),
    		prefix: prefix,
    	}
    }
    
    // GetLocalStorage returns Javascript's local storage.
    func GetLocalStorage() *LocalStorage {
    	return jsStorage
    }
    
    // GetItem returns a key's value from the local storage given its name. Returns
    // os.ErrNotExist if the key does not exist. Underneath, it calls
    // localStorage.GetItem().
    //
    //   - Specification:
    //     https://html.spec.whatwg.org/multipage/webstorage.html#dom-storage-getitem-dev
    //   - Documentation:
    //     https://developer.mozilla.org/en-US/docs/Web/API/Storage/getItem
    func (ls *LocalStorage) GetItem(keyName string) ([]byte, error) {
    	keyValue := ls.getItem(ls.prefix + keyName)
    	if keyValue.IsNull() {
    		return nil, os.ErrNotExist
    	}
    
    	decodedKeyValue, err := base64.StdEncoding.DecodeString(keyValue.String())
    	if err != nil {
    		return nil, err
    	}
    
    	return decodedKeyValue, nil
    }
    
    // SetItem adds a key's value to local storage given its name. Underneath, it
    // calls localStorage.SetItem().
    //
    //   - Specification:
    //     https://html.spec.whatwg.org/multipage/webstorage.html#dom-storage-setitem-dev
    //   - Documentation:
    //     https://developer.mozilla.org/en-US/docs/Web/API/Storage/setItem
    func (ls *LocalStorage) SetItem(keyName string, keyValue []byte) {
    	encodedKeyValue := base64.StdEncoding.EncodeToString(keyValue)
    	ls.setItem(ls.prefix+keyName, encodedKeyValue)
    }
    
    // RemoveItem removes a key's value from local storage given its name. If there
    // is no item with the given key, this function does nothing. Underneath, it
    // calls localStorage.RemoveItem().
    //
    //   - Specification:
    //     https://html.spec.whatwg.org/multipage/webstorage.html#dom-storage-removeitem-dev
    //   - Documentation:
    //     https://developer.mozilla.org/en-US/docs/Web/API/Storage/removeItem
    func (ls *LocalStorage) RemoveItem(keyName string) {
    	ls.removeItem(ls.prefix + keyName)
    }
    
    // Clear clears all the keys in storage. Underneath, it calls
    // localStorage.clear().
    //
    //   - Specification:
    //     https://html.spec.whatwg.org/multipage/webstorage.html#dom-storage-clear-dev
    //   - Documentation:
    //     https://developer.mozilla.org/en-US/docs/Web/API/Storage/clear
    func (ls *LocalStorage) Clear() {
    	ls.clear()
    }
    
    // ClearPrefix clears all keys with the given prefix.
    func (ls *LocalStorage) ClearPrefix(prefix string) {
    	// Get a copy of all key names at once
    	keys := ls.keys()
    
    	// Loop through each key
    	for i := 0; i < keys.Length(); i++ {
    		if v := keys.Index(i); !v.IsNull() {
    			keyName := strings.TrimPrefix(v.String(), ls.prefix)
    			if strings.HasPrefix(keyName, prefix) {
    				ls.removeItem(v.String())
    			}
    		}
    	}
    }
    
    // ClearWASM clears all the keys in storage created by WASM.
    func (ls *LocalStorage) ClearWASM() {
    	// Get a copy of all key names at once
    	keys := ls.keys()
    
    	// Loop through each key
    	for i := 0; i < keys.Length(); i++ {
    		if v := keys.Index(i); !v.IsNull() {
    			keyName := v.String()
    			if strings.HasPrefix(keyName, ls.prefix) {
    				ls.RemoveItem(strings.TrimPrefix(keyName, ls.prefix))
    			}
    		}
    	}
    }
    
    // Key returns the name of the nth key in localStorage. Return os.ErrNotExist if
    // the key does not exist. The order of keys is not defined. If there is no item
    // with the given key, this function does nothing. Underneath, it calls
    // localStorage.key().
    //
    //   - Specification:
    //     https://html.spec.whatwg.org/multipage/webstorage.html#dom-storage-key-dev
    //   - Documentation:
    //     https://developer.mozilla.org/en-US/docs/Web/API/Storage/key
    func (ls *LocalStorage) Key(n int) (string, error) {
    	keyName := ls.key(n)
    	if keyName.IsNull() {
    		return "", os.ErrNotExist
    	}
    
    	return strings.TrimPrefix(keyName.String(), ls.prefix), nil
    }
    
    // Keys returns a list of all key names in local storage.
    func (ls *LocalStorage) Keys() []string {
    	keyNamesJson := utils.JSON.Call("stringify", ls.keys())
    
    	var keyNames []string
    	err := json.Unmarshal([]byte(keyNamesJson.String()), &keyNames)
    	if err != nil {
    		jww.FATAL.Panicf(
    			"Failed to JSON unmarshal localStorage key name list: %+v", err)
    	}
    
    	return keyNames
    }
    
    // Length returns the number of keys in localStorage. Underneath, it accesses
    // the property localStorage.length.
    //
    //   - Specification:
    //     https://html.spec.whatwg.org/multipage/webstorage.html#dom-storage-key-dev
    //   - Documentation:
    //     https://developer.mozilla.org/en-US/docs/Web/API/Storage/length
    func (ls *LocalStorage) Length() int {
    	return ls.length().Int()
    }
    
    // Wrappers for Javascript Storage methods and properties.
    func (ls *LocalStorage) getItem(keyName string) js.Value  { return ls.v.Call("getItem", keyName) }
    func (ls *LocalStorage) setItem(keyName, keyValue string) { ls.v.Call("setItem", keyName, keyValue) }
    func (ls *LocalStorage) removeItem(keyName string)        { ls.v.Call("removeItem", keyName) }
    func (ls *LocalStorage) clear()                           { ls.v.Call("clear") }
    func (ls *LocalStorage) key(n int) js.Value               { return ls.v.Call("key", n) }
    func (ls *LocalStorage) length() js.Value                 { return ls.v.Get("length") }
    func (ls *LocalStorage) keys() js.Value                   { return utils.Object.Call("keys", ls.v) }