From 4ad84bdcf974a4497ac208a0298f82400302416f Mon Sep 17 00:00:00 2001
From: "Richard T. Carback III" <rick.carback@gmail.com>
Date: Wed, 28 Sep 2022 17:13:40 +0000
Subject: [PATCH] Callback interface implementation, fix to UpdateSentStatus
 race condition, Wasm -> WASM, and update client dependency

---
 go.mod                           |  2 +-
 go.sum                           |  2 ++
 indexedDb/implementation.go      | 17 +++++++++++++-
 indexedDb/implementation_test.go |  8 ++++---
 indexedDb/init.go                | 40 +++++++++++++++++++++++---------
 5 files changed, 53 insertions(+), 16 deletions(-)

diff --git a/go.mod b/go.mod
index 1ebdbb75..8476dd2d 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 v1.5.1-0.20220925005456-24eb354b1d6f
+	gitlab.com/elixxir/client v1.5.1-0.20220928170652-fe87f3ae67b4
 	gitlab.com/elixxir/crypto v0.0.7-0.20220923233816-0364f1b203c6
 	gitlab.com/elixxir/primitives v0.0.3-0.20220901220638-1acc75fabdc6
 	gitlab.com/xx_network/primitives v0.0.4-0.20220809193445-9fc0a5209548
diff --git a/go.sum b/go.sum
index ec84476e..debddfda 100644
--- a/go.sum
+++ b/go.sum
@@ -634,6 +634,8 @@ gitlab.com/elixxir/client v1.5.1-0.20220925004736-f9acd4f64d77 h1:wjyV+vXxAAapD8
 gitlab.com/elixxir/client v1.5.1-0.20220925004736-f9acd4f64d77/go.mod h1:z1Bdlja75CF3UrzifMC0LQwjlEdOcJCfXEX5k9AKQTQ=
 gitlab.com/elixxir/client v1.5.1-0.20220925005456-24eb354b1d6f h1:m0jwFnBatQH/3uIhSwO0le06ky80LLuzqKD+Xd4nwXk=
 gitlab.com/elixxir/client v1.5.1-0.20220925005456-24eb354b1d6f/go.mod h1:z1Bdlja75CF3UrzifMC0LQwjlEdOcJCfXEX5k9AKQTQ=
+gitlab.com/elixxir/client v1.5.1-0.20220928170652-fe87f3ae67b4 h1:WgpR5CjPDXSRF5lpRhyyexmNsMQq9xiwMJd3HeDOb68=
+gitlab.com/elixxir/client v1.5.1-0.20220928170652-fe87f3ae67b4/go.mod h1:z1Bdlja75CF3UrzifMC0LQwjlEdOcJCfXEX5k9AKQTQ=
 gitlab.com/elixxir/comms v0.0.4-0.20220916185715-f1e9a5eda939 h1:+VRx2ULHKs040bBhDAOKNCZnbcXxUk3jD9JoKQzQpLk=
 gitlab.com/elixxir/comms v0.0.4-0.20220916185715-f1e9a5eda939/go.mod h1:AO6XkMhaHJW8eXlgL5m3UUcJqsSP8F5Wm1GX+wyq/rw=
 gitlab.com/elixxir/crypto v0.0.0-20200804182833-984246dea2c4/go.mod h1:ucm9SFKJo+K0N2GwRRpaNr+tKXMIOVWzmyUD0SbOu2c=
diff --git a/indexedDb/implementation.go b/indexedDb/implementation.go
index 2614d6c3..d6e10517 100644
--- a/indexedDb/implementation.go
+++ b/indexedDb/implementation.go
@@ -12,6 +12,7 @@ package indexedDb
 import (
 	"context"
 	"encoding/json"
+	"sync"
 	"syscall/js"
 	"time"
 
@@ -35,7 +36,9 @@ const dbTimeout = time.Second
 // system passed an object that adheres to in order to get events on the
 // channel.
 type wasmModel struct {
-	db *idb.Database
+	db                *idb.Database
+	receivedMessageCB MessageReceivedCallback
+	updateMux         sync.Mutex
 }
 
 // newContext builds a context for database operations.
@@ -157,6 +160,7 @@ func (w *wasmModel) ReceiveMessage(channelID *id.ID,
 	if err != nil {
 		jww.ERROR.Printf("%+v", errors.Wrap(parentErr, err.Error()))
 	}
+	go w.receivedMessageCB(uuid, channelID)
 	return uuid
 }
 
@@ -179,6 +183,7 @@ func (w *wasmModel) ReceiveReply(channelID *id.ID,
 	if err != nil {
 		jww.ERROR.Printf("%+v", errors.Wrap(parentErr, err.Error()))
 	}
+	go w.receivedMessageCB(uuid, channelID)
 	return uuid
 }
 
@@ -200,6 +205,7 @@ func (w *wasmModel) ReceiveReaction(channelID *id.ID, messageID cryptoChannel.Me
 	if err != nil {
 		jww.ERROR.Printf("%+v", errors.Wrap(parentErr, err.Error()))
 	}
+	go w.receivedMessageCB(uuid, channelID)
 	return uuid
 }
 
@@ -210,6 +216,12 @@ func (w *wasmModel) UpdateSentStatus(uuid uint64, messageID cryptoChannel.Messag
 	timestamp time.Time, round rounds.Round, status channels.SentStatus) {
 	parentErr := errors.New("failed to UpdateSentStatus")
 
+	// FIXME: this is a bit of race condition without the mux.
+	//        This should be done via the transactions (i.e., make a
+	//        special version of receiveHelper)
+	w.updateMux.Lock()
+	defer w.updateMux.Unlock()
+
 	// Convert messageID to the key generated by json.Marshal
 	key := js.ValueOf(uuid)
 
@@ -232,6 +244,9 @@ func (w *wasmModel) UpdateSentStatus(uuid uint64, messageID cryptoChannel.Messag
 	if err != nil {
 		jww.ERROR.Printf("%+v", errors.Wrap(parentErr, err.Error()))
 	}
+	channelID := &id.ID{}
+	copy(channelID[:], newMessage.ChannelID)
+	go w.receivedMessageCB(uuid, channelID)
 }
 
 // buildMessage is a private helper that converts typical [channels.EventModel]
diff --git a/indexedDb/implementation_test.go b/indexedDb/implementation_test.go
index e6479da1..5b47174f 100644
--- a/indexedDb/implementation_test.go
+++ b/indexedDb/implementation_test.go
@@ -29,11 +29,13 @@ func TestMain(m *testing.M) {
 	os.Exit(m.Run())
 }
 
+func dummyCallback(uuid uint64, channelID *id.ID) {}
+
 // Test wasmModel.UpdateSentStatus happy path and ensure fields don't change.
 func TestWasmModel_UpdateSentStatus(t *testing.T) {
 	testString := "test"
 	testMsgId := channel.MakeMessageID([]byte(testString))
-	eventModel, err := newWasmModel(testString)
+	eventModel, err := newWASMModel(testString, dummyCallback)
 	if err != nil {
 		t.Fatalf("%+v", err)
 	}
@@ -88,7 +90,7 @@ func TestWasmModel_UpdateSentStatus(t *testing.T) {
 
 // Smoke test wasmModel.JoinChannel/wasmModel.LeaveChannel happy paths.
 func TestWasmModel_JoinChannel_LeaveChannel(t *testing.T) {
-	eventModel, err := newWasmModel("test")
+	eventModel, err := newWASMModel("test", dummyCallback)
 	if err != nil {
 		t.Fatalf("%+v", err)
 	}
@@ -128,7 +130,7 @@ func TestWasmModel_JoinChannel_LeaveChannel(t *testing.T) {
 func TestWasmModel_UUIDTest(t *testing.T) {
 	testString := "testHello"
 	testMsgId := channel.MakeMessageID([]byte(testString))
-	eventModel, err := newWasmModel(testString)
+	eventModel, err := newWASMModel(testString, dummyCallback)
 	if err != nil {
 		t.Fatalf("%+v", err)
 	}
diff --git a/indexedDb/init.go b/indexedDb/init.go
index 20c8313c..af98dec2 100644
--- a/indexedDb/init.go
+++ b/indexedDb/init.go
@@ -17,26 +17,44 @@ import (
 	jww "github.com/spf13/jwalterweatherman"
 
 	"gitlab.com/elixxir/client/channels"
+	"gitlab.com/xx_network/primitives/id"
 )
 
 const (
-	// databaseSuffix is the suffix to be appended to the name of the database.
-	databaseSuffix = "_messenger"
+	// databaseSuffix is the suffix to be appended to the name of
+	// the database.
+	databaseSuffix = "_speakeasy"
 
-	// currentVersion is the current version of the IndexDb runtime. Used for
-	// migration purposes.
+	// currentVersion is the current version of the IndexDb
+	// runtime. Used for migration purposes.
 	currentVersion uint = 1
 )
 
-// NewWasmEventModel returns a [channels.EventModel] backed by a wasmModel.
+// MessageReceivedCallback is called any time a message is received or updated
+type MessageReceivedCallback func(uuid uint64, channelID *id.ID)
+
+// NewWASMEventModelBuilder returns an EventModelBuilder which allows
+// the channel manager to define the path but the callback is the same
+// across the board.
+func NewWASMEventModelBuilder(
+	cb MessageReceivedCallback) channels.EventModelBuilder {
+	fn := func(path string) (channels.EventModel, error) {
+		return NewWASMEventModel(path, cb)
+	}
+	return fn
+}
+
+// NewWASMEventModel returns a [channels.EventModel] backed by a wasmModel.
 // The name should be a base64 encoding of the users public key.
-func NewWasmEventModel(pubkeyBase64 string) (channels.EventModel, error) {
-	databaseName := pubkeyBase64 + databaseSuffix
-	return newWasmModel(databaseName)
+func NewWASMEventModel(path string, cb MessageReceivedCallback) (
+	channels.EventModel, error) {
+	databaseName := path + databaseSuffix
+	return newWASMModel(databaseName, cb)
 }
 
-// newWasmModel creates the given [idb.Database] and returns a wasmModel.
-func newWasmModel(databaseName string) (*wasmModel, error) {
+// newWASMModel creates the given [idb.Database] and returns a wasmModel.
+func newWASMModel(databaseName string, cb MessageReceivedCallback) (
+	*wasmModel, error) {
 	// Attempt to open database object
 	ctx, cancel := newContext()
 	defer cancel()
@@ -61,7 +79,7 @@ func newWasmModel(databaseName string) (*wasmModel, error) {
 	// Wait for database open to finish
 	db, err := openRequest.Await(ctx)
 
-	return &wasmModel{db: db}, err
+	return &wasmModel{db: db, receivedMessageCB: cb}, err
 }
 
 // v1Upgrade performs the v0 -> v1 database upgrade.
-- 
GitLab