diff --git a/.gitignore b/.gitignore index 6070f29..50160e5 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ dist/ wasm/assets/ *.wasm *.doodad +*.level docker/ubuntu docker/debian docker/fedora diff --git a/pkg/config.go b/pkg/config.go index c664ea9..57bf52b 100644 --- a/pkg/config.go +++ b/pkg/config.go @@ -43,12 +43,8 @@ func (d *Doodle) EditFile(filename string) error { return d.EditDrawing(filename) } - // WASM: no filesystem access, can go no further. - if runtime.GOOS == "js" { - return fmt.Errorf("EditFile(%s): not found for WASM and can go no further", filename) - } - - // Check the user's levels directory. + // Check the user's levels directory. In WASM this will check in + // localStorage. if foundFilename := userdir.ResolvePath(filename, extension, false); foundFilename != "" { log.Info("EditFile: resolved name '%s' to path %s", filename, foundFilename) absPath = foundFilename diff --git a/pkg/doodads/fmt_readwrite.go b/pkg/doodads/fmt_readwrite.go index fab3ae5..fcc2fd3 100644 --- a/pkg/doodads/fmt_readwrite.go +++ b/pkg/doodads/fmt_readwrite.go @@ -68,6 +68,14 @@ func LoadFile(filename string) (*Doodad, error) { // WASM: try the file over HTTP ajax request. if runtime.GOOS == "js" { + if result, ok := wasm.GetSession(filename); ok { + log.Info("recall doodad data from localStorage") + return FromJSON(filename, []byte(result)) + } + + // TODO: ajax load for doodads might not work, filesystem.FindFile returns + // the base file for WASM but for now force it to system doodads path + filename = "assets/doodads/" + filename jsonData, err := wasm.HTTPGet(filename) if err != nil { return nil, err @@ -90,13 +98,19 @@ func (d *Doodad) WriteFile(filename string) error { d.Version = 1 d.GameVersion = branding.Version - // bin, err := m.ToBinary() bin, err := d.ToJSON() if err != nil { return err } - // Save it to their profile directory. + // WASM: place in localStorage. + if runtime.GOOS == "js" { + log.Info("wasm: write %s to localStorage", filename) + wasm.SetSession(filename, string(bin)) + return nil + } + + // Desktop: write to disk. filename = userdir.DoodadPath(filename) log.Info("Write Doodad: %s", filename) err = ioutil.WriteFile(filename, bin, 0644) diff --git a/pkg/filesystem/filesystem.go b/pkg/filesystem/filesystem.go index f2bb7d1..956f24d 100644 --- a/pkg/filesystem/filesystem.go +++ b/pkg/filesystem/filesystem.go @@ -105,7 +105,7 @@ func FindFile(filename string) (string, error) { // WASM: can't check the filesystem. Let the caller go ahead and try // loading via ajax request. if runtime.GOOS == "js" { - return candidate, nil + return filename, nil } // external system level? @@ -133,7 +133,7 @@ func FindFile(filename string) (string, error) { // WASM: can't check the filesystem. Let the caller go ahead and try // loading via ajax request. if runtime.GOOS == "js" { - return candidate, nil + return filename, nil } // external system doodad? diff --git a/pkg/level/fmt_readwrite.go b/pkg/level/fmt_readwrite.go index 46c8a89..ca39bf3 100644 --- a/pkg/level/fmt_readwrite.go +++ b/pkg/level/fmt_readwrite.go @@ -61,8 +61,14 @@ func LoadFile(filename string) (*Level, error) { return FromJSON(filename, jsonData) } - // WASM: try the file over HTTP ajax request. + // WASM: try the file from localStorage or HTTP ajax request. if runtime.GOOS == "js" { + if result, ok := wasm.GetSession(filename); ok { + log.Info("recall level data from localStorage") + return FromJSON(filename, []byte(result)) + } + + // Ajax request. jsonData, err := wasm.HTTPGet(filename) if err != nil { return nil, err @@ -98,7 +104,6 @@ func (m *Level) WriteFile(filename string) error { m.Version = 1 m.GameVersion = branding.Version - // bin, err := m.ToBinary() bin, err := m.ToJSON() if err != nil { return err @@ -107,6 +112,15 @@ func (m *Level) WriteFile(filename string) error { // Save it to their profile directory. filename = userdir.LevelPath(filename) log.Info("Write Level: %s", filename) + + // WASM: place in localStorage. + if runtime.GOOS == "js" { + log.Info("wasm: write %s to localStorage", filename) + wasm.SetSession(filename, string(bin)) + return nil + } + + // Desktop: write to disk. err = ioutil.WriteFile(filename, bin, 0644) if err != nil { return fmt.Errorf("level.WriteFile: %s", err) diff --git a/pkg/userdir/userdir.go b/pkg/userdir/userdir.go index dbe3df0..85ec48d 100644 --- a/pkg/userdir/userdir.go +++ b/pkg/userdir/userdir.go @@ -7,7 +7,7 @@ import ( "runtime" "strings" - "git.kirsle.net/apps/doodle/pkg/log" + "git.kirsle.net/apps/doodle/pkg/wasm" "github.com/kirsle/configdir" ) @@ -77,6 +77,11 @@ func CacheFilename(filename ...string) string { func ListDoodads() ([]string, error) { var names []string + // WASM: list from localStorage. + if runtime.GOOS == "js" { + return wasm.StorageKeys(DoodadDirectory + "/"), nil + } + files, err := ioutil.ReadDir(DoodadDirectory) if err != nil { return names, err @@ -96,6 +101,11 @@ func ListDoodads() ([]string, error) { func ListLevels() ([]string, error) { var names []string + // WASM: list from localStorage. + if runtime.GOOS == "js" { + return wasm.StorageKeys(LevelDirectory + "/"), nil + } + files, err := ioutil.ReadDir(LevelDirectory) if err != nil { return names, err @@ -133,15 +143,11 @@ func resolvePath(directory, filename, extension string) string { // existed. So the filename should have a ".level" or ".doodad" extension and // then this path will resolve the ProfileDirectory of the file. func ResolvePath(filename, extension string, one bool) string { - // WASM has no file system. - if runtime.GOOS == "js" { - log.Error("userdir.ResolvePath: not supported in WASM build") - return "" - } - // If the filename exists outright, return it. - if _, err := os.Stat(filename); !os.IsNotExist(err) { - return filename + if !(runtime.GOOS == "js") { + if _, err := os.Stat(filename); !os.IsNotExist(err) { + return filename + } } var paths []string @@ -157,6 +163,15 @@ func ResolvePath(filename, extension string, one bool) string { } for _, test := range paths { + // WASM: check the path in localStorage. + if runtime.GOOS == "js" { + if _, ok := wasm.GetSession(test); ok { + return test + } + continue + } + + // Desktop: test the filesystem. if _, err := os.Stat(test); os.IsNotExist(err) { continue } diff --git a/pkg/wasm/localstorage.go b/pkg/wasm/localstorage.go index 584a7d1..96df402 100644 --- a/pkg/wasm/localstorage.go +++ b/pkg/wasm/localstorage.go @@ -2,6 +2,12 @@ package wasm +// StorageKeys returns the list of localStorage keys matching a prefix. +// This is a no-op when not in wasm. +func StorageKeys(prefix string) []string { + return []string{} +} + // SetSession sets a binary value on sessionStorage. // This is a no-op when not in wasm. func SetSession(key string, value string) { diff --git a/pkg/wasm/localstorage_js.go b/pkg/wasm/localstorage_js.go index eb8074d..dc7ea58 100644 --- a/pkg/wasm/localstorage_js.go +++ b/pkg/wasm/localstorage_js.go @@ -3,20 +3,37 @@ package wasm import ( + "strings" "syscall/js" + + "git.kirsle.net/apps/doodle/pkg/log" ) +// StorageKeys returns the list of localStorage keys matching a prefix. +func StorageKeys(prefix string) []string { + keys := js.Global().Get("Object").Call("keys", js.Global().Get("localStorage")) + + var result []string + for i := 0; i < keys.Length(); i++ { + value := keys.Index(i).String() + if strings.HasPrefix(value, prefix) { + result = append(result, + strings.TrimPrefix(keys.Index(i).String(), prefix), + ) + } + } + log.Info("LS KEYS: %+v", result) + return result +} + // SetSession sets a text value on sessionStorage. func SetSession(key string, value string) { - // b64 := base64.StdEncoding.EncodeToString(value) - panic("SesSession: " + key) - js.Global().Get("sessionStorage").Call("setItem", key, value) + js.Global().Get("localStorage").Call("setItem", key, value) } // GetSession retrieves a text value from sessionStorage. func GetSession(key string) (string, bool) { - panic("GetSession: " + key) var value js.Value - value = js.Global().Get("sessionStorage").Call("getItem", key) + value = js.Global().Get("localStorage").Call("getItem", key) return value.String(), value.Type() == js.TypeString }