feat: implement WebAssembly chat functionality with message sending and announcing capabilities, including tests for function registration

This commit is contained in:
2025-12-31 11:50:38 -06:00
parent ff0088644e
commit 562f850b8f
3 changed files with 154 additions and 0 deletions

78
examples/wasm/main.go Normal file
View File

@@ -0,0 +1,78 @@
//go:build js && wasm
// +build js,wasm
package main
import (
"encoding/hex"
"fmt"
"syscall/js"
"git.quad4.io/Networks/Reticulum-Go/pkg/wasm"
"git.quad4.io/RNS-Things/reticulum-go-mf/pkg/mf"
)
var messenger *mf.Messenger
func main() {
// Register the generic WASM bridge functions first
wasm.RegisterJSFunctions()
// Add chat-specific functions to the "reticulum" JS object
reticulum := js.Global().Get("reticulum")
reticulum.Set("sendMessage", js.FuncOf(SendMessage))
reticulum.Set("sendAnnounce", js.FuncOf(SendAnnounce))
// Keep the Go program running
select {}
}
func SendMessage(this js.Value, args []js.Value) interface{} {
if len(args) < 2 {
return js.ValueOf(map[string]interface{}{
"error": "Destination hash and message required",
})
}
destHashHex := args[0].String()
message := args[1].String()
destHash, err := hex.DecodeString(destHashHex)
if err != nil {
return js.ValueOf(map[string]interface{}{
"error": fmt.Sprintf("Invalid destination hash: %v", err),
})
}
// Initialize messenger if not already done
if messenger == nil {
t := wasm.GetTransport()
d := wasm.GetDestinationPointer()
if t == nil || d == nil {
return js.ValueOf(map[string]interface{}{
"error": "Reticulum not initialized",
})
}
messenger = mf.NewMessenger(t, d)
}
// Use the high-level Messenger from mf package
if err := messenger.SendMessage(destHash, message); err != nil {
return js.ValueOf(map[string]interface{}{
"error": fmt.Sprintf("Send failed: %v", err),
})
}
return js.ValueOf(map[string]interface{}{
"success": true,
})
}
func SendAnnounce(this js.Value, args []js.Value) interface{} {
var appData []byte
if len(args) >= 1 && args[0].String() != "" {
appData = []byte(args[0].String())
}
return wasm.SendAnnounce(appData)
}

View File

@@ -0,0 +1,33 @@
//go:build js && wasm
// +build js,wasm
package main
import (
"syscall/js"
"testing"
"git.quad4.io/Networks/Reticulum-Go/pkg/wasm"
)
func TestRegisterFunctions(t *testing.T) {
// Register functions
wasm.RegisterJSFunctions()
reticulum := js.Global().Get("reticulum")
if reticulum.IsUndefined() {
t.Fatal("reticulum object not registered")
}
// Manually register chat functions since main() has select{}
reticulum.Set("sendMessage", js.FuncOf(SendMessage))
reticulum.Set("sendAnnounce", js.FuncOf(SendAnnounce))
tests := []string{"sendMessage", "sendAnnounce", "init", "getIdentity", "sendData"}
for _, name := range tests {
if reticulum.Get(name).Type() != js.TypeFunction {
t.Errorf("function %s not registered correctly", name)
}
}
}

View File

@@ -0,0 +1,43 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Reticulum WASM Chat Example</title>
</head>
<body>
<script src="js/wasm_exec.js"></script>
<script>
if (!WebAssembly.instantiateStreaming) { // polyfill
WebAssembly.instantiateStreaming = async (resp, importObject) => {
const source = await (await resp).arrayBuffer();
return await WebAssembly.instantiate(source, importObject);
};
}
const go = new Go();
let mod, inst;
WebAssembly.instantiateStreaming(fetch("static/reticulum-go.wasm"), go.importObject).then(async (result) => {
mod = result.module;
inst = result.instance;
console.log("WASM loaded");
await go.run(inst);
}).catch((err) => {
console.error(err);
});
// Basic chat interface helper
window.onChatMessage = (msg) => {
console.log("Chat message received:", msg);
};
window.onPeerDiscovered = (peer) => {
console.log("Peer discovered:", peer);
};
window.log = (msg, level) => {
console.log(`[${level}] ${msg}`);
};
</script>
<h1>Reticulum WASM Chat</h1>
<p>Open console to see output.</p>
</body>
</html>