Skip to content

Commit

Permalink
chat example working; add value on pbs; restructured how updates are …
Browse files Browse the repository at this point in the history
…handled
  • Loading branch information
d-roak committed Sep 9, 2024
1 parent 17b039d commit 9e70b72
Show file tree
Hide file tree
Showing 16 changed files with 2,241 additions and 1,053 deletions.
1 change: 0 additions & 1 deletion examples/chat/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ <h1>Topology Protocol - Chat</h1>

<input id="roomInput" type="text" placeholder="Room ID" />
<button id="joinRoom">Connect</button>
<button id="fetchMessages">Fetch Messages</button>
<button id="createRoom">Create Room</button>
</div>

Expand Down
14 changes: 0 additions & 14 deletions examples/chat/src/handlers.ts

This file was deleted.

115 changes: 37 additions & 78 deletions examples/chat/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
import { GSet } from "@topology-foundation/crdt";
import { TopologyNode } from "@topology-foundation/node";
import {
type TopologyObject,
newTopologyObject,
} from "@topology-foundation/object";
import type { TopologyObject } from "@topology-foundation/object";
import { handleObjectOps } from "./handlers";
import { Chat, addMessage, getMessages } from "./objects/chat";
import { Chat } from "./objects/chat";

const node = new TopologyNode();
// CRO = Conflict-free Replicated Object
let topologyObject: TopologyObject<Chat>;
let topologyObject: TopologyObject;
let chatCRO: Chat;
let peers: string[] = [];
let discoveryPeers: string[] = [];
let objectPeers: string[] = [];

const render = () => {
if (topologyObject)
(<HTMLButtonElement>document.getElementById("chatId")).innerText =
topologyObject.id;
const element_peerId = <HTMLDivElement>document.getElementById("peerId");
element_peerId.innerHTML = node.networkNode.peerId;

Expand All @@ -33,7 +33,7 @@ const render = () => {
element_objectPeers.innerHTML = `[${objectPeers.join(", ")}]`;

if (!chatCRO) return;
const chat = getMessages(chatCRO);
const chat = chatCRO.getMessages();
const element_chat = <HTMLDivElement>document.getElementById("chat");
element_chat.innerHTML = "";

Expand All @@ -59,19 +59,29 @@ async function sendMessage(message: string) {
alert("Please create or join a chat room first");
return;
}
console.log(
"Sending message: ",
`(${timestamp}, ${message}, ${node.networkNode.peerId})`,
);

// call the fn on the chat.ts object and the topologyObject
addMessage(chatCRO, timestamp, message, node.networkNode.peerId);
node.updateObject(topologyObject.id, [
{
fn: "addMessage",
args: [timestamp, message, node.networkNode.peerId],
},
]);
chatCRO.addMessage(timestamp, message, node.networkNode.peerId);
render();
}

async function createConnectHandlers() {
node.addCustomGroupMessageHandler(topologyObject.id, (e) => {
// on create/connect
if (topologyObject)
objectPeers = node.networkNode.getGroupPeers(topologyObject.id);
render();
});

node.objectStore.subscribe(topologyObject.id, (_, obj) => {
chatCRO.mergeCallback(
obj.vertices.map((v) => {
return {
type: v.operation ? v.operation.type : "",
value: v.operation ? v.operation.value : "",
};
}),
);
});

render();
}
Expand All @@ -93,22 +103,7 @@ async function main() {
button_create.addEventListener("click", async () => {
topologyObject = await node.createObject(new Chat());
chatCRO = topologyObject.cro as Chat;

node.addCustomGroupMessageHandler(topologyObject.id, (e) => {
// on create/connect
if (topologyObject)
objectPeers = node.networkNode.getGroupPeers(topologyObject.id);
render();
});

node.objectStore.subscribe(topologyObject.id, (_, obj) => {
console.log("Received object operations: ", obj);
handleObjectOps(chatCRO, obj.vertices);
});

(<HTMLButtonElement>document.getElementById("chatId")).innerText =
topologyObject.id;
render();
createConnectHandlers();
});

const button_connect = <HTMLButtonElement>document.getElementById("joinRoom");
Expand All @@ -122,50 +117,14 @@ async function main() {
return;
}

chatCRO = new Chat();
topologyObject = await node.subscribeObject(objectId, true);

// message handler for the CRO
node.addCustomGroupMessageHandler(topologyObject.id, (e) => {
// on create/connect
if (topologyObject)
objectPeers = node.networkNode.getGroupPeers(topologyObject.id);
render();
});

node.objectStore.subscribe(topologyObject.id, (_, obj) => {
console.log("Received object operations: ", obj);
handleObjectOps(chatCRO, obj.operations);
});
});

const button_fetch = <HTMLButtonElement>(
document.getElementById("fetchMessages")
);
button_fetch.addEventListener("click", async () => {
const input: HTMLInputElement = <HTMLInputElement>(
document.getElementById("roomInput")
topologyObject = await node.createObject(
new Chat(),
objectId,
undefined,
true,
);
const objectId = input.value;
try {
// biome-ignore lint/suspicious/noExplicitAny: TODO fix this
const object: any = node.objectStore.get(objectId);
console.log("Object received: ", object);

const arr: string[] = Array.from(object.chat._set);
object.chat._set = new Set<string>(arr);
object.chat = Object.assign(
new GSet<string>(new Set<string>()),
object.chat,
);
chatCRO = Object.assign(new Chat(), object);

(<HTMLButtonElement>document.getElementById("chatId")).innerText =
topologyObject.id;
render();
} catch (e) {
console.error("Error while connecting to the CRO ", objectId, e);
}
chatCRO = topologyObject.cro as Chat;
createConnectHandlers();
});

const button_send = <HTMLButtonElement>document.getElementById("sendMessage");
Expand Down
22 changes: 17 additions & 5 deletions examples/chat/src/objects/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ import {
} from "@topology-foundation/crdt";
import {
ActionType,
Vertex,
type Vertex,
type CRO,
type Operation,
} from "@topology-foundation/object";

export class Chat implements CRO<Chat> {
export class Chat implements CRO {
operations: string[] = ["addMessage"];
// store messages as strings in the format (timestamp, message, nodeId)
messages: GSet<string>;
Expand All @@ -21,6 +21,14 @@ export class Chat implements CRO<Chat> {
}

addMessage(timestamp: string, message: string, nodeId: string): void {
this._addMessage(timestamp, message, nodeId);
}

private _addMessage(
timestamp: string,
message: string,
nodeId: string,
): void {
this.messages.add(`(${timestamp}, ${message}, ${nodeId})`);
}

Expand All @@ -32,13 +40,17 @@ export class Chat implements CRO<Chat> {
this.messages.merge(other.messages);
}

resolveConflicts(vertices: Vertex<Chat>[]): ActionType {
resolveConflicts(vertices: Vertex[]): ActionType {
return ActionType.Nop;
}

mergeCallback(operations: Operation<Chat>[]): void {
mergeCallback(operations: Operation[]): void {
for (const op of operations) {
console.log(op);
const args = op.value as string[];
// just addMessage
this.messages.add(`(${args[0]}, ${args[1]}, ${args[2]})`);
this._addMessage(args[0], args[1], args[2]);
console.log(op, args);
}
}
}
Expand Down
13 changes: 8 additions & 5 deletions packages/network/src/proto/messages.proto
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
syntax = "proto3";
package topology.network;
import "google/protobuf/struct.proto";

// Supposed to be the RIBLT stuff
message Vertex {
message Operation {
string type = 1;
string value = 2;
repeated google.protobuf.Value value = 2;
}
string hash = 1;
string nodeId = 2;
Expand All @@ -28,16 +29,18 @@ message Message {
}

message Update {
string objectId = 1;
}

message Sync {
repeated string vertex_hashes = 1;
string objectId = 1;
repeated string vertex_hashes = 2;
}

message SyncAccept {
// not strings
repeated Vertex diff = 1;
repeated string missing = 2;
string objectId = 1;
repeated Vertex diff = 2;
repeated string missing = 3;
}

message SyncReject { }
Loading

0 comments on commit 9e70b72

Please sign in to comment.