diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 57bb307982992558537e7837628bdafb52387d50..00a9f0f079f2cd1328420b9c01ed1e95370f88b2 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -20,7 +20,6 @@ stages: - test - build - tag - - version_check - doc-update - version_check diff --git a/go.mod b/go.mod index 331f7bd62019b6fedb5bfd2b49e1f42ee6302b48..60cdd5d061c3d438431dc54a505ddc5b91b90ee5 100644 --- a/go.mod +++ b/go.mod @@ -7,8 +7,8 @@ require ( github.com/hack-pad/go-indexeddb v0.2.0 github.com/pkg/errors v0.9.1 github.com/spf13/jwalterweatherman v1.1.0 - gitlab.com/elixxir/client/v4 v4.3.12-0.20230111204451-42f3876835cc - gitlab.com/elixxir/crypto v0.0.7-0.20230109232445-64f3e6192c3a + gitlab.com/elixxir/client/v4 v4.3.12-0.20230113154924-09ab9d5f0387 + gitlab.com/elixxir/crypto v0.0.7-0.20230113153754-4be32f0a0a89 gitlab.com/elixxir/primitives v0.0.3-0.20230109222259-f62b2a90b62c gitlab.com/xx_network/crypto v0.0.5-0.20230109222209-557b66d73c33 gitlab.com/xx_network/primitives v0.0.4-0.20221219230308-4b5550a9247d @@ -18,35 +18,25 @@ require ( require ( filippo.io/edwards25519 v1.0.0 // indirect git.xx.network/elixxir/grpc-web-go-client v0.0.0-20221221204132-2ed1fec765f1 // indirect - github.com/agnivade/wasmbrowsertest v0.6.0 // indirect github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect github.com/badoux/checkmail v1.2.1 // indirect github.com/cenkalti/backoff/v4 v4.1.3 // indirect - github.com/chromedp/cdproto v0.0.0-20221108233440-fad8339618ab // indirect - github.com/chromedp/chromedp v0.8.6 // indirect - github.com/chromedp/sysutil v1.0.0 // indirect github.com/cloudflare/circl v1.2.0 // indirect github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect github.com/elliotchance/orderedmap v1.4.0 // indirect github.com/forPelevin/gomoji v1.1.8 // indirect github.com/fsnotify/fsnotify v1.5.4 // indirect - github.com/go-interpreter/wagon v0.6.0 // indirect - github.com/gobwas/httphead v0.1.0 // indirect - github.com/gobwas/pool v0.2.1 // indirect github.com/gobwas/ws v1.1.0 // indirect github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 // indirect github.com/golang/protobuf v1.5.2 // indirect - github.com/google/pprof v0.0.0-20221103000818-d260c55eee4c // indirect github.com/gorilla/websocket v1.5.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/improbable-eng/grpc-web v0.15.0 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect - github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.11.7 // indirect github.com/klauspost/cpuid/v2 v2.1.0 // indirect github.com/magiconair/properties v1.8.6 // indirect - github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-isatty v0.0.14 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect diff --git a/go.sum b/go.sum index 8a21a42deacb65702eb8af57b5a245bd5f093826..0254623295bcaa94abf36d6b831f44b06007c039 100644 --- a/go.sum +++ b/go.sum @@ -548,6 +548,18 @@ gitlab.com/elixxir/client/v4 v4.3.12-0.20230111011720-685725612575 h1:ArQqykxDAq gitlab.com/elixxir/client/v4 v4.3.12-0.20230111011720-685725612575/go.mod h1:03CuoZNfVcZAbD0IrX/FxOMMwaKUWCMeMWE6781ZDIg= gitlab.com/elixxir/client/v4 v4.3.12-0.20230111204451-42f3876835cc h1:c+h+mq8ouaCpUbgjAdvCtlk3zZAvxW4GhyePzHbpocc= gitlab.com/elixxir/client/v4 v4.3.12-0.20230111204451-42f3876835cc/go.mod h1:03CuoZNfVcZAbD0IrX/FxOMMwaKUWCMeMWE6781ZDIg= +gitlab.com/elixxir/client/v4 v4.3.12-0.20230112174912-de174933d587 h1:kdG+nQlSaPOmSEe/YvVK3fria8OZj/bHpZEO4aBXr8M= +gitlab.com/elixxir/client/v4 v4.3.12-0.20230112174912-de174933d587/go.mod h1:hx/wYHaAvX9hse9vUWIzE/4IqWFFMbCDMPuGQAaAGDQ= +gitlab.com/elixxir/client/v4 v4.3.12-0.20230112175132-70ac938c67eb h1:B86ZcS4AdozJlY/GNuzivHjOSPYS3rebqiAwH59yXqI= +gitlab.com/elixxir/client/v4 v4.3.12-0.20230112175132-70ac938c67eb/go.mod h1:hx/wYHaAvX9hse9vUWIzE/4IqWFFMbCDMPuGQAaAGDQ= +gitlab.com/elixxir/client/v4 v4.3.12-0.20230112205052-fc2a77781cff h1:uWWdqP2ZpwabVdrNHxz5l7u607vk7ATD5QVzy5kgkvU= +gitlab.com/elixxir/client/v4 v4.3.12-0.20230112205052-fc2a77781cff/go.mod h1:nWdt+YuNQ5T5LhshVpAuVnYqaVASCFMIyFgG2h8V+is= +gitlab.com/elixxir/client/v4 v4.3.12-0.20230112234243-43cd8e0f61df h1:pq6pT5m4TivgDZNyZdqpK0G/iNeQnBCK4U9kh/arjFE= +gitlab.com/elixxir/client/v4 v4.3.12-0.20230112234243-43cd8e0f61df/go.mod h1:ohWuyRXcETnCnx8X1+WNQhh70wqLd9GQYQVD0YaKW+4= +gitlab.com/elixxir/client/v4 v4.3.12-0.20230113004731-2099bf23f6b7 h1:uafHvqWkuAe/0/TLe/mi14uCE1utOxk3/vBwV32QzCs= +gitlab.com/elixxir/client/v4 v4.3.12-0.20230113004731-2099bf23f6b7/go.mod h1:ohWuyRXcETnCnx8X1+WNQhh70wqLd9GQYQVD0YaKW+4= +gitlab.com/elixxir/client/v4 v4.3.12-0.20230113154924-09ab9d5f0387 h1:EEJ2MOgGzGcu/Pr6xPBFBLPZDPowShPCsyD0sTRmC2Q= +gitlab.com/elixxir/client/v4 v4.3.12-0.20230113154924-09ab9d5f0387/go.mod h1:akit+kacZtAbLxE6DkOJDAy8LfYOw2gy385IQJyDWCY= gitlab.com/elixxir/comms v0.0.4-0.20230109184457-e10f20295430 h1:OydFdoBbLz5iFzCiYEb+m8Q2pZjdVVCne4m+MyFAzUc= gitlab.com/elixxir/comms v0.0.4-0.20230109184457-e10f20295430/go.mod h1:aFnxDpIxEEFHdAa2dEeydzo00u/IAcfrqPSEnmeffbY= gitlab.com/elixxir/comms v0.0.4-0.20230109233320-a0c90d3324a0 h1:jMmI+j4P5e+nmf82xKs679M6EzeuUhQJjOwXhXi6Cl0= @@ -556,6 +568,14 @@ gitlab.com/elixxir/crypto v0.0.7-0.20230109182503-bd51c95bdcb3 h1:5au07K9R4K4RMR gitlab.com/elixxir/crypto v0.0.7-0.20230109182503-bd51c95bdcb3/go.mod h1:7whUm4bnEdEoiVfMnu3TbHgvlrz0Ywp/Tekqg2Wl7vw= gitlab.com/elixxir/crypto v0.0.7-0.20230109232445-64f3e6192c3a h1:XjE1WZk9DDyw2Rb7p14PWdlpMmsQJFkKVRcMpgMakUQ= gitlab.com/elixxir/crypto v0.0.7-0.20230109232445-64f3e6192c3a/go.mod h1:5RnVcYvjX6Q0+2Rg/PcWgqJSrhOHgzw14YLbd0AshjE= +gitlab.com/elixxir/crypto v0.0.7-0.20230112173817-b922d4a512ed h1:P5wgqwsvnEMJVnerv7dZ/zP0FcjrqJ9zZRaC9N4XEEo= +gitlab.com/elixxir/crypto v0.0.7-0.20230112173817-b922d4a512ed/go.mod h1:T+uRZRqDdf9C8+VLGNY3mrCoWuoctfzxFlO+XNorIxM= +gitlab.com/elixxir/crypto v0.0.7-0.20230112203618-74f75155c930 h1:28Meq1azAIm96QlPnGKr+hVH/PPF91u9tnhyNivXf/4= +gitlab.com/elixxir/crypto v0.0.7-0.20230112203618-74f75155c930/go.mod h1:T+uRZRqDdf9C8+VLGNY3mrCoWuoctfzxFlO+XNorIxM= +gitlab.com/elixxir/crypto v0.0.7-0.20230112220617-8ac1fa200509 h1:SXHXgaI0dLies9w+nJ26RbMb8Wz+4TzH5gGiJA4scwc= +gitlab.com/elixxir/crypto v0.0.7-0.20230112220617-8ac1fa200509/go.mod h1:T+uRZRqDdf9C8+VLGNY3mrCoWuoctfzxFlO+XNorIxM= +gitlab.com/elixxir/crypto v0.0.7-0.20230113153754-4be32f0a0a89 h1:esw3acCVQDJiRGjtYM5oJ+3mPbGY/eJHcyXn7h3Khzg= +gitlab.com/elixxir/crypto v0.0.7-0.20230113153754-4be32f0a0a89/go.mod h1:T+uRZRqDdf9C8+VLGNY3mrCoWuoctfzxFlO+XNorIxM= gitlab.com/elixxir/ekv v0.2.1 h1:dtwbt6KmAXG2Tik5d60iDz2fLhoFBgWwST03p7T+9Is= gitlab.com/elixxir/ekv v0.2.1/go.mod h1:USLD7xeDnuZEavygdrgzNEwZXeLQJK/w1a+htpN+JEU= gitlab.com/elixxir/primitives v0.0.3-0.20221214192222-988b44a6958a h1:F17FfEjS+/uDI/TTYQD21S5JvNZ9+p9bieau2nyLCzo= diff --git a/indexedDb/impl/channels/implementation.go b/indexedDb/impl/channels/implementation.go index 40fbb264e4fc5f3ae507feec0b728bc30276fd19..28b679546521e254ed9bb46b8de61cf45ebfadef 100644 --- a/indexedDb/impl/channels/implementation.go +++ b/indexedDb/impl/channels/implementation.go @@ -13,7 +13,6 @@ import ( "crypto/ed25519" "encoding/base64" "encoding/json" - "gitlab.com/elixxir/xxdk-wasm/indexedDb/impl" "strings" "sync" "syscall/js" @@ -28,6 +27,7 @@ import ( cryptoBroadcast "gitlab.com/elixxir/crypto/broadcast" cryptoChannel "gitlab.com/elixxir/crypto/channel" "gitlab.com/elixxir/crypto/message" + "gitlab.com/elixxir/xxdk-wasm/indexedDb/impl" "gitlab.com/elixxir/xxdk-wasm/utils" "gitlab.com/xx_network/primitives/id" ) @@ -39,6 +39,8 @@ type wasmModel struct { db *idb.Database cipher cryptoChannel.Cipher receivedMessageCB MessageReceivedCallback + deletedMessageCB DeletedMessageCallback + mutedUserCB MutedUserCallback updateMux sync.Mutex } @@ -79,7 +81,7 @@ func (w *wasmModel) LeaveChannel(channelID *id.ID) { parentErr := errors.New("failed to LeaveChannel") // Delete the channel from storage - err := impl.Delete(w.db, channelsStoreName, + err := indexedDb.Delete(w.db, channelsStoreName, js.ValueOf(channelID.String())) if err != nil { jww.ERROR.Printf("%+v", errors.WithMessagef(parentErr, @@ -488,8 +490,22 @@ func (w *wasmModel) GetMessage( // DeleteMessage removes a message with the given messageID from storage. func (w *wasmModel) DeleteMessage(messageID message.ID) error { msgId := js.ValueOf(base64.StdEncoding.EncodeToString(messageID.Bytes())) - return impl.DeleteIndex(w.db, messageStoreName, - messageStoreMessageIndex, pkeyName, msgId) + + err := impl.DeleteIndex( + w.db, messageStoreName, messageStoreMessageIndex, pkeyName, msgId) + if err != nil { + return err + } + + go w.deletedMessageCB(messageID) + + return nil +} + +// MuteUser is called whenever a user is muted or unmuted. +func (w *wasmModel) MuteUser( + channelID *id.ID, pubKey ed25519.PublicKey, unmute bool) { + go w.mutedUserCB(channelID, pubKey, unmute) } // msgIDLookup gets the UUID of the Message with the given messageID. @@ -510,5 +526,4 @@ func (w *wasmModel) msgIDLookup(messageID message.ID) (*Message, error) { return nil, err } return resultMsg, nil - } diff --git a/indexedDb/impl/channels/implementation_test.go b/indexedDb/impl/channels/implementation_test.go index 8642dc927d085d125ca669a9c9b67a9c144fcece..0e9b58de94f9b37b899cc428eca2aa735698c800 100644 --- a/indexedDb/impl/channels/implementation_test.go +++ b/indexedDb/impl/channels/implementation_test.go @@ -10,25 +10,27 @@ package main import ( + "crypto/ed25519" "encoding/json" "fmt" - "github.com/hack-pad/go-indexeddb/idb" - "gitlab.com/elixxir/crypto/message" - "gitlab.com/elixxir/xxdk-wasm/indexedDb/impl" - "gitlab.com/elixxir/xxdk-wasm/storage" - "gitlab.com/xx_network/crypto/csprng" - "gitlab.com/xx_network/primitives/netTime" "os" "strconv" "testing" "time" + "github.com/hack-pad/go-indexeddb/idb" jww "github.com/spf13/jwalterweatherman" + "gitlab.com/elixxir/client/v4/channels" "gitlab.com/elixxir/client/v4/cmix/rounds" cryptoBroadcast "gitlab.com/elixxir/crypto/broadcast" cryptoChannel "gitlab.com/elixxir/crypto/channel" + "gitlab.com/elixxir/crypto/message" + "gitlab.com/elixxir/xxdk-wasm/indexedDb/impl" + "gitlab.com/elixxir/xxdk-wasm/storage" + "gitlab.com/xx_network/crypto/csprng" "gitlab.com/xx_network/primitives/id" + "gitlab.com/xx_network/primitives/netTime" ) func TestMain(m *testing.M) { @@ -36,14 +38,10 @@ func TestMain(m *testing.M) { os.Exit(m.Run()) } -func dummyCallback(uint64, *id.ID, bool) {} - -// dummyStoreDatabaseName always returns nil error and adheres to the -// storeDatabaseNameFn type. +func dummyReceivedMessageCB(uint64, *id.ID, bool) {} +func dummyDeletedMessageCB(message.ID) {} +func dummyMutedUserCB(*id.ID, ed25519.PublicKey, bool) {} func dummyStoreDatabaseName(string) error { return nil } - -// dummyStoreEncryptionStatus returns the same encryption status passed into it -// and adheres to the storeEncryptionStatusFn type. func dummyStoreEncryptionStatus(_ string, encryptionStatus bool) (bool, error) { return encryptionStatus, nil } @@ -66,7 +64,8 @@ func TestWasmModel_msgIDLookup(t *testing.T) { testString := "TestWasmModel_msgIDLookup" + cs testMsgId := message.DeriveChannelMessageID(&id.ID{1}, 0, []byte(testString)) - eventModel, err2 := newWASMModel(testString, c, dummyCallback, + eventModel, err2 := newWASMModel(testString, c, + dummyReceivedMessageCB, dummyDeletedMessageCB, dummyMutedUserCB, dummyStoreDatabaseName, dummyStoreEncryptionStatus) if err2 != nil { t.Fatal(err2) @@ -96,8 +95,9 @@ func TestWasmModel_DeleteMessage(t *testing.T) { storage.GetLocalStorage().Clear() testString := "TestWasmModel_DeleteMessage" testMsgId := message.DeriveChannelMessageID(&id.ID{1}, 0, []byte(testString)) - eventModel, err := newWASMModel(testString, nil, dummyCallback, - dummyStoreDatabaseName, dummyStoreEncryptionStatus) + eventModel, err := newWASMModel(testString, nil, dummyReceivedMessageCB, + dummyDeletedMessageCB, dummyMutedUserCB, dummyStoreDatabaseName, + dummyStoreEncryptionStatus) if err != nil { t.Fatal(err) } @@ -153,7 +153,8 @@ func Test_wasmModel_UpdateSentStatus(t *testing.T) { testString := "Test_wasmModel_UpdateSentStatus" + cs testMsgId := message.DeriveChannelMessageID( &id.ID{1}, 0, []byte(testString)) - eventModel, err2 := newWASMModel(testString, c, dummyCallback, + eventModel, err2 := newWASMModel(testString, c, + dummyReceivedMessageCB, dummyDeletedMessageCB, dummyMutedUserCB, dummyStoreDatabaseName, dummyStoreEncryptionStatus) if err2 != nil { t.Fatal(err) @@ -161,8 +162,8 @@ func Test_wasmModel_UpdateSentStatus(t *testing.T) { // Store a test message testMsg := buildMessage([]byte(testString), testMsgId.Bytes(), nil, - testString, []byte(testString), []byte{8, 6, 7, 5}, 0, 0, netTime.Now(), - time.Second, 0, 0, false, false, channels.Sent) + testString, []byte(testString), []byte{8, 6, 7, 5}, 0, 0, + netTime.Now(), time.Second, 0, 0, false, false, channels.Sent) uuid, err2 := eventModel.receiveHelper(testMsg, false) if err2 != nil { t.Fatal(err2) @@ -221,8 +222,9 @@ func Test_wasmModel_JoinChannel_LeaveChannel(t *testing.T) { } t.Run("Test_wasmModel_JoinChannel_LeaveChannel"+cs, func(t *testing.T) { storage.GetLocalStorage().Clear() - eventModel, err2 := newWASMModel("test", c, dummyCallback, - dummyStoreDatabaseName, dummyStoreEncryptionStatus) + eventModel, err2 := newWASMModel("test", c, dummyReceivedMessageCB, + dummyDeletedMessageCB, dummyMutedUserCB, dummyStoreDatabaseName, + dummyStoreEncryptionStatus) if err2 != nil { t.Fatal(err2) } @@ -275,7 +277,8 @@ func Test_wasmModel_UUIDTest(t *testing.T) { t.Run("Test_wasmModel_UUIDTest"+cs, func(t *testing.T) { storage.GetLocalStorage().Clear() testString := "testHello" + cs - eventModel, err2 := newWASMModel(testString, c, dummyCallback, + eventModel, err2 := newWASMModel(testString, c, + dummyReceivedMessageCB, dummyDeletedMessageCB, dummyMutedUserCB, dummyStoreDatabaseName, dummyStoreEncryptionStatus) if err2 != nil { t.Fatal(err2) @@ -322,7 +325,8 @@ func Test_wasmModel_DuplicateReceives(t *testing.T) { t.Run("Test_wasmModel_DuplicateReceives"+cs, func(t *testing.T) { storage.GetLocalStorage().Clear() testString := "testHello" - eventModel, err2 := newWASMModel(testString, c, dummyCallback, + eventModel, err2 := newWASMModel(testString, c, + dummyReceivedMessageCB, dummyDeletedMessageCB, dummyMutedUserCB, dummyStoreDatabaseName, dummyStoreEncryptionStatus) if err2 != nil { t.Fatal(err2) @@ -372,7 +376,8 @@ func Test_wasmModel_deleteMsgByChannel(t *testing.T) { testString := "test_deleteMsgByChannel" totalMessages := 10 expectedMessages := 5 - eventModel, err2 := newWASMModel(testString, c, dummyCallback, + eventModel, err2 := newWASMModel(testString, c, + dummyReceivedMessageCB, dummyDeletedMessageCB, dummyMutedUserCB, dummyStoreDatabaseName, dummyStoreEncryptionStatus) if err2 != nil { t.Fatal(err2) @@ -443,7 +448,8 @@ func TestWasmModel_receiveHelper_UniqueIndex(t *testing.T) { t.Run("TestWasmModel_receiveHelper_UniqueIndex"+cs, func(t *testing.T) { storage.GetLocalStorage().Clear() testString := fmt.Sprintf("test_receiveHelper_UniqueIndex_%d", i) - eventModel, err2 := newWASMModel(testString, c, dummyCallback, + eventModel, err2 := newWASMModel(testString, c, + dummyReceivedMessageCB, dummyDeletedMessageCB, dummyMutedUserCB, dummyStoreDatabaseName, dummyStoreEncryptionStatus) if err2 != nil { t.Fatal(err2) diff --git a/indexedDb/impl/channels/init.go b/indexedDb/impl/channels/init.go index 70768a14752e50a984741946452fd7702c0d8561..dd0334c00198261013d4c302f29ead76c1fab5da 100644 --- a/indexedDb/impl/channels/init.go +++ b/indexedDb/impl/channels/init.go @@ -10,11 +10,13 @@ package main import ( + "crypto/ed25519" "github.com/hack-pad/go-indexeddb/idb" "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" "gitlab.com/elixxir/client/v4/channels" cryptoChannel "gitlab.com/elixxir/crypto/channel" + "gitlab.com/elixxir/crypto/message" "gitlab.com/elixxir/xxdk-wasm/indexedDb/impl" "gitlab.com/xx_network/primitives/id" "syscall/js" @@ -35,6 +37,14 @@ const ( // update is true if the row is old and was edited. type MessageReceivedCallback func(uuid uint64, channelID *id.ID, update bool) +// DeletedMessageCallback is called any time a message is deleted. +type DeletedMessageCallback func(messageID message.ID) + +// MutedUserCallback is called any time a user is muted or unmuted. unmute is +// true if the user has been unmuted and false if they have been muted. +type MutedUserCallback func( + channelID *id.ID, pubKey ed25519.PublicKey, unmute bool) + // storeDatabaseNameFn matches storage.StoreIndexedDb so that the data can be // sent between the worker and main thread. type storeDatabaseNameFn func(databaseName string) error @@ -47,16 +57,20 @@ type storeEncryptionStatusFn func( // NewWASMEventModel returns a [channels.EventModel] backed by a wasmModel. // The name should be a base64 encoding of the users public key. func NewWASMEventModel(path string, encryption cryptoChannel.Cipher, - cb MessageReceivedCallback, storeDatabaseName storeDatabaseNameFn, + messageReceivedCB MessageReceivedCallback, + deletedMessageCB DeletedMessageCallback, + mutedUserCB MutedUserCallback, storeDatabaseName storeDatabaseNameFn, storeEncryptionStatus storeEncryptionStatusFn) (channels.EventModel, error) { databaseName := path + databaseSuffix - return newWASMModel( - databaseName, encryption, cb, storeDatabaseName, storeEncryptionStatus) + return newWASMModel(databaseName, encryption, messageReceivedCB, + deletedMessageCB, mutedUserCB, storeDatabaseName, storeEncryptionStatus) } // newWASMModel creates the given [idb.Database] and returns a wasmModel. func newWASMModel(databaseName string, encryption cryptoChannel.Cipher, - cb MessageReceivedCallback, storeDatabaseName storeDatabaseNameFn, + messageReceivedCB MessageReceivedCallback, + deletedMessageCB DeletedMessageCallback, mutedUserCB MutedUserCallback, + storeDatabaseName storeDatabaseNameFn, storeEncryptionStatus storeEncryptionStatusFn) (*wasmModel, error) { // Attempt to open database object ctx, cancel := impl.NewContext() @@ -115,7 +129,13 @@ func newWASMModel(databaseName string, encryption cryptoChannel.Cipher, jww.WARN.Printf("IndexedDb encryption disabled!") } - wrapper := &wasmModel{db: db, receivedMessageCB: cb, cipher: encryption} + wrapper := &wasmModel{ + db: db, + cipher: encryption, + receivedMessageCB: messageReceivedCB, + deletedMessageCB: deletedMessageCB, + mutedUserCB: mutedUserCB, + } return wrapper, nil } diff --git a/indexedDb/impl/dm/implementation.go b/indexedDb/impl/dm/implementation.go index 293754b06f0035b7b63feca8f2a40ec405f38557..8280d7b5138c00eab29b150ecc1fd749b6d60b13 100644 --- a/indexedDb/impl/dm/implementation.go +++ b/indexedDb/impl/dm/implementation.go @@ -12,7 +12,6 @@ package main import ( "crypto/ed25519" "encoding/json" - "gitlab.com/elixxir/xxdk-wasm/indexedDb/impl" "strings" "sync" "syscall/js" @@ -28,6 +27,7 @@ import ( "github.com/hack-pad/go-indexeddb/idb" cryptoChannel "gitlab.com/elixxir/crypto/channel" "gitlab.com/elixxir/crypto/message" + "gitlab.com/elixxir/xxdk-wasm/indexedDb/impl" ) // wasmModel implements dm.EventModel interface, which uses the channels system @@ -100,8 +100,7 @@ func (w *wasmModel) Receive(messageID message.ID, nickname string, text []byte, parentErr := errors.New("failed to Receive") // If there is no extant Conversation, create one. - _, err := impl.Get( - w.db, conversationStoreName, utils.CopyBytesToJS(pubKey)) + _, err := impl.Get(w.db, conversationStoreName, utils.CopyBytesToJS(pubKey)) if err != nil { if strings.Contains(err.Error(), impl.ErrDoesNotExist) { err = w.joinConversation(nickname, pubKey, dmToken, codeset) @@ -143,8 +142,7 @@ func (w *wasmModel) ReceiveText(messageID message.ID, nickname, text string, parentErr := errors.New("failed to ReceiveText") // If there is no extant Conversation, create one. - _, err := impl.Get( - w.db, conversationStoreName, utils.CopyBytesToJS(pubKey)) + _, err := impl.Get(w.db, conversationStoreName, utils.CopyBytesToJS(pubKey)) if err != nil { if strings.Contains(err.Error(), impl.ErrDoesNotExist) { err = w.joinConversation(nickname, pubKey, dmToken, codeset) @@ -188,8 +186,7 @@ func (w *wasmModel) ReceiveReply(messageID, reactionTo message.ID, nickname, parentErr := errors.New("failed to ReceiveReply") // If there is no extant Conversation, create one. - _, err := impl.Get( - w.db, conversationStoreName, utils.CopyBytesToJS(pubKey)) + _, err := impl.Get(w.db, conversationStoreName, utils.CopyBytesToJS(pubKey)) if err != nil { if strings.Contains(err.Error(), impl.ErrDoesNotExist) { err = w.joinConversation(nickname, pubKey, dmToken, codeset) @@ -233,8 +230,7 @@ func (w *wasmModel) ReceiveReaction(messageID, _ message.ID, nickname, parentErr := errors.New("failed to ReceiveText") // If there is no extant Conversation, create one. - _, err := impl.Get( - w.db, conversationStoreName, utils.CopyBytesToJS(pubKey)) + _, err := impl.Get(w.db, conversationStoreName, utils.CopyBytesToJS(pubKey)) if err != nil { if strings.Contains(err.Error(), impl.ErrDoesNotExist) { err = w.joinConversation(nickname, pubKey, dmToken, codeset) diff --git a/main.go b/main.go index de379dbdbf5eb3132e8cc546df5a6b601665e5e6..80fea11e65a139a0d939c1c05a6ada764bd92ab1 100644 --- a/main.go +++ b/main.go @@ -196,6 +196,8 @@ func main() { js.Global().Set("GetClientVersion", js.FuncOf(wasm.GetClientVersion)) js.Global().Set("GetClientGitVersion", js.FuncOf(wasm.GetClientGitVersion)) js.Global().Set("GetClientDependencies", js.FuncOf(wasm.GetClientDependencies)) + js.Global().Set("GetWasmSemanticVersion", js.FuncOf(wasm.GetWasmSemanticVersion)) + js.Global().Set("GetXXDKSemanticVersion", js.FuncOf(wasm.GetXXDKSemanticVersion)) <-make(chan bool) os.Exit(0) diff --git a/storage/version.go b/storage/version.go index d513092819786e1a13a1d36fc52464f5c402df94..7d39cb94d3bf0204f846fd620b68c87b59f12ce9 100644 --- a/storage/version.go +++ b/storage/version.go @@ -11,9 +11,11 @@ package storage import ( "os" + "sync" "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" + "gitlab.com/elixxir/client/v4/bindings" ) @@ -38,17 +40,23 @@ func CheckAndStoreVersions() error { func checkAndStoreVersions( currentWasmVer, currentClientVer string, ls *LocalStorage) error { - // Get the stored client and WASM versions, if they exists - storedClientVer, err := initOrLoadStoredSemver( - clientVerKey, currentClientVer, ls) + // Get the stored client version, if it exists + storedClientVer, err := + initOrLoadStoredSemver(clientVerKey, currentClientVer, ls) if err != nil { return err } + + // Get the stored WASM versions, if it exists storedWasmVer, err := initOrLoadStoredSemver(semverKey, currentWasmVer, ls) if err != nil { return err } + // Store old versions to memory + setOldClientSemVersion(storedClientVer) + setOldWasmSemVersion(storedWasmVer) + // Check if client needs an update if storedClientVer != currentClientVer { jww.INFO.Printf("xxDK client out of date; upgrading version: v%s → v%s", @@ -96,3 +104,43 @@ func initOrLoadStoredSemver( // Return the stored version return string(storedVersion), nil } + +// oldVersions contains the old versions of xxdk WASM and xxdk client that were +// stored in storage before being overwritten on update. +var oldVersions struct { + wasm string + client string + sync.Mutex +} + +// GetOldWasmSemVersion returns the old version of xxdk WASM before being +// updated. +func GetOldWasmSemVersion() string { + oldVersions.Lock() + defer oldVersions.Unlock() + return oldVersions.wasm +} + +// GetOldClientSemVersion returns the old version of xxdk client before being +// updated. +func GetOldClientSemVersion() string { + oldVersions.Lock() + defer oldVersions.Unlock() + return oldVersions.client +} + +// setOldWasmSemVersion sets the old version of xxdk WASM. This should be called +// before it is updated. +func setOldWasmSemVersion(v string) { + oldVersions.Lock() + defer oldVersions.Unlock() + oldVersions.wasm = v +} + +// setOldClientSemVersion sets the old version of xxdk client. This should be +// called before it is updated. +func setOldClientSemVersion(v string) { + oldVersions.Lock() + defer oldVersions.Unlock() + oldVersions.client = v +} diff --git a/wasm/channels.go b/wasm/channels.go index 30442a269ac8d252256179926c3b0dd6404a8163..43ae72bdf4ad9b01441c20c800ca9bafbdcf7620 100644 --- a/wasm/channels.go +++ b/wasm/channels.go @@ -10,10 +10,12 @@ package wasm import ( + "crypto/ed25519" "encoding/base64" "encoding/json" "errors" "gitlab.com/elixxir/client/v4/channels" + "gitlab.com/elixxir/crypto/message" channelsDb "gitlab.com/elixxir/xxdk-wasm/indexedDb/worker/channels" "gitlab.com/xx_network/primitives/id" "sync" @@ -320,14 +322,23 @@ func LoadChannelsManager(_ js.Value, args []js.Value) any { // - args[1] - Path to Javascript file that starts the worker (string). // - args[2] - Bytes of a private identity ([channel.PrivateIdentity]) that is // generated by [GenerateChannelIdentity] (Uint8Array). -// - args[3] - Function that takes in the same parameters as -// [indexedDb.MessageReceivedCallback]. On the Javascript side, the UUID is -// returned as an int and the channelID as a Uint8Array. The row in the -// database that was updated can be found using the UUID. The channel ID is -// provided so that the recipient can filter if they want to the processes -// the update now or not. An "update" bool is present which tells you if the -// row is new or if it is an edited old row. -// - args[4] - ID of [ChannelDbCipher] object in tracker (int). Create this +// - args[3] - The received message callback, which is called everytime a +// message is added or changed in the database. It is a function that takes +// in the same parameters as [indexedDb.MessageReceivedCallback]. On the +// Javascript side, the UUID is returned as an int and the channelID as a +// Uint8Array. The row in the database that was updated can be found using +// the UUID. The channel ID is provided so that the recipient can filter if +// they want to the processes the update now or not. An "update" bool is +// present which tells you if the row is new or if it is an edited old row. +// - args[4] - The deleted message callback, which is called everytime a +// message is deleted from the database. It is a function that takes in the +// same parameters as [indexedDb.DeletedMessageCallback]. On the Javascript +// side, the message ID is returned as a Uint8Array. +// - args[5] - The muted user callback, which is called everytime a user is +// muted or unmuted. It is a function that takes in the same parameters as +// [indexedDb.MutedUserCallback]. On the Javascript side, the channel ID and +// user public key are returned as Uint8Array. +// - args[6] - ID of [ChannelDbCipher] object in tracker (int). Create this // object with [NewChannelsDatabaseCipher] and get its id with // [ChannelDbCipher.GetID]. // @@ -340,15 +351,17 @@ func NewChannelsManagerWithIndexedDb(_ js.Value, args []js.Value) any { wasmJsPath := args[1].String() privateIdentity := utils.CopyBytesToGo(args[2]) messageReceivedCB := args[3] - cipherID := args[4].Int() + deletedMessageCB := args[4] + mutedUserCB := args[5] + cipherID := args[6].Int() cipher, err := bindings.GetChannelDbCipherTrackerFromID(cipherID) if err != nil { utils.Throw(utils.TypeError, err) } - return newChannelsManagerWithIndexedDb( - cmixID, wasmJsPath, privateIdentity, messageReceivedCB, cipher) + return newChannelsManagerWithIndexedDb(cmixID, wasmJsPath, privateIdentity, + messageReceivedCB, deletedMessageCB, mutedUserCB, cipher) } // NewChannelsManagerWithIndexedDbUnsafe creates a new [ChannelsManager] from a @@ -369,35 +382,58 @@ func NewChannelsManagerWithIndexedDb(_ js.Value, args []js.Value) any { // - args[1] - Path to Javascript file that starts the worker (string). // - args[2] - Bytes of a private identity ([channel.PrivateIdentity]) that is // generated by [GenerateChannelIdentity] (Uint8Array). -// - args[3] - Function that takes in the same parameters as -// [indexedDb.MessageReceivedCallback]. On the Javascript side, the UUID is -// returned as an int and the channelID as a Uint8Array. The row in the -// database that was updated can be found using the UUID. The channel ID is -// provided so that the recipient can filter if they want to the processes -// the update now or not. An "update" bool is present which tells you if -// the row is new or if it is an edited old row +// - args[3] - The received message callback, which is called everytime a +// message is added or changed in the database. It is a function that takes +// in the same parameters as [indexedDb.MessageReceivedCallback]. On the +// Javascript side, the UUID is returned as an int and the channelID as a +// Uint8Array. The row in the database that was updated can be found using +// the UUID. The channel ID is provided so that the recipient can filter if +// they want to the processes the update now or not. An "update" bool is +// present which tells you if the row is new or if it is an edited old row. +// - args[4] - The deleted message callback, which is called everytime a +// message is deleted from the database. It is a function that takes in the +// same parameters as [indexedDb.DeletedMessageCallback]. On the Javascript +// side, the message ID is returned as a Uint8Array. +// - args[5] - The muted user callback, which is called everytime a user is +// muted or unmuted. It is a function that takes in the same parameters as +// [indexedDb.MutedUserCallback]. On the Javascript side, the channel ID and +// user public key are returned as Uint8Array. // // Returns a promise: // - Resolves to a Javascript representation of the [ChannelsManager] object. // - Rejected with an error if loading indexedDb or the manager fails. +// FIXME: package names in comments for indexedDb func NewChannelsManagerWithIndexedDbUnsafe(_ js.Value, args []js.Value) any { cmixID := args[0].Int() wasmJsPath := args[1].String() privateIdentity := utils.CopyBytesToGo(args[2]) messageReceivedCB := args[3] + deletedMessageCB := args[4] + mutedUserCB := args[5] - return newChannelsManagerWithIndexedDb( - cmixID, wasmJsPath, privateIdentity, messageReceivedCB, nil) + return newChannelsManagerWithIndexedDb(cmixID, wasmJsPath, privateIdentity, + messageReceivedCB, deletedMessageCB, mutedUserCB, nil) } func newChannelsManagerWithIndexedDb(cmixID int, wasmJsPath string, - privateIdentity []byte, cb js.Value, cipher *bindings.ChannelDbCipher) any { + privateIdentity []byte, messageReceivedCB, deletedMessageCB, mutedUserCB js.Value, + cipher *bindings.ChannelDbCipher) any { - messageReceivedCB := func(uuid uint64, channelID *id.ID, update bool) { - cb.Invoke(uuid, utils.CopyBytesToJS(channelID.Marshal()), update) + messageReceived := func(uuid uint64, channelID *id.ID, update bool) { + messageReceivedCB.Invoke(uuid, utils.CopyBytesToJS(channelID.Marshal()), update) } - model := channelsDb.NewWASMEventModelBuilder(wasmJsPath, cipher, messageReceivedCB) + deletedMessage := func(messageID message.ID) { + deletedMessageCB.Invoke(utils.CopyBytesToJS(messageID.Marshal())) + } + + mutedUser := func(channelID *id.ID, pubKey ed25519.PublicKey, unmute bool) { + mutedUserCB.Invoke(utils.CopyBytesToJS(channelID.Marshal()), + utils.CopyBytesToJS(pubKey), unmute) + } + + model := channelsDb.NewWASMEventModelBuilder( + wasmJsPath, cipher, messageReceived, deletedMessage, mutedUser) promiseFn := func(resolve, reject func(args ...any) js.Value) { cm, err := bindings.NewChannelsManagerGoEventModel( @@ -426,14 +462,23 @@ func newChannelsManagerWithIndexedDb(cmixID int, wasmJsPath string, // - args[1] - Path to Javascript file that starts the worker (string). // - args[2] - The storage tag associated with the previously created channel // manager and retrieved with [ChannelsManager.GetStorageTag] (string). -// - args[3] - Function that takes in the same parameters as -// [indexedDb.MessageReceivedCallback]. On the Javascript side, the UUID is -// returned as an int and the channelID as a Uint8Array. The row in the -// database that was updated can be found using the UUID. The channel ID is -// provided so that the recipient can filter if they want to the processes -// the update now or not. An "update" bool is present which tells you if the -// row is new or if it is an edited old row. -// - args[4] - ID of [ChannelDbCipher] object in tracker (int). Create this +// - args[3] - The received message callback, which is called everytime a +// message is added or changed in the database. It is a function that takes +// in the same parameters as [indexedDb.MessageReceivedCallback]. On the +// Javascript side, the UUID is returned as an int and the channelID as a +// Uint8Array. The row in the database that was updated can be found using +// the UUID. The channel ID is provided so that the recipient can filter if +// they want to the processes the update now or not. An "update" bool is +// present which tells you if the row is new or if it is an edited old row. +// - args[4] - The deleted message callback, which is called everytime a +// message is deleted from the database. It is a function that takes in the +// same parameters as [indexedDb.DeletedMessageCallback]. On the Javascript +// side, the message ID is returned as a Uint8Array. +// - args[5] - The muted user callback, which is called everytime a user is +// muted or unmuted. It is a function that takes in the same parameters as +// [indexedDb.MutedUserCallback]. On the Javascript side, the channel ID and +// user public key are returned as Uint8Array. +// - args[6] - ID of [ChannelDbCipher] object in tracker (int). Create this // object with [NewChannelsDatabaseCipher] and get its id with // [ChannelDbCipher.GetID]. // @@ -446,15 +491,17 @@ func LoadChannelsManagerWithIndexedDb(_ js.Value, args []js.Value) any { wasmJsPath := args[1].String() storageTag := args[2].String() messageReceivedCB := args[3] - cipherID := args[4].Int() + deletedMessageCB := args[4] + mutedUserCB := args[5] + cipherID := args[6].Int() cipher, err := bindings.GetChannelDbCipherTrackerFromID(cipherID) if err != nil { utils.Throw(utils.TypeError, err) } - return loadChannelsManagerWithIndexedDb( - cmixID, wasmJsPath, storageTag, messageReceivedCB, cipher) + return loadChannelsManagerWithIndexedDb(cmixID, wasmJsPath, storageTag, + messageReceivedCB, deletedMessageCB, mutedUserCB, cipher) } // LoadChannelsManagerWithIndexedDbUnsafe loads an existing [ChannelsManager] @@ -473,13 +520,22 @@ func LoadChannelsManagerWithIndexedDb(_ js.Value, args []js.Value) any { // - args[1] - Path to Javascript file that starts the worker (string). // - args[2] - The storage tag associated with the previously created channel // manager and retrieved with [ChannelsManager.GetStorageTag] (string). -// - args[3] - Function that takes in the same parameters as -// [indexedDb.MessageReceivedCallback]. On the Javascript side, the UUID is -// returned as an int and the channelID as a Uint8Array. The row in the -// database that was updated can be found using the UUID. The channel ID is -// provided so that the recipient can filter if they want to the processes -// the update now or not. An "update" bool is present which tells you if -// the row is new or if it is an edited old row +// - args[3] - The received message callback, which is called everytime a +// message is added or changed in the database. It is a function that takes +// in the same parameters as [indexedDb.MessageReceivedCallback]. On the +// Javascript side, the UUID is returned as an int and the channelID as a +// Uint8Array. The row in the database that was updated can be found using +// the UUID. The channel ID is provided so that the recipient can filter if +// they want to the processes the update now or not. An "update" bool is +// present which tells you if the row is new or if it is an edited old row. +// - args[4] - The deleted message callback, which is called everytime a +// message is deleted from the database. It is a function that takes in the +// same parameters as [indexedDb.DeletedMessageCallback]. On the Javascript +// side, the message ID is returned as a Uint8Array. +// - args[5] - The muted user callback, which is called everytime a user is +// muted or unmuted. It is a function that takes in the same parameters as +// [indexedDb.MutedUserCallback]. On the Javascript side, the channel ID and +// user public key are returned as Uint8Array. // // Returns a promise: // - Resolves to a Javascript representation of the [ChannelsManager] object. @@ -489,19 +545,32 @@ func LoadChannelsManagerWithIndexedDbUnsafe(_ js.Value, args []js.Value) any { wasmJsPath := args[1].String() storageTag := args[2].String() messageReceivedCB := args[3] + deletedMessageCB := args[3] + mutedUserCB := args[4] - return loadChannelsManagerWithIndexedDb( - cmixID, wasmJsPath, storageTag, messageReceivedCB, nil) + return loadChannelsManagerWithIndexedDb(cmixID, wasmJsPath, storageTag, + messageReceivedCB, deletedMessageCB, mutedUserCB, nil) } func loadChannelsManagerWithIndexedDb(cmixID int, wasmJsPath, storageTag string, - cb js.Value, cipher *bindings.ChannelDbCipher) any { - messageReceivedCB := func(uuid uint64, channelID *id.ID, updated bool) { - cb.Invoke(uuid, utils.CopyBytesToJS(channelID.Marshal()), updated) + messageReceivedCB, deletedMessageCB, mutedUserCB js.Value, + cipher *bindings.ChannelDbCipher) any { + + messageReceived := func(uuid uint64, channelID *id.ID, update bool) { + messageReceivedCB.Invoke(uuid, utils.CopyBytesToJS(channelID.Marshal()), update) + } + + deletedMessage := func(messageID message.ID) { + deletedMessageCB.Invoke(utils.CopyBytesToJS(messageID.Marshal())) + } + + mutedUser := func(channelID *id.ID, pubKey ed25519.PublicKey, unmute bool) { + mutedUserCB.Invoke(utils.CopyBytesToJS(channelID.Marshal()), + utils.CopyBytesToJS(pubKey), unmute) } model := channelsDb.NewWASMEventModelBuilder( - wasmJsPath, cipher, messageReceivedCB) + wasmJsPath, cipher, messageReceived, deletedMessage, mutedUser) promiseFn := func(resolve, reject func(args ...any) js.Value) { cm, err := bindings.LoadChannelsManagerGoEventModel( @@ -879,14 +948,14 @@ func ValidForever(js.Value, []js.Value) any { func (cm *ChannelsManager) SendGeneric(_ js.Value, args []js.Value) any { marshalledChanId := utils.CopyBytesToGo(args[0]) messageType := args[1].Int() - message := utils.CopyBytesToGo(args[2]) + msg := utils.CopyBytesToGo(args[2]) leaseTimeMS := int64(args[3].Int()) tracked := args[4].Bool() cmixParamsJSON := utils.CopyBytesToGo(args[5]) promiseFn := func(resolve, reject func(args ...any) js.Value) { sendReport, err := cm.api.SendGeneric(marshalledChanId, messageType, - message, leaseTimeMS, tracked, cmixParamsJSON) + msg, leaseTimeMS, tracked, cmixParamsJSON) if err != nil { reject(utils.JsTrace(err)) } else { @@ -922,13 +991,13 @@ func (cm *ChannelsManager) SendGeneric(_ js.Value, args []js.Value) any { // - Rejected with an error if sending fails. func (cm *ChannelsManager) SendMessage(_ js.Value, args []js.Value) any { marshalledChanId := utils.CopyBytesToGo(args[0]) - message := args[1].String() + msg := args[1].String() leaseTimeMS := int64(args[2].Int()) cmixParamsJSON := utils.CopyBytesToGo(args[3]) promiseFn := func(resolve, reject func(args ...any) js.Value) { sendReport, err := cm.api.SendMessage( - marshalledChanId, message, leaseTimeMS, cmixParamsJSON) + marshalledChanId, msg, leaseTimeMS, cmixParamsJSON) if err != nil { reject(utils.JsTrace(err)) } else { @@ -971,13 +1040,13 @@ func (cm *ChannelsManager) SendMessage(_ js.Value, args []js.Value) any { // - Rejected with an error if sending fails. func (cm *ChannelsManager) SendReply(_ js.Value, args []js.Value) any { marshalledChanId := utils.CopyBytesToGo(args[0]) - message := args[1].String() + msg := args[1].String() messageToReactTo := utils.CopyBytesToGo(args[2]) leaseTimeMS := int64(args[3].Int()) cmixParamsJSON := utils.CopyBytesToGo(args[4]) promiseFn := func(resolve, reject func(args ...any) js.Value) { - sendReport, err := cm.api.SendReply(marshalledChanId, message, + sendReport, err := cm.api.SendReply(marshalledChanId, msg, messageToReactTo, leaseTimeMS, cmixParamsJSON) if err != nil { reject(utils.JsTrace(err)) @@ -1068,14 +1137,14 @@ func (cm *ChannelsManager) SendReaction(_ js.Value, args []js.Value) any { func (cm *ChannelsManager) SendAdminGeneric(_ js.Value, args []js.Value) any { marshalledChanId := utils.CopyBytesToGo(args[0]) messageType := args[1].Int() - message := utils.CopyBytesToGo(args[2]) + msg := utils.CopyBytesToGo(args[2]) leaseTimeMS := int64(args[3].Int()) tracked := args[4].Bool() cmixParamsJSON := utils.CopyBytesToGo(args[5]) promiseFn := func(resolve, reject func(args ...any) js.Value) { sendReport, err := cm.api.SendAdminGeneric(marshalledChanId, - messageType, message, leaseTimeMS, tracked, cmixParamsJSON) + messageType, msg, leaseTimeMS, tracked, cmixParamsJSON) if err != nil { reject(utils.JsTrace(err)) } else { @@ -1624,6 +1693,7 @@ func (emb *eventModelBuilder) Build(path string) bindings.EventModel { updateFromMessageID: utils.WrapCB(emJs, "UpdateFromMessageID"), getMessage: utils.WrapCB(emJs, "GetMessage"), deleteMessage: utils.WrapCB(emJs, "DeleteMessage"), + muteUser: utils.WrapCB(emJs, "MuteUser"), } } @@ -1639,6 +1709,7 @@ type eventModel struct { updateFromMessageID func(args ...any) js.Value getMessage func(args ...any) js.Value deleteMessage func(args ...any) js.Value + muteUser func(args ...any) js.Value } // JoinChannel is called whenever a channel is joined locally. @@ -1858,6 +1929,17 @@ func (em *eventModel) DeleteMessage(messageID []byte) error { return nil } +// MuteUser mutes the given user or unmutes them. +// +// Parameters: +// - channelID - The bytes of the [id.ID] of the channel the user is being +// muted in. +// - pubKey - The [ed25519.PublicKey] of the user that is muted or unmuted. +func (em *eventModel) MuteUser(channelID, pubkey []byte, unmute bool) { + em.muteUser( + utils.CopyBytesToJS(channelID), utils.CopyBytesToJS(pubkey), unmute) +} + // MessageAndError contains a message returned by eventModel.GetMessage or any // possible error that occurs during lookup. Only one field should be present at // a time; if an error occurs, ModelMessage should be empty. diff --git a/wasm/version.go b/wasm/version.go index 528641b61a5161d308e04a7b6991bde21f801e7c..ee1a921cc666ef3a5e2491b5f43371ee098e2f3f 100644 --- a/wasm/version.go +++ b/wasm/version.go @@ -10,9 +10,12 @@ package wasm import ( + "encoding/json" + "syscall/js" + "gitlab.com/elixxir/client/v4/bindings" "gitlab.com/elixxir/xxdk-wasm/storage" - "syscall/js" + "gitlab.com/elixxir/xxdk-wasm/utils" ) // GetVersion returns the current xxDK WASM semantic version. @@ -49,3 +52,60 @@ func GetClientGitVersion(js.Value, []js.Value) any { func GetClientDependencies(js.Value, []js.Value) any { return bindings.GetDependencies() } + +// VersionInfo contains information about the current and old version of the +// API. +type VersionInfo struct { + Current string `json:"current"` + Updated bool `json:"updated"` + Old string `json:"old"` +} + +// GetWasmSemanticVersion returns the current version of the WASM client, it's +// old version before being updated, and if it has been updated. +// +// Returns: +// - JSON of [VersionInfo] (Uint8Array). +// - Throws a TypeError if getting the version failed. +func GetWasmSemanticVersion(js.Value, []js.Value) any { + vi := VersionInfo{ + Current: storage.SEMVER, + Updated: false, + Old: storage.GetOldWasmSemVersion(), + } + + if vi.Current != vi.Old { + vi.Updated = true + } + + data, err := json.Marshal(vi) + if err != nil { + utils.Throw(utils.TypeError, err) + } + + return utils.CopyBytesToJS(data) +} + +// GetXXDKSemanticVersion returns the current version of the xxdk client, it's +// old version before being updated, and if it has been updated. +// +// Returns: +// - JSON of [VersionInfo] (Uint8Array). +// - Throws a TypeError if getting the version failed. +func GetXXDKSemanticVersion(js.Value, []js.Value) any { + vi := VersionInfo{ + Current: bindings.GetVersion(), + Updated: false, + Old: storage.GetOldClientSemVersion(), + } + if vi.Current != vi.Old { + vi.Updated = true + } + + data, err := json.Marshal(vi) + if err != nil { + utils.Throw(utils.TypeError, err) + } + + return utils.CopyBytesToJS(data) +}