2021-12-21 13:39:46 +01:00
|
|
|
import { PageDirection, Waku, WakuMessage } from "js-waku";
|
2021-10-28 09:47:14 +02:00
|
|
|
|
|
|
|
import { idToContactCodeTopic } from "./contentTopic";
|
|
|
|
import { Identity } from "./identity";
|
|
|
|
import { StatusUpdate_StatusType } from "./proto/communities/v1/status_update";
|
|
|
|
import { bufToHex } from "./utils";
|
2021-12-21 13:39:46 +01:00
|
|
|
import { ChatIdentity } from "./wire/chat_identity";
|
2021-10-28 09:47:14 +02:00
|
|
|
import { StatusUpdate } from "./wire/status_update";
|
|
|
|
|
2021-11-18 16:34:26 +01:00
|
|
|
const STATUS_BROADCAST_INTERVAL = 30000;
|
2022-01-04 23:40:21 +01:00
|
|
|
const NICKNAME_BROADCAST_INTERVAL = 300000;
|
2021-10-28 15:01:43 +02:00
|
|
|
|
2021-10-28 09:47:14 +02:00
|
|
|
export class Contacts {
|
|
|
|
waku: Waku;
|
2021-12-17 18:45:32 +01:00
|
|
|
identity: Identity | undefined;
|
2021-12-21 13:39:46 +01:00
|
|
|
nickname?: string;
|
2021-10-28 15:01:43 +02:00
|
|
|
private callback: (publicKey: string, clock: number) => void;
|
2021-12-21 13:39:46 +01:00
|
|
|
private callbackNickname: (publicKey: string, nickname: string) => void;
|
2021-10-28 09:47:14 +02:00
|
|
|
private contacts: string[] = [];
|
|
|
|
|
2021-10-28 15:01:43 +02:00
|
|
|
/**
|
|
|
|
* Contacts holds a list of user contacts and listens to their status broadcast
|
|
|
|
*
|
|
|
|
* When watched user broadcast callback is called.
|
|
|
|
*
|
|
|
|
* Class also broadcasts own status on contact-code topic
|
|
|
|
*
|
|
|
|
* @param identity identity of user that is used to broadcast status message
|
|
|
|
*
|
|
|
|
* @param waku waku class used to listen to broadcast and broadcast status
|
|
|
|
*
|
|
|
|
* @param callback callback function called when user status broadcast is received
|
|
|
|
*/
|
2021-10-28 09:47:14 +02:00
|
|
|
public constructor(
|
2021-12-17 18:45:32 +01:00
|
|
|
identity: Identity | undefined,
|
2021-10-28 09:47:14 +02:00
|
|
|
waku: Waku,
|
2021-12-21 13:39:46 +01:00
|
|
|
callback: (publicKey: string, clock: number) => void,
|
|
|
|
callbackNickname: (publicKey: string, nickname: string) => void,
|
|
|
|
nickname?: string
|
2021-10-28 09:47:14 +02:00
|
|
|
) {
|
|
|
|
this.waku = waku;
|
|
|
|
this.identity = identity;
|
2021-12-21 13:39:46 +01:00
|
|
|
this.nickname = nickname;
|
2021-10-28 09:47:14 +02:00
|
|
|
this.callback = callback;
|
2021-12-21 13:39:46 +01:00
|
|
|
this.callbackNickname = callbackNickname;
|
2021-10-28 09:47:14 +02:00
|
|
|
this.startBroadcast();
|
2021-12-17 18:45:32 +01:00
|
|
|
if (identity) {
|
|
|
|
this.addContact(bufToHex(identity.publicKey));
|
|
|
|
}
|
2021-10-28 09:47:14 +02:00
|
|
|
}
|
|
|
|
|
2021-10-28 15:01:43 +02:00
|
|
|
/**
|
|
|
|
* Add contact to watch list of status broadcast
|
|
|
|
*
|
|
|
|
* When user broadcasts its status callback is called
|
|
|
|
*
|
|
|
|
* @param publicKey public key of user
|
|
|
|
*/
|
|
|
|
public addContact(publicKey: string): void {
|
|
|
|
if (!this.contacts.find((e) => publicKey === e)) {
|
2021-10-28 09:47:14 +02:00
|
|
|
const now = new Date();
|
|
|
|
const callback = (wakuMessage: WakuMessage): void => {
|
|
|
|
if (wakuMessage.payload) {
|
|
|
|
const msg = StatusUpdate.decode(wakuMessage.payload);
|
2021-10-28 15:01:43 +02:00
|
|
|
this.callback(publicKey, msg.clock ?? 0);
|
2021-10-28 09:47:14 +02:00
|
|
|
}
|
|
|
|
};
|
2021-10-28 15:01:43 +02:00
|
|
|
this.contacts.push(publicKey);
|
|
|
|
this.callback(publicKey, 0);
|
|
|
|
this.waku.store.queryHistory([idToContactCodeTopic(publicKey)], {
|
2021-10-28 09:47:14 +02:00
|
|
|
callback: (msgs) => msgs.forEach((e) => callback(e)),
|
|
|
|
timeFilter: {
|
2021-10-28 15:01:43 +02:00
|
|
|
startTime: new Date(now.getTime() - STATUS_BROADCAST_INTERVAL * 2),
|
2021-10-28 09:47:14 +02:00
|
|
|
endTime: now,
|
|
|
|
},
|
|
|
|
});
|
2021-12-21 13:39:46 +01:00
|
|
|
this.waku.store.queryHistory([idToContactCodeTopic(publicKey)], {
|
|
|
|
callback: (msgs) =>
|
|
|
|
msgs.some((e) => {
|
|
|
|
try {
|
|
|
|
if (e.payload) {
|
|
|
|
const chatIdentity = ChatIdentity.decode(e?.payload);
|
|
|
|
if (chatIdentity) {
|
|
|
|
this.callbackNickname(
|
|
|
|
publicKey,
|
|
|
|
chatIdentity?.displayName ?? ""
|
|
|
|
);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
} catch {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}),
|
|
|
|
pageDirection: PageDirection.BACKWARD,
|
|
|
|
});
|
2021-10-28 15:01:43 +02:00
|
|
|
this.waku.relay.addObserver(callback, [idToContactCodeTopic(publicKey)]);
|
2021-10-28 09:47:14 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private startBroadcast(): void {
|
|
|
|
const send = async (): Promise<void> => {
|
2021-12-17 18:45:32 +01:00
|
|
|
if (this.identity) {
|
|
|
|
const statusUpdate = StatusUpdate.create(
|
|
|
|
StatusUpdate_StatusType.AUTOMATIC,
|
|
|
|
""
|
|
|
|
);
|
|
|
|
const msg = await WakuMessage.fromBytes(
|
|
|
|
statusUpdate.encode(),
|
|
|
|
idToContactCodeTopic(bufToHex(this.identity.publicKey))
|
|
|
|
);
|
|
|
|
this.waku.relay.send(msg);
|
|
|
|
}
|
2021-10-28 09:47:14 +02:00
|
|
|
};
|
2021-12-21 13:39:46 +01:00
|
|
|
|
2022-01-04 23:40:21 +01:00
|
|
|
const handleNickname = async (): Promise<void> => {
|
|
|
|
if (this.identity) {
|
|
|
|
const publicKey = bufToHex(this.identity.publicKey);
|
|
|
|
const now = new Date().getTime();
|
|
|
|
let newNickname = "";
|
|
|
|
let clock = 0;
|
|
|
|
await this.waku.store.queryHistory([idToContactCodeTopic(publicKey)], {
|
|
|
|
callback: (msgs) =>
|
|
|
|
msgs.some((e) => {
|
|
|
|
try {
|
|
|
|
if (e.payload) {
|
|
|
|
const chatIdentity = ChatIdentity.decode(e?.payload);
|
|
|
|
if (chatIdentity) {
|
|
|
|
if (chatIdentity?.displayName) {
|
|
|
|
clock = chatIdentity?.clock ?? 0;
|
|
|
|
newNickname = chatIdentity?.displayName;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
} catch {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}),
|
|
|
|
pageDirection: PageDirection.BACKWARD,
|
|
|
|
});
|
|
|
|
|
|
|
|
if (this.nickname) {
|
|
|
|
if (this.nickname !== newNickname) {
|
|
|
|
await sendNickname();
|
|
|
|
} else {
|
|
|
|
if (clock < now - NICKNAME_BROADCAST_INTERVAL) {
|
|
|
|
await sendNickname();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
this.nickname = newNickname;
|
|
|
|
this.callbackNickname(publicKey, newNickname);
|
|
|
|
if (clock < now - NICKNAME_BROADCAST_INTERVAL) {
|
|
|
|
await sendNickname();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
setInterval(send, NICKNAME_BROADCAST_INTERVAL);
|
|
|
|
};
|
|
|
|
|
2021-12-21 13:39:46 +01:00
|
|
|
const sendNickname = async (): Promise<void> => {
|
2022-01-03 10:42:06 +01:00
|
|
|
if (this.identity) {
|
|
|
|
const publicKey = bufToHex(this.identity.publicKey);
|
|
|
|
if (this.nickname) {
|
|
|
|
const chatIdentity = new ChatIdentity({
|
|
|
|
clock: new Date().getTime(),
|
|
|
|
color: "",
|
|
|
|
description: "",
|
|
|
|
emoji: "",
|
|
|
|
images: {},
|
|
|
|
ensName: "",
|
|
|
|
displayName: this?.nickname ?? "",
|
|
|
|
});
|
|
|
|
const msg = await WakuMessage.fromBytes(
|
|
|
|
chatIdentity.encode(),
|
|
|
|
idToContactCodeTopic(publicKey),
|
|
|
|
{ sigPrivKey: this.identity.privateKey }
|
|
|
|
);
|
|
|
|
await this.waku.relay.send(msg);
|
|
|
|
}
|
2021-12-21 13:39:46 +01:00
|
|
|
}
|
|
|
|
};
|
2022-01-04 23:40:21 +01:00
|
|
|
handleNickname();
|
2021-10-28 09:47:14 +02:00
|
|
|
send();
|
2021-10-28 15:01:43 +02:00
|
|
|
setInterval(send, STATUS_BROADCAST_INTERVAL);
|
2021-10-28 09:47:14 +02:00
|
|
|
}
|
|
|
|
}
|