diff --git a/go.mod b/go.mod index 1f46ca38697f5d8e67582669b50625e9f32826d1..be90c570fff81bcb1ebeb21d9efa3d0f787d95c7 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ 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.6.2-0.20230511215110-b43e18a47875 + gitlab.com/elixxir/client/v4 v4.6.2-0.20230512234533-17b97e5a36cc gitlab.com/elixxir/crypto v0.0.7-0.20230413162806-a99ec4bfea32 gitlab.com/elixxir/primitives v0.0.3-0.20230214180039-9a25e2d3969c gitlab.com/xx_network/crypto v0.0.5-0.20230214003943-8a09396e95dd diff --git a/go.sum b/go.sum index e071dbf39c79e147644414620be8475fd8af96d7..c30479ce508ca63955094f661f6a6548bc14130a 100644 --- a/go.sum +++ b/go.sum @@ -515,6 +515,8 @@ gitlab.com/elixxir/client/v4 v4.6.2-0.20230425190953-cd51598e9245 h1:pBwoSYD+BFI gitlab.com/elixxir/client/v4 v4.6.2-0.20230425190953-cd51598e9245/go.mod h1:G+lN+LvQPGcm5BQnrhnqT1xiRIAzH3OffAM+5oI9SUg= gitlab.com/elixxir/client/v4 v4.6.2-0.20230511215110-b43e18a47875 h1:u9DlU8xAk0rTvguhWK+6D/MfLHdR+jlDTfGJjiszJDE= gitlab.com/elixxir/client/v4 v4.6.2-0.20230511215110-b43e18a47875/go.mod h1:dLKU2zSWrZLk/fomAtt1DFGgpTHQAfPdxdXNp3EtRZU= +gitlab.com/elixxir/client/v4 v4.6.2-0.20230512234533-17b97e5a36cc h1:f5lwmwejXTerTUjro9d5Ws1mKGD6ChXHMaBMhB3OhgA= +gitlab.com/elixxir/client/v4 v4.6.2-0.20230512234533-17b97e5a36cc/go.mod h1:dLKU2zSWrZLk/fomAtt1DFGgpTHQAfPdxdXNp3EtRZU= gitlab.com/elixxir/client/v4 v4.6.3 h1:oUsm5cn2Vnfqz+xwGYKrqFkPNN3sDAyp00EPGhUIA5E= gitlab.com/elixxir/client/v4 v4.6.3/go.mod h1:G+lN+LvQPGcm5BQnrhnqT1xiRIAzH3OffAM+5oI9SUg= gitlab.com/elixxir/comms v0.0.4-0.20230310205528-f06faa0d2f0b h1:8AVK93UEs/aufoqtFgyMVt9gf0oJ8F4pA60ZvEVvG+s= diff --git a/indexedDb/impl/channels/callbacks.go b/indexedDb/impl/channels/callbacks.go index 205a5e0df78b3cb6125042a963e5de8026fd4e08..6fc2235b5515e13e92d4bbf66135bc3647a2b740 100644 --- a/indexedDb/impl/channels/callbacks.go +++ b/indexedDb/impl/channels/callbacks.go @@ -10,8 +10,9 @@ package main import ( - "crypto/ed25519" "encoding/json" + "time" + "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" "gitlab.com/elixxir/client/v4/channels" @@ -24,7 +25,6 @@ import ( "gitlab.com/elixxir/xxdk-wasm/worker" "gitlab.com/xx_network/crypto/csprng" "gitlab.com/xx_network/primitives/id" - "time" ) var zeroUUID = []byte{0, 0, 0, 0, 0, 0, 0, 0} @@ -71,8 +71,7 @@ func (m *manager) newWASMEventModelCB(data []byte) ([]byte, error) { "failed to JSON unmarshal Cipher from main thread: %+v", err) } - m.model, err = NewWASMEventModel(msg.DatabaseName, encryption, - m.messageReceivedCallback, m.deletedMessageCallback, m.mutedUserCallback) + m.model, err = NewWASMEventModel(msg.DatabaseName, encryption, m) if err != nil { return []byte(err.Error()), nil } @@ -80,12 +79,8 @@ func (m *manager) newWASMEventModelCB(data []byte) ([]byte, error) { return []byte{}, nil } -// messageReceivedCallback sends calls to the channels.MessageReceivedCallback -// in the main thread. -// -// storeEncryptionStatus adhere to the channels.MessageReceivedCallback type. -func (m *manager) messageReceivedCallback( - uuid uint64, channelID *id.ID, update bool) { +// MessageReceived implements [bindings.ChannelUICallbacks.MessageReceived]. +func (m *manager) MessageReceived(uuid int64, channelID []byte, update bool) { // Package parameters for sending msg := &wChannels.MessageReceivedCallbackMessage{ UUID: uuid, @@ -102,20 +97,13 @@ func (m *manager) messageReceivedCallback( m.wtm.SendMessage(wChannels.MessageReceivedCallbackTag, data) } -// deletedMessageCallback sends calls to the channels.DeletedMessageCallback in -// the main thread. -// -// storeEncryptionStatus adhere to the channels.MessageReceivedCallback type. -func (m *manager) deletedMessageCallback(messageID message.ID) { - m.wtm.SendMessage(wChannels.DeletedMessageCallbackTag, messageID.Marshal()) +// MessageDeleted implements [bindings.ChannelUICallbacks.MessageDeleted]. +func (m *manager) MessageDeleted(messageID []byte) { + m.wtm.SendMessage(wChannels.DeletedMessageCallbackTag, messageID) } -// mutedUserCallback sends calls to the channels.MutedUserCallback in the main -// thread. -// -// storeEncryptionStatus adhere to the channels.MessageReceivedCallback type. -func (m *manager) mutedUserCallback( - channelID *id.ID, pubKey ed25519.PublicKey, unmute bool) { +// UserMuted implements [bindings.ChannelUICallbacks.UserMuted]. +func (m *manager) UserMuted(channelID, pubKey []byte, unmute bool) { // Package parameters for sending msg := &wChannels.MuteUserMessage{ ChannelID: channelID, @@ -132,6 +120,12 @@ func (m *manager) mutedUserCallback( m.wtm.SendMessage(wChannels.MutedUserCallbackTag, data) } +// NicknameUpdate implements [bindings.ChannelUICallbacks.NicknameUpdate] +func (m *manager) NicknameUpdate(channelIdBytes []byte, nickname string, + exists bool) { + jww.FATAL.Panicf("unimplemented") +} + // joinChannelCB is the callback for wasmModel.JoinChannel. Always returns nil; // meaning, no response is supplied (or expected). func (m *manager) joinChannelCB(data []byte) ([]byte, error) { @@ -368,7 +362,12 @@ func (m *manager) muteUserCB(data []byte) ([]byte, error) { "failed to JSON unmarshal %T from main thread: %+v", msg, err) } - m.model.MuteUser(msg.ChannelID, msg.PubKey, msg.Unmute) + channelID := id.ID{} + err = channelID.UnmarshalJSON(msg.ChannelID) + if err != nil { + return nil, err + } + m.model.MuteUser(&channelID, msg.PubKey, msg.Unmute) return nil, nil } diff --git a/indexedDb/impl/channels/implementation.go b/indexedDb/impl/channels/implementation.go index 0976d263b00011b7ec4a6a5d6bfce38c2780beb0..19dba81e1671cc2dbecbe4761df476473f83e0ad 100644 --- a/indexedDb/impl/channels/implementation.go +++ b/indexedDb/impl/channels/implementation.go @@ -21,13 +21,13 @@ import ( "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" + "gitlab.com/elixxir/client/v4/bindings" "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" - wChannels "gitlab.com/elixxir/xxdk-wasm/indexedDb/worker/channels" "gitlab.com/elixxir/xxdk-wasm/utils" "gitlab.com/xx_network/primitives/id" ) @@ -38,11 +38,9 @@ import ( // NOTE: This model is NOT thread safe - it is the responsibility of the // caller to ensure that its methods are called sequentially. type wasmModel struct { - db *idb.Database - cipher cryptoChannel.Cipher - receivedMessageCB wChannels.MessageReceivedCallback - deletedMessageCB wChannels.DeletedMessageCallback - mutedUserCB wChannels.MutedUserCallback + db *idb.Database + cipher cryptoChannel.Cipher + cbs bindings.ChannelUICallbacks } // JoinChannel is called whenever a channel is joined locally. @@ -161,8 +159,10 @@ func (w *wasmModel) ReceiveMessage(channelID *id.ID, messageID message.ID, } } + channelIDBytes := channelID.Marshal() + msgToInsert := buildMessage( - channelID.Marshal(), messageID.Bytes(), nil, nickname, + channelIDBytes, messageID.Bytes(), nil, nickname, textBytes, pubKey, dmToken, codeset, timestamp, lease, round.ID, mType, false, hidden, status) @@ -172,7 +172,7 @@ func (w *wasmModel) ReceiveMessage(channelID *id.ID, messageID message.ID, return 0 } - go w.receivedMessageCB(uuid, channelID, false) + go w.cbs.MessageReceived(int64(uuid), channelIDBytes, false) return uuid } @@ -199,7 +199,9 @@ func (w *wasmModel) ReceiveReply(channelID *id.ID, messageID, } } - msgToInsert := buildMessage(channelID.Marshal(), messageID.Bytes(), + channelIDBytes := channelID.Marshal() + + msgToInsert := buildMessage(channelIDBytes, messageID.Bytes(), replyTo.Bytes(), nickname, textBytes, pubKey, dmToken, codeset, timestamp, lease, round.ID, mType, hidden, false, status) @@ -208,7 +210,7 @@ func (w *wasmModel) ReceiveReply(channelID *id.ID, messageID, jww.ERROR.Printf("Failed to receive reply: %+v", err) return 0 } - go w.receivedMessageCB(uuid, channelID, false) + go w.cbs.MessageReceived(int64(uuid), channelIDBytes, false) return uuid } @@ -235,8 +237,9 @@ func (w *wasmModel) ReceiveReaction(channelID *id.ID, messageID, } } + channelIDBytes := channelID.Marshal() msgToInsert := buildMessage( - channelID.Marshal(), messageID.Bytes(), reactionTo.Bytes(), nickname, + channelIDBytes, messageID.Bytes(), reactionTo.Bytes(), nickname, textBytes, pubKey, dmToken, codeset, timestamp, lease, round.ID, mType, false, hidden, status) @@ -245,7 +248,7 @@ func (w *wasmModel) ReceiveReaction(channelID *id.ID, messageID, jww.ERROR.Printf("Failed to receive reaction: %+v", err) return 0 } - go w.receivedMessageCB(uuid, channelID, false) + go w.cbs.MessageReceived(int64(uuid), channelIDBytes, false) return uuid } @@ -392,9 +395,7 @@ func (w *wasmModel) updateMessage(currentMsg *Message, messageID *message.ID, if err != nil { return 0, err } - channelID := &id.ID{} - copy(channelID[:], currentMsg.ChannelID) - go w.receivedMessageCB(uuid, channelID, true) + go w.cbs.MessageReceived(int64(uuid), currentMsg.ChannelID, true) return uuid, nil } @@ -492,7 +493,7 @@ func (w *wasmModel) DeleteMessage(messageID message.ID) error { return err } - go w.deletedMessageCB(messageID) + go w.cbs.MessageDeleted(messageID.Bytes()) return nil } @@ -500,7 +501,7 @@ func (w *wasmModel) DeleteMessage(messageID message.ID) error { // 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) + go w.cbs.UserMuted(channelID.Marshal(), pubKey, unmute) } // valueToMessage is a helper for converting js.Value to Message. diff --git a/indexedDb/impl/channels/implementation_test.go b/indexedDb/impl/channels/implementation_test.go index 4b22b8300f5549a5dc2f3902f7ed95277777555f..d189d82413b9e6954dba28be9a1361b75e30d65e 100644 --- a/indexedDb/impl/channels/implementation_test.go +++ b/indexedDb/impl/channels/implementation_test.go @@ -11,17 +11,17 @@ package main import ( "bytes" - "crypto/ed25519" "encoding/json" "errors" "fmt" - cft "gitlab.com/elixxir/client/v4/channelsFileTransfer" - "gitlab.com/elixxir/crypto/fileTransfer" "os" "strconv" "testing" "time" + cft "gitlab.com/elixxir/client/v4/channelsFileTransfer" + "gitlab.com/elixxir/crypto/fileTransfer" + "github.com/hack-pad/go-indexeddb/idb" jww "github.com/spf13/jwalterweatherman" @@ -42,15 +42,19 @@ func TestMain(m *testing.M) { os.Exit(m.Run()) } -func dummyReceivedMessageCB(uint64, *id.ID, bool) {} -func dummyDeletedMessageCB(message.ID) {} -func dummyMutedUserCB(*id.ID, ed25519.PublicKey, bool) {} +type dummyCbs struct{} + +func (c *dummyCbs) MessageReceived(uuid int64, channelID []byte, update bool) {} +func (c *dummyCbs) UserMuted(channelID []byte, pubKey []byte, unmute bool) {} +func (c *dummyCbs) MessageDeleted(messageId []byte) {} +func (c *dummyCbs) NicknameUpdate(channelIdBytes []byte, nickname string, + exists bool) { +} // Happy path test for receiving, updating, getting, and deleting a File. func TestWasmModel_ReceiveFile(t *testing.T) { testString := "TestWasmModel_ReceiveFile" - m, err := newWASMModel(testString, nil, - dummyReceivedMessageCB, dummyDeletedMessageCB, dummyMutedUserCB) + m, err := newWASMModel(testString, nil, &dummyCbs{}) if err != nil { t.Fatal(err) } @@ -137,7 +141,7 @@ func TestWasmModel_GetMessage(t *testing.T) { testMsgId := message.DeriveChannelMessageID(&id.ID{1}, 0, []byte(testString)) eventModel, err := newWASMModel(testString, c, - dummyReceivedMessageCB, dummyDeletedMessageCB, dummyMutedUserCB) + &dummyCbs{}) if err != nil { t.Fatal(err) } @@ -167,8 +171,7 @@ 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, dummyReceivedMessageCB, - dummyDeletedMessageCB, dummyMutedUserCB) + eventModel, err := newWASMModel(testString, nil, &dummyCbs{}) if err != nil { t.Fatal(err) } @@ -225,7 +228,7 @@ func Test_wasmModel_UpdateSentStatus(t *testing.T) { testMsgId := message.DeriveChannelMessageID( &id.ID{1}, 0, []byte(testString)) eventModel, err2 := newWASMModel(testString, c, - dummyReceivedMessageCB, dummyDeletedMessageCB, dummyMutedUserCB) + &dummyCbs{}) if err2 != nil { t.Fatal(err) } @@ -292,8 +295,7 @@ 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, dummyReceivedMessageCB, - dummyDeletedMessageCB, dummyMutedUserCB) + eventModel, err2 := newWASMModel("test", c, &dummyCbs{}) if err2 != nil { t.Fatal(err2) } @@ -347,7 +349,7 @@ func Test_wasmModel_UUIDTest(t *testing.T) { storage.GetLocalStorage().Clear() testString := "testHello" + cs eventModel, err2 := newWASMModel(testString, c, - dummyReceivedMessageCB, dummyDeletedMessageCB, dummyMutedUserCB) + &dummyCbs{}) if err2 != nil { t.Fatal(err2) } @@ -394,7 +396,7 @@ func Test_wasmModel_DuplicateReceives(t *testing.T) { t.Run(testString, func(t *testing.T) { storage.GetLocalStorage().Clear() eventModel, err := newWASMModel(testString, c, - dummyReceivedMessageCB, dummyDeletedMessageCB, dummyMutedUserCB) + &dummyCbs{}) if err != nil { t.Fatal(err) } @@ -443,7 +445,7 @@ func Test_wasmModel_deleteMsgByChannel(t *testing.T) { totalMessages := 10 expectedMessages := 5 eventModel, err := newWASMModel(testString, c, - dummyReceivedMessageCB, dummyDeletedMessageCB, dummyMutedUserCB) + &dummyCbs{}) if err != nil { t.Fatal(err) } @@ -514,7 +516,7 @@ func TestWasmModel_receiveHelper_UniqueIndex(t *testing.T) { storage.GetLocalStorage().Clear() testString := fmt.Sprintf("test_receiveHelper_UniqueIndex_%d", i) eventModel, err := newWASMModel(testString, c, - dummyReceivedMessageCB, dummyDeletedMessageCB, dummyMutedUserCB) + &dummyCbs{}) if err != nil { t.Fatal(err) } diff --git a/indexedDb/impl/channels/init.go b/indexedDb/impl/channels/init.go index ecc16da447b0db684ca55031a26e6a3d19206320..282b4582fe49a98d9d62ec9912adb43ea6d549e5 100644 --- a/indexedDb/impl/channels/init.go +++ b/indexedDb/impl/channels/init.go @@ -15,10 +15,10 @@ import ( "github.com/hack-pad/go-indexeddb/idb" jww "github.com/spf13/jwalterweatherman" + "gitlab.com/elixxir/client/v4/bindings" "gitlab.com/elixxir/client/v4/channels" cryptoChannel "gitlab.com/elixxir/crypto/channel" "gitlab.com/elixxir/xxdk-wasm/indexedDb/impl" - wChannels "gitlab.com/elixxir/xxdk-wasm/indexedDb/worker/channels" ) // currentVersion is the current version of the IndexedDb runtime. Used for @@ -29,18 +29,13 @@ const currentVersion uint = 2 // The name should be a base64 encoding of the users public key. Returns the // EventModel based on IndexedDb and the database name as reported by IndexedDb. func NewWASMEventModel(databaseName string, encryption cryptoChannel.Cipher, - messageReceivedCB wChannels.MessageReceivedCallback, - deletedMessageCB wChannels.DeletedMessageCallback, - mutedUserCB wChannels.MutedUserCallback) (channels.EventModel, error) { - return newWASMModel(databaseName, encryption, messageReceivedCB, - deletedMessageCB, mutedUserCB) + channelsCbs bindings.ChannelUICallbacks) (channels.EventModel, error) { + return newWASMModel(databaseName, encryption, channelsCbs) } // newWASMModel creates the given [idb.Database] and returns a wasmModel. func newWASMModel(databaseName string, encryption cryptoChannel.Cipher, - messageReceivedCB wChannels.MessageReceivedCallback, - deletedMessageCB wChannels.DeletedMessageCallback, - mutedUserCB wChannels.MutedUserCallback) (*wasmModel, error) { + channelsCbs bindings.ChannelUICallbacks) (*wasmModel, error) { // Attempt to open database object ctx, cancel := impl.NewContext() defer cancel() @@ -86,11 +81,9 @@ func newWASMModel(databaseName string, encryption cryptoChannel.Cipher, } wrapper := &wasmModel{ - db: db, - cipher: encryption, - receivedMessageCB: messageReceivedCB, - deletedMessageCB: deletedMessageCB, - mutedUserCB: mutedUserCB, + db: db, + cipher: encryption, + cbs: channelsCbs, } return wrapper, nil } diff --git a/indexedDb/worker/channels/implementation.go b/indexedDb/worker/channels/implementation.go index 9639bc185095e74cbf4b3e63256fead39bbcef82..2e11702432dd5d4b1dae668715ccb6a7bdc50950 100644 --- a/indexedDb/worker/channels/implementation.go +++ b/indexedDb/worker/channels/implementation.go @@ -452,16 +452,16 @@ func (w *wasmModel) DeleteMessage(messageID message.ID) error { // MuteUserMessage is JSON marshalled and sent to the worker for // [wasmModel.MuteUser]. type MuteUserMessage struct { - ChannelID *id.ID `json:"channelID"` - PubKey ed25519.PublicKey `json:"pubKey"` - Unmute bool `json:"unmute"` + ChannelID []byte `json:"channelID"` + PubKey []byte `json:"pubKey"` + Unmute bool `json:"unmute"` } // MuteUser is called whenever a user is muted or unmuted. func (w *wasmModel) MuteUser( channelID *id.ID, pubKey ed25519.PublicKey, unmute bool) { msg := MuteUserMessage{ - ChannelID: channelID, + ChannelID: channelID.Marshal(), PubKey: pubKey, Unmute: unmute, } diff --git a/indexedDb/worker/channels/init.go b/indexedDb/worker/channels/init.go index 2ee630caf43177460a19f14d2b953d3a03b9c687..34dd3f0d2548042a3778267e9b5e46172c384dd6 100644 --- a/indexedDb/worker/channels/init.go +++ b/indexedDb/worker/channels/init.go @@ -10,19 +10,19 @@ package channels import ( - "crypto/ed25519" "encoding/json" - "github.com/pkg/errors" "time" + "github.com/pkg/errors" + jww "github.com/spf13/jwalterweatherman" + "gitlab.com/elixxir/client/v4/bindings" "gitlab.com/elixxir/client/v4/channels" cryptoChannel "gitlab.com/elixxir/crypto/channel" "gitlab.com/elixxir/crypto/message" "gitlab.com/elixxir/xxdk-wasm/storage" "gitlab.com/elixxir/xxdk-wasm/worker" - "gitlab.com/xx_network/primitives/id" ) // databaseSuffix is the suffix to be appended to the name of the database. @@ -31,26 +31,24 @@ const databaseSuffix = "_speakeasy" // MessageReceivedCallback is called any time a message is received or updated. // // update is true if the row is old and was edited. -type MessageReceivedCallback func(uuid uint64, channelID *id.ID, update bool) +type MessageReceivedCallback func(uuid int64, channelID []byte, update bool) // DeletedMessageCallback is called any time a message is deleted. -type DeletedMessageCallback func(messageID message.ID) +type DeletedMessageCallback func(messageID []byte) // 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) +type MutedUserCallback func(channelID, pubKey []byte, unmute bool) // NewWASMEventModelBuilder returns an EventModelBuilder which allows // the channel manager to define the path but the callback is the same // across the board. func NewWASMEventModelBuilder(wasmJsPath string, - encryption cryptoChannel.Cipher, messageReceivedCB MessageReceivedCallback, - deletedMessageCB DeletedMessageCallback, - mutedUserCB MutedUserCallback) channels.EventModelBuilder { + encryption cryptoChannel.Cipher, + channelCbs bindings.ChannelUICallbacks) channels.EventModelBuilder { fn := func(path string) (channels.EventModel, error) { return NewWASMEventModel(path, wasmJsPath, encryption, - messageReceivedCB, deletedMessageCB, mutedUserCB) + channelCbs) } return fn } @@ -65,8 +63,7 @@ type NewWASMEventModelMessage struct { // NewWASMEventModel returns a [channels.EventModel] backed by a wasmModel. // The name should be a base64 encoding of the users public key. func NewWASMEventModel(path, wasmJsPath string, encryption cryptoChannel.Cipher, - messageReceivedCB MessageReceivedCallback, - deletedMessageCB DeletedMessageCallback, mutedUserCB MutedUserCallback) ( + channelCbs bindings.ChannelUICallbacks) ( channels.EventModel, error) { databaseName := path + databaseSuffix @@ -77,15 +74,15 @@ func NewWASMEventModel(path, wasmJsPath string, encryption cryptoChannel.Cipher, // Register handler to manage messages for the MessageReceivedCallback wm.RegisterCallback(MessageReceivedCallbackTag, - messageReceivedCallbackHandler(messageReceivedCB)) + messageReceivedCallbackHandler(channelCbs.MessageReceived)) // Register handler to manage messages for the DeletedMessageCallback wm.RegisterCallback(DeletedMessageCallbackTag, - deletedMessageCallbackHandler(deletedMessageCB)) + deletedMessageCallbackHandler(channelCbs.MessageDeleted)) // Register handler to manage messages for the MutedUserCallback wm.RegisterCallback(MutedUserCallbackTag, - mutedUserCallbackHandler(mutedUserCB)) + mutedUserCallbackHandler(channelCbs.UserMuted)) // Store the database name err = storage.StoreIndexedDb(databaseName) @@ -135,8 +132,8 @@ func NewWASMEventModel(path, wasmJsPath string, encryption cryptoChannel.Cipher, // MessageReceivedCallbackMessage is JSON marshalled and received from the // worker for the [MessageReceivedCallback] callback. type MessageReceivedCallbackMessage struct { - UUID uint64 `json:"uuid"` - ChannelID *id.ID `json:"channelID"` + UUID int64 `json:"uuid"` + ChannelID []byte `json:"channelID"` Update bool `json:"update"` } @@ -166,7 +163,7 @@ func deletedMessageCallbackHandler(cb DeletedMessageCallback) func(data []byte) "Failed to JSON unmarshal message ID from worker: %+v", err) } - cb(messageID) + cb(messageID.Bytes()) } } diff --git a/wasm/channels.go b/wasm/channels.go index 307fe5cee304ff580ba1726b8036ce81268cc991..92231d692a1cd7ab4c685665fa6738333adbc731 100644 --- a/wasm/channels.go +++ b/wasm/channels.go @@ -10,17 +10,15 @@ 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" "syscall/js" + "gitlab.com/elixxir/client/v4/channels" + channelsDb "gitlab.com/elixxir/xxdk-wasm/indexedDb/worker/channels" + "gitlab.com/elixxir/client/v4/bindings" "gitlab.com/elixxir/xxdk-wasm/utils" ) @@ -274,7 +272,7 @@ func NewChannelsManager(_ js.Value, args []js.Value) any { em := newEventModelBuilder(args[3]) cm, err := bindings.NewChannelsManager( - cmixId, privateIdentity, extensionBuilderIDsJSON, em) + cmixId, privateIdentity, extensionBuilderIDsJSON, em, nil) if err != nil { utils.Throw(utils.TypeError, err) return nil @@ -299,13 +297,17 @@ func NewChannelsManager(_ js.Value, args []js.Value) any { // - args[2] - A function that initialises and returns a Javascript object // that matches the [bindings.EventModel] interface. The function must match // the Build function in [bindings.EventModelBuilder]. +// - args[3] - A callback object which implements the +// [bindings.ChannelUICallbacks] javascript functions. // // Returns: // - Javascript representation of the [ChannelsManager] object. // - Throws a TypeError if loading the manager fails. func LoadChannelsManager(_ js.Value, args []js.Value) any { em := newEventModelBuilder(args[2]) - cm, err := bindings.LoadChannelsManager(args[0].Int(), args[1].String(), em) + cUI := newChannelUI(args[3]) + cm, err := bindings.LoadChannelsManager(args[0].Int(), args[1].String(), + em, cUI) if err != nil { utils.Throw(utils.TypeError, err) return nil @@ -335,23 +337,9 @@ func LoadChannelsManager(_ js.Value, args []js.Value) any { // IDs. The ID can be retrieved from an object with an extension builder // (e.g., [ChannelsFileTransfer.GetExtensionBuilderID]). Leave empty if not // using extension builders. Example: `[2,11,5]` (Uint8Array). -// - args[4] - 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 [channels.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[5] - 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[6] - 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[7] - ID of [ChannelDbCipher] object in tracker (int). Create this +// - args[4] - A callback object which implements the +// [bindings.ChannelUICallbacks] javascript functions. +// - args[5] - ID of [ChannelDbCipher] object in tracker (int). Create this // object with [NewChannelsDatabaseCipher] and get its id with // [ChannelDbCipher.GetID]. // @@ -364,10 +352,8 @@ func NewChannelsManagerWithIndexedDb(_ js.Value, args []js.Value) any { wasmJsPath := args[1].String() privateIdentity := utils.CopyBytesToGo(args[2]) extensionBuilderIDsJSON := utils.CopyBytesToGo(args[3]) - messageReceivedCB := args[4] - deletedMessageCB := args[5] - mutedUserCB := args[6] - cipherID := args[7].Int() + channelCbs := newChannelUI(args[4]) + cipherID := args[5].Int() cipher, err := bindings.GetChannelDbCipherTrackerFromID(cipherID) if err != nil { @@ -375,8 +361,7 @@ func NewChannelsManagerWithIndexedDb(_ js.Value, args []js.Value) any { } return newChannelsManagerWithIndexedDb(cmixID, wasmJsPath, privateIdentity, - extensionBuilderIDsJSON, messageReceivedCB, deletedMessageCB, - mutedUserCB, cipher) + extensionBuilderIDsJSON, channelCbs, cipher) } // NewChannelsManagerWithIndexedDbUnsafe creates a new [ChannelsManager] from a @@ -401,22 +386,8 @@ func NewChannelsManagerWithIndexedDb(_ js.Value, args []js.Value) any { // IDs. The ID can be retrieved from an object with an extension builder // (e.g., [ChannelsFileTransfer.GetExtensionBuilderID]). Leave empty if not // using extension builders. Example: `[2,11,5]` (Uint8Array). -// - args[4] - 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[5] - 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[6] - 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[4] - A callback object which implements the +// [bindings.ChannelUICallbacks] javascript functions. // // Returns a promise: // - Resolves to a Javascript representation of the [ChannelsManager] object. @@ -428,38 +399,23 @@ func NewChannelsManagerWithIndexedDbUnsafe(_ js.Value, args []js.Value) any { wasmJsPath := args[1].String() privateIdentity := utils.CopyBytesToGo(args[2]) extensionBuilderIDsJSON := utils.CopyBytesToGo(args[3]) - messageReceivedCB := args[4] - deletedMessageCB := args[5] - mutedUserCB := args[6] + channelsCbs := newChannelUI(args[4]) return newChannelsManagerWithIndexedDb(cmixID, wasmJsPath, privateIdentity, - extensionBuilderIDsJSON, messageReceivedCB, deletedMessageCB, - mutedUserCB, nil) + extensionBuilderIDsJSON, channelsCbs, nil) } func newChannelsManagerWithIndexedDb(cmixID int, wasmJsPath string, - privateIdentity, extensionBuilderIDsJSON []byte, 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) - } + privateIdentity, extensionBuilderIDsJSON []byte, + channelsCbs bindings.ChannelUICallbacks, + cipher *bindings.ChannelDbCipher) any { model := channelsDb.NewWASMEventModelBuilder( - wasmJsPath, cipher, messageReceived, deletedMessage, mutedUser) + wasmJsPath, cipher, channelsCbs) promiseFn := func(resolve, reject func(args ...any) js.Value) { cm, err := bindings.NewChannelsManagerGoEventModel( - cmixID, privateIdentity, extensionBuilderIDsJSON, model) + cmixID, privateIdentity, extensionBuilderIDsJSON, model, channelsCbs) if err != nil { reject(utils.JsTrace(err)) } else { @@ -484,23 +440,9 @@ 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] - 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 +// - args[3] - A callback object which implements the +// [bindings.ChannelUICallbacks] javascript functions. +// - args[4] - ID of [ChannelDbCipher] object in tracker (int). Create this // object with [NewChannelsDatabaseCipher] and get its id with // [ChannelDbCipher.GetID]. // @@ -512,10 +454,8 @@ func LoadChannelsManagerWithIndexedDb(_ js.Value, args []js.Value) any { cmixID := args[0].Int() wasmJsPath := args[1].String() storageTag := args[2].String() - messageReceivedCB := args[3] - deletedMessageCB := args[4] - mutedUserCB := args[5] - cipherID := args[6].Int() + channelsCbs := newChannelUI(args[3]) + cipherID := args[4].Int() cipher, err := bindings.GetChannelDbCipherTrackerFromID(cipherID) if err != nil { @@ -523,7 +463,7 @@ func LoadChannelsManagerWithIndexedDb(_ js.Value, args []js.Value) any { } return loadChannelsManagerWithIndexedDb(cmixID, wasmJsPath, storageTag, - messageReceivedCB, deletedMessageCB, mutedUserCB, cipher) + channelsCbs, cipher) } // LoadChannelsManagerWithIndexedDbUnsafe loads an existing [ChannelsManager] @@ -542,22 +482,8 @@ 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] - 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[3] - A callback object which implements the +// [bindings.ChannelUICallbacks] javascript functions. // // Returns a promise: // - Resolves to a Javascript representation of the [ChannelsManager] object. @@ -566,37 +492,22 @@ func LoadChannelsManagerWithIndexedDbUnsafe(_ js.Value, args []js.Value) any { cmixID := args[0].Int() wasmJsPath := args[1].String() storageTag := args[2].String() - messageReceivedCB := args[3] - deletedMessageCB := args[3] - mutedUserCB := args[4] + cUI := newChannelUI(args[3]) return loadChannelsManagerWithIndexedDb(cmixID, wasmJsPath, storageTag, - messageReceivedCB, deletedMessageCB, mutedUserCB, nil) + cUI, nil) } func loadChannelsManagerWithIndexedDb(cmixID int, wasmJsPath, storageTag string, - messageReceivedCB, deletedMessageCB, mutedUserCB js.Value, + channelsCbs bindings.ChannelUICallbacks, 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, messageReceived, deletedMessage, mutedUser) + wasmJsPath, cipher, channelsCbs) promiseFn := func(resolve, reject func(args ...any) js.Value) { cm, err := bindings.LoadChannelsManagerGoEventModel( - cmixID, storageTag, model, nil) + cmixID, storageTag, model, nil, channelsCbs) if err != nil { reject(utils.JsTrace(err)) } else { @@ -2274,3 +2185,45 @@ func (c *ChannelDbCipher) UnmarshalJSON(_ js.Value, args []js.Value) any { } return nil } + +// newChannelUI maps the methods on the Javascript object to the +// channelUI callbacks implementation struct. +func newChannelUI(cbImpl js.Value) *channelUI { + return &channelUI{ + messageReceived: utils.WrapCB(cbImpl, "MessageReceived"), + userMuted: utils.WrapCB(cbImpl, "UserMuted"), + messageDeleted: utils.WrapCB(cbImpl, "MessageDeleted"), + nicknameUpdate: utils.WrapCB(cbImpl, "NicknameUpdate"), + } +} + +// eventModel wraps Javascript callbacks to adhere to the +// [bindings.ChannelUICallbacks] interface. +type channelUI struct { + messageReceived func(args ...any) js.Value + userMuted func(args ...any) js.Value + messageDeleted func(args ...any) js.Value + nicknameUpdate func(args ...any) js.Value +} + +// MessageReceived implements [bindings.ChannelUICallbacks.MessageReceived]. +func (c *channelUI) MessageReceived(uuid int64, channelID []byte, update bool) { + c.messageReceived(uuid, utils.CopyBytesToJS(channelID), update) +} + +// UserMuted implements [bindings.ChannelUICallbacks.UserMuted]. +func (c *channelUI) UserMuted(channelID []byte, pubKey []byte, unmute bool) { + c.userMuted(utils.CopyBytesToJS(channelID), utils.CopyBytesToJS(pubKey), + unmute) +} + +// MessageDeleted implements [bindings.ChannelUICallbacks.MessageDeleted]. +func (c *channelUI) MessageDeleted(messageId []byte) { + c.messageDeleted(utils.CopyBytesToJS(messageId)) +} + +// NicknameUpdate implements [bindings.ChannelUICallbacks.NicknameUpdate] +func (c *channelUI) NicknameUpdate(channelIdBytes []byte, nickname string, + exists bool) { + c.nicknameUpdate(utils.CopyBytesToJS(channelIdBytes), nickname, exists) +}