diff --git a/packages/react-chat-example/src/index.tsx b/packages/react-chat-example/src/index.tsx
index 4bcde45e..3227de4f 100644
--- a/packages/react-chat-example/src/index.tsx
+++ b/packages/react-chat-example/src/index.tsx
@@ -1,4 +1,4 @@
-import { lightTheme, ReactChat } from "@dappconnect/react-chat";
+import { darkTheme, lightTheme, ReactChat } from "@dappconnect/react-chat";
import React, { useRef, useState } from "react";
import ReactDOM from "react-dom";
import styled from "styled-components";
@@ -32,6 +32,8 @@ function DragDiv() {
const moved = useRef(false);
const setting = useRef("");
+ const [theme, setTheme] = useState(true);
+
const onMouseMove = (e: MouseEvent) => {
if (setting.current === "position") {
e.preventDefault();
@@ -54,33 +56,42 @@ function DragDiv() {
};
return (
-
- {
- setting.current = "position";
- document.addEventListener("mousemove", onMouseMove);
- document.addEventListener("mouseup", onMouseUp);
+ <>
+
+
+ {
- setting.current = "size";
+ setting.current = "position";
document.addEventListener("mousemove", onMouseMove);
document.addEventListener("mouseup", onMouseUp);
}}
- >
- )}
-
+ />
+
+
+
+ {showChat && (
+ {
+ setting.current = "size";
+ document.addEventListener("mousemove", onMouseMove);
+ document.addEventListener("mouseup", onMouseUp);
+ }}
+ >
+ )}
+
+ >
);
}
diff --git a/packages/react-chat/src/components/Chat.tsx b/packages/react-chat/src/components/Chat.tsx
index 3ee414ef..c5b9513d 100644
--- a/packages/react-chat/src/components/Chat.tsx
+++ b/packages/react-chat/src/components/Chat.tsx
@@ -49,6 +49,7 @@ export function Chat({
loadPrevDay,
loadingMessages,
community,
+ contacts,
} = useMessenger(activeChannel?.id ?? "", communityKey, identity);
const [isModalVisible, setIsModalVisible] = useState(false);
@@ -68,14 +69,7 @@ export function Chat({
description: community.description.identity?.description ?? "",
};
} else {
- return {
- id: 1,
- name: "",
- icon: "",
- members: 0,
- membersList: [],
- description: "",
- };
+ return undefined;
}
}, [community]);
@@ -101,7 +95,7 @@ export function Chat({
{showChannels && !narrow && (
- {messenger ? (
+ {community && communityData ? (
) : (
@@ -117,6 +111,8 @@ export function Chat({
)}
{showMembers && !narrow && (
)}
- setIsModalVisible(false)}
- icon={communityData.icon}
- name={communityData.name}
- subtitle="Public Community"
- description={communityData.description}
- publicKey={communityKey}
- />
+ {communityData && (
+ setIsModalVisible(false)}
+ icon={communityData.icon}
+ name={communityData.name}
+ subtitle="Public Community"
+ description={communityData.description}
+ publicKey={communityKey}
+ />
+ )}
);
}
diff --git a/packages/react-chat/src/components/Chat/ChatBody.tsx b/packages/react-chat/src/components/Chat/ChatBody.tsx
index 0979675b..b56d4888 100644
--- a/packages/react-chat/src/components/Chat/ChatBody.tsx
+++ b/packages/react-chat/src/components/Chat/ChatBody.tsx
@@ -1,10 +1,12 @@
import React, { useCallback, useEffect, useMemo, useState } from "react";
+import { Identity } from "status-communities/dist/cjs";
import styled from "styled-components";
import { useNarrow } from "../../contexts/narrowProvider";
import { ChannelData } from "../../models/ChannelData";
import { ChatMessage } from "../../models/ChatMessage";
import { CommunityData } from "../../models/CommunityData";
+import { Contact } from "../../models/Contact";
import { Metadata } from "../../models/Metadata";
import { Theme } from "../../styles/themes";
import { Channel } from "../Channels/Channel";
@@ -21,9 +23,11 @@ import { ChatInput } from "./ChatInput";
import { ChatMessages } from "./ChatMessages";
interface ChatBodyProps {
+ identity: Identity;
+ contacts: Contact[];
theme: Theme;
channel: ChannelData;
- community: CommunityData;
+ community: CommunityData | undefined;
messenger: any;
messages: ChatMessage[];
sendMessage: (text: string, image?: Uint8Array) => void;
@@ -44,6 +48,8 @@ interface ChatBodyProps {
}
export function ChatBody({
+ identity,
+ contacts,
theme,
channel,
community,
@@ -95,7 +101,7 @@ export function ChatBody({
}
>
- {messenger ? (
+ {messenger && community ? (
<>
{(showCommunity || narrow) && (
@@ -125,14 +131,14 @@ export function ChatBody({
>
- {!messenger && }
+ {!community && }
- {messenger ? (
+ {messenger && community ? (
<>
{!showChannelsList && !showMembersList && (
<>
{messages.length > 0 ? (
- messenger ? (
+ messenger && community ? (
(undefined);
const [showSizeLimit, setShowSizeLimit] = useState(false);
+
useEffect(() => {
window.addEventListener("click", () => setShowEmoji(false));
return () => {
@@ -127,18 +128,17 @@ export function ChatInput({ theme, addMessage }: ChatInputProps) {
/>
- {image && (
-
-
+
+ {image && (
setImageUint(undefined)} />
-
- )}
-
+ )}
+
+
{
@@ -160,6 +160,12 @@ export function ChatInput({ theme, addMessage }: ChatInputProps) {
);
}
+const InputWrapper = styled.div`
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+`;
+
const View = styled.div`
display: flex;
align-items: center;
@@ -187,28 +193,7 @@ const InputButtons = styled.div`
}
`;
-const ImagePreviewWrapper = styled.div`
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 82px;
- z-index: 1;
-`;
-
-const ImagePreviewOverlay = styled.div`
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- background: #eef2f5;
- border-radius: 16px 16px 4px 16px;
- opacity: 0.9;
-`;
-
const ImagePreview = styled.img`
- position: relative;
width: 64px;
height: 64px;
border-radius: 16px 16px 4px 16px;
diff --git a/packages/react-chat/src/components/Members/Members.tsx b/packages/react-chat/src/components/Members/Members.tsx
index fcdff0d2..bfd28c3b 100644
--- a/packages/react-chat/src/components/Members/Members.tsx
+++ b/packages/react-chat/src/components/Members/Members.tsx
@@ -1,18 +1,21 @@
import React from "react";
+import { Identity } from "status-communities/dist/cjs";
import styled from "styled-components";
-import { CommunityData } from "../../models/CommunityData";
+import { Contact } from "../../models/Contact";
import { MembersList } from "./MembersList";
interface MembersProps {
- community: CommunityData;
+ identity: Identity;
+ contacts: Contact[];
setShowChannels: (val: boolean) => void;
setMembersList: any;
}
export function Members({
- community,
+ identity,
+ contacts,
setShowChannels,
setMembersList,
}: MembersProps) {
@@ -20,7 +23,8 @@ export function Members({
Members
diff --git a/packages/react-chat/src/components/Members/MembersList.tsx b/packages/react-chat/src/components/Members/MembersList.tsx
index f9c4a43e..5888d92c 100644
--- a/packages/react-chat/src/components/Members/MembersList.tsx
+++ b/packages/react-chat/src/components/Members/MembersList.tsx
@@ -1,20 +1,24 @@
import React from "react";
+import { Identity, utils } from "status-communities/dist/cjs";
+import { bufToHex } from "status-communities/dist/cjs/utils";
import styled from "styled-components";
-import { CommunityData } from "../../models/CommunityData";
+import { Contact } from "../../models/Contact";
import { UserIcon } from "../Icons/UserIcon";
import { Member, MemberData, MemberIcon } from "./Member";
interface MembersListProps {
- community: CommunityData;
+ identity: Identity;
+ contacts: Contact[];
setShowChannels: (val: boolean) => void;
setShowMembers?: (val: boolean) => void;
setMembersList: any;
}
export function MembersList({
- community,
+ identity,
+ contacts,
setShowChannels,
setShowMembers,
setMembersList,
@@ -27,18 +31,19 @@ export function MembersList({
- Guest564732
+ {utils.bufToHex(identity.publicKey)}
Online
- {community.membersList
- .filter(() => false)
- .map((member) => (
+ {contacts
+ .filter((e) => e.id != bufToHex(identity.publicKey))
+ .filter((e) => e.online)
+ .map((contact) => (
Offline
- {community.membersList.map((member) => (
-
- ))}
+ {contacts
+ .filter((e) => e.id != bufToHex(identity.publicKey))
+ .filter((e) => !e.online)
+ .map((contact) => (
+
+ ))}
);
diff --git a/packages/react-chat/src/components/NarrowMode/NarrowMembers.tsx b/packages/react-chat/src/components/NarrowMode/NarrowMembers.tsx
index dbe07daa..4e494f31 100644
--- a/packages/react-chat/src/components/NarrowMode/NarrowMembers.tsx
+++ b/packages/react-chat/src/components/NarrowMode/NarrowMembers.tsx
@@ -1,20 +1,26 @@
import React from "react";
+import { Identity } from "status-communities/dist/cjs";
import styled from "styled-components";
import { CommunityData } from "../../models/CommunityData";
+import { Contact } from "../../models/Contact";
import { MembersList } from "../Members/MembersList";
import { NarrowTopbar } from "./NarrowTopbar";
interface NarrowMembersProps {
+ identity: Identity;
community: CommunityData;
+ contacts: Contact[];
setShowChannels: (val: boolean) => void;
setShowMembersList: (val: boolean) => void;
setMembersList: any;
}
export function NarrowMembers({
+ identity,
community,
+ contacts,
setShowChannels,
setShowMembersList,
setMembersList,
@@ -23,7 +29,8 @@ export function NarrowMembers({
(
{}
);
@@ -17,6 +20,9 @@ export function useMessages(chatId: string) {
(msg: ApplicationMetadataMessage, id: string, date: Date) => {
const newMessage = ChatMessage.fromMetadataMessage(msg, date);
if (newMessage) {
+ if (contacts) {
+ contacts.addContact(newMessage.sender);
+ }
setMessages((prev) => {
return {
...prev,
@@ -31,7 +37,7 @@ export function useMessages(chatId: string) {
incNotification(id);
}
},
- []
+ [contacts]
);
const activeMessages = useMemo(
diff --git a/packages/react-chat/src/hooks/messenger/useMessenger.ts b/packages/react-chat/src/hooks/messenger/useMessenger.ts
index 3458be74..09d3a2d5 100644
--- a/packages/react-chat/src/hooks/messenger/useMessenger.ts
+++ b/packages/react-chat/src/hooks/messenger/useMessenger.ts
@@ -1,8 +1,16 @@
// import { StoreCodec } from "js-waku";
-import { useCallback, useEffect, useState } from "react";
-import { Community, Identity, Messenger } from "status-communities/dist/cjs";
+import { useCallback, useEffect, useMemo, useState } from "react";
+import {
+ Community,
+ Contacts,
+ Identity,
+ Messenger,
+ utils,
+} from "status-communities/dist/cjs";
-import { createCommunityMessenger } from "../../utils/createCommunityMessenger";
+import { Contact } from "../../models/Contact";
+import { createCommunity } from "../../utils/createCommunity";
+import { createMessenger } from "../../utils/createMessenger";
import { useLoadPrevDay } from "./useLoadPrevDay";
import { useMessages } from "./useMessages";
@@ -13,27 +21,65 @@ export function useMessenger(
identity: Identity
) {
const [messenger, setMessenger] = useState(undefined);
+
+ const [internalContacts, setInternalContacts] = useState<{
+ [id: string]: number;
+ }>({});
+
+ const contactsClass = useMemo(() => {
+ if (messenger) {
+ const newContacts = new Contacts(
+ identity,
+ messenger.waku,
+ (id, clock) => {
+ setInternalContacts((prev) => {
+ return { ...prev, [id]: clock };
+ });
+ }
+ );
+ newContacts.addContact(utils.bufToHex(identity.publicKey));
+ return newContacts;
+ }
+ }, [messenger]);
+
+ const contacts = useMemo(() => {
+ const now = Date.now();
+ const newContacts: Contact[] = [];
+ Object.entries(internalContacts).forEach(([id, clock]) => {
+ newContacts.push({
+ id,
+ online: clock > now - 301000,
+ });
+ });
+ return newContacts;
+ }, [internalContacts]);
+
const { addMessage, clearNotifications, notifications, messages } =
- useMessages(chatId);
+ useMessages(chatId, contactsClass);
const [community, setCommunity] = useState(undefined);
const { loadPrevDay, loadingMessages } = useLoadPrevDay(chatId, messenger);
useEffect(() => {
- createCommunityMessenger(communityKey, addMessage, identity).then(
- (result) => {
- setCommunity(result.community);
- setMessenger(result.messenger);
- }
- );
+ createMessenger(identity).then((e) => {
+ setMessenger(e);
+ });
}, []);
+ useEffect(() => {
+ if (messenger && contactsClass) {
+ createCommunity(communityKey, addMessage, messenger).then((e) => {
+ setCommunity(e);
+ });
+ }
+ }, [messenger, communityKey, addMessage, contactsClass]);
+
useEffect(() => {
if (messenger && community?.chats) {
Array.from(community?.chats.values()).forEach(({ id }) =>
loadPrevDay(id)
);
}
- }, [messenger]);
+ }, [messenger, community]);
const sendMessage = useCallback(
async (messageText?: string, image?: Uint8Array) => {
@@ -63,5 +109,6 @@ export function useMessenger(
loadPrevDay,
loadingMessages,
community,
+ contacts,
};
}
diff --git a/packages/react-chat/src/hooks/useRefBreak.ts b/packages/react-chat/src/hooks/useRefBreak.ts
index 494a6481..23db5a51 100644
--- a/packages/react-chat/src/hooks/useRefBreak.ts
+++ b/packages/react-chat/src/hooks/useRefBreak.ts
@@ -1,7 +1,7 @@
import { useEffect, useState } from "react";
export function useRefBreak(dimension: number, sizeThreshold: number) {
- const [widthBreak, setWidthBreak] = useState(false);
+ const [widthBreak, setWidthBreak] = useState(dimension < sizeThreshold);
useEffect(() => {
const checkDimensions = () => {
diff --git a/packages/react-chat/src/models/ChatMessage.ts b/packages/react-chat/src/models/ChatMessage.ts
index 91e29fb0..e202812c 100644
--- a/packages/react-chat/src/models/ChatMessage.ts
+++ b/packages/react-chat/src/models/ChatMessage.ts
@@ -1,4 +1,4 @@
-import { ApplicationMetadataMessage } from "status-communities/dist/cjs";
+import { ApplicationMetadataMessage, utils } from "status-communities/dist/cjs";
import { uintToImgUrl } from "../utils";
@@ -29,10 +29,7 @@ export class ChatMessage {
if (msg.chatMessage?.image) {
image = uintToImgUrl(msg.chatMessage?.image.payload);
}
- const sender = msg.signer.reduce(
- (p: string, c: number): string => p + c.toString(16),
- "0x"
- );
+ const sender = utils.bufToHex(msg.signer);
return new ChatMessage(content, date, sender, image);
} else {
return undefined;
diff --git a/packages/react-chat/src/models/Contact.ts b/packages/react-chat/src/models/Contact.ts
new file mode 100644
index 00000000..0677d8d0
--- /dev/null
+++ b/packages/react-chat/src/models/Contact.ts
@@ -0,0 +1,4 @@
+export type Contact = {
+ id: string;
+ online: boolean;
+};
diff --git a/packages/react-chat/src/utils/createCommunity.ts b/packages/react-chat/src/utils/createCommunity.ts
new file mode 100644
index 00000000..7d7e20b2
--- /dev/null
+++ b/packages/react-chat/src/utils/createCommunity.ts
@@ -0,0 +1,23 @@
+import { Community, Messenger } from "status-communities/dist/cjs";
+import { ApplicationMetadataMessage } from "status-communities/dist/cjs";
+
+export async function createCommunity(
+ communityKey: string,
+ addMessage: (msg: ApplicationMetadataMessage, id: string, date: Date) => void,
+ messenger: Messenger
+) {
+ const community = await Community.instantiateCommunity(
+ communityKey,
+ messenger.waku
+ );
+ await Promise.all(
+ Array.from(community.chats.values()).map(async (chat) => {
+ await messenger.joinChat(chat);
+ messenger.addObserver(
+ (msg, date) => addMessage(msg, chat.id, date),
+ chat.id
+ );
+ })
+ );
+ return community;
+}
diff --git a/packages/react-chat/src/utils/createCommunityMessenger.ts b/packages/react-chat/src/utils/createCommunityMessenger.ts
deleted file mode 100644
index bda50eef..00000000
--- a/packages/react-chat/src/utils/createCommunityMessenger.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-import { StoreCodec } from "js-waku";
-import { Community, Identity, Messenger } from "status-communities/dist/cjs";
-import { ApplicationMetadataMessage } from "status-communities/dist/cjs";
-
-const WAKU_OPTIONS = {
- libp2p: {
- config: {
- pubsub: {
- enabled: true,
- emitSelf: true,
- },
- },
- },
-};
-
-export async function createCommunityMessenger(
- communityKey: string,
- addMessage: (msg: ApplicationMetadataMessage, id: string, date: Date) => void,
- identity: Identity
-) {
- const messenger = await Messenger.create(identity, WAKU_OPTIONS);
- await new Promise((resolve) => {
- messenger.waku.libp2p.peerStore.on("change:protocols", ({ protocols }) => {
- if (protocols.includes(StoreCodec)) {
- resolve("");
- }
- });
- });
- const community = await Community.instantiateCommunity(
- communityKey,
- messenger.waku
- );
- await Promise.all(
- Array.from(community.chats.values()).map(async (chat) => {
- await messenger.joinChat(chat);
- messenger.addObserver(
- (msg, date) => addMessage(msg, chat.id, date),
- chat.id
- );
- })
- );
-
- return { messenger, community, identity };
-}
diff --git a/packages/react-chat/src/utils/createMessenger.ts b/packages/react-chat/src/utils/createMessenger.ts
new file mode 100644
index 00000000..f9fba769
--- /dev/null
+++ b/packages/react-chat/src/utils/createMessenger.ts
@@ -0,0 +1,26 @@
+import { StoreCodec } from "js-waku";
+import { Identity, Messenger } from "status-communities/dist/cjs";
+
+const WAKU_OPTIONS = {
+ libp2p: {
+ config: {
+ pubsub: {
+ enabled: true,
+ emitSelf: true,
+ },
+ },
+ },
+};
+
+export async function createMessenger(identity: Identity) {
+ const messenger = await Messenger.create(identity, WAKU_OPTIONS);
+ await new Promise((resolve) => {
+ messenger.waku.libp2p.peerStore.on("change:protocols", ({ protocols }) => {
+ if (protocols.includes(StoreCodec)) {
+ resolve("");
+ }
+ });
+ });
+
+ return messenger;
+}
diff --git a/packages/status-communities/proto/communities/v1/status_update.proto b/packages/status-communities/proto/communities/v1/status_update.proto
new file mode 100644
index 00000000..759b390e
--- /dev/null
+++ b/packages/status-communities/proto/communities/v1/status_update.proto
@@ -0,0 +1,32 @@
+syntax = "proto3";
+
+package communities.v1;
+
+/* Specs:
+:AUTOMATIC
+ To Send - "AUTOMATIC" status ping every 5 minutes
+ Display - Online for up to 5 minutes from the last clock, after that Offline
+:ALWAYS_ONLINE
+ To Send - "ALWAYS_ONLINE" status ping every 5 minutes
+ Display - Online for up to 2 weeks from the last clock, after that Offline
+:INACTIVE
+ To Send - A single "INACTIVE" status ping
+ Display - Offline forever
+Note: Only send pings if the user interacted with the app in the last x minutes. */
+message StatusUpdate {
+
+ uint64 clock = 1;
+
+ StatusType status_type = 2;
+
+ string custom_text = 3;
+
+ enum StatusType {
+ UNKNOWN_STATUS_TYPE = 0;
+ AUTOMATIC = 1;
+ DO_NOT_DISTURB = 2;
+ ALWAYS_ONLINE = 3;
+ INACTIVE = 4;
+ };
+
+ }
\ No newline at end of file
diff --git a/packages/status-communities/src/contacts.ts b/packages/status-communities/src/contacts.ts
new file mode 100644
index 00000000..7e7e5536
--- /dev/null
+++ b/packages/status-communities/src/contacts.ts
@@ -0,0 +1,63 @@
+import { Waku, WakuMessage } from "js-waku";
+
+import { idToContactCodeTopic } from "./contentTopic";
+import { Identity } from "./identity";
+import { StatusUpdate_StatusType } from "./proto/communities/v1/status_update";
+import { bufToHex } from "./utils";
+import { StatusUpdate } from "./wire/status_update";
+
+export class Contacts {
+ waku: Waku;
+ identity: Identity;
+ private callback: (id: string, clock: number) => void;
+ private contacts: string[] = [];
+
+ public constructor(
+ identity: Identity,
+ waku: Waku,
+ callback: (id: string, clock: number) => void
+ ) {
+ this.waku = waku;
+ this.identity = identity;
+ this.callback = callback;
+ this.startBroadcast();
+ }
+
+ public addContact(id: string): void {
+ if (!this.contacts.find((e) => id === e)) {
+ const now = new Date();
+ const callback = (wakuMessage: WakuMessage): void => {
+ if (wakuMessage.payload) {
+ const msg = StatusUpdate.decode(wakuMessage.payload);
+ this.callback(id, msg.clock ?? 0);
+ }
+ };
+ this.contacts.push(id);
+ this.callback(id, 0);
+ this.waku.store.queryHistory([idToContactCodeTopic(id)], {
+ callback: (msgs) => msgs.forEach((e) => callback(e)),
+ timeFilter: {
+ startTime: new Date(now.getTime() - 400000),
+ endTime: now,
+ },
+ });
+ this.waku.relay.addObserver(callback, [idToContactCodeTopic(id)]);
+ }
+ }
+
+ private startBroadcast(): void {
+ const send = async (): Promise => {
+ const statusUpdate = StatusUpdate.create(
+ StatusUpdate_StatusType.AUTOMATIC,
+ ""
+ );
+ const msg = await WakuMessage.fromBytes(
+ statusUpdate.encode(),
+ idToContactCodeTopic(bufToHex(this.identity.publicKey))
+ );
+ this.waku.relay.send(msg);
+ };
+ send();
+ setInterval(send, 300000);
+ }
+}
diff --git a/packages/status-communities/src/contentTopic.ts b/packages/status-communities/src/contentTopic.ts
index 0c78777f..48c1dc22 100644
--- a/packages/status-communities/src/contentTopic.ts
+++ b/packages/status-communities/src/contentTopic.ts
@@ -16,3 +16,7 @@ export function idToContentTopic(id: string): string {
return "/waku/1/" + "0x" + topic.toString("hex") + "/rfc26";
}
+
+export function idToContactCodeTopic(id: string): string {
+ return idToContentTopic(id + "-contact-code");
+}
diff --git a/packages/status-communities/src/index.ts b/packages/status-communities/src/index.ts
index 4a3027b8..c12bddab 100644
--- a/packages/status-communities/src/index.ts
+++ b/packages/status-communities/src/index.ts
@@ -1,6 +1,7 @@
export { Identity } from "./identity";
export { Messenger } from "./messenger";
export { Community } from "./community";
+export { Contacts } from "./contacts";
export { Chat } from "./chat";
export * as utils from "./utils";
export { ApplicationMetadataMessage } from "./wire/application_metadata_message";
diff --git a/packages/status-communities/src/proto/communities/v1/status_update.ts b/packages/status-communities/src/proto/communities/v1/status_update.ts
new file mode 100644
index 00000000..31e9f122
--- /dev/null
+++ b/packages/status-communities/src/proto/communities/v1/status_update.ts
@@ -0,0 +1,212 @@
+/* eslint-disable */
+import Long from "long";
+import _m0 from "protobufjs/minimal";
+
+export const protobufPackage = "communities.v1";
+
+/**
+ * Specs:
+ * :AUTOMATIC
+ * To Send - "AUTOMATIC" status ping every 5 minutes
+ * Display - Online for up to 5 minutes from the last clock, after that Offline
+ * :ALWAYS_ONLINE
+ * To Send - "ALWAYS_ONLINE" status ping every 5 minutes
+ * Display - Online for up to 2 weeks from the last clock, after that Offline
+ * :INACTIVE
+ * To Send - A single "INACTIVE" status ping
+ * Display - Offline forever
+ * Note: Only send pings if the user interacted with the app in the last x minutes.
+ */
+export interface StatusUpdate {
+ clock: number;
+ statusType: StatusUpdate_StatusType;
+ customText: string;
+}
+
+export enum StatusUpdate_StatusType {
+ UNKNOWN_STATUS_TYPE = 0,
+ AUTOMATIC = 1,
+ DO_NOT_DISTURB = 2,
+ ALWAYS_ONLINE = 3,
+ INACTIVE = 4,
+ UNRECOGNIZED = -1,
+}
+
+export function statusUpdate_StatusTypeFromJSON(
+ object: any
+): StatusUpdate_StatusType {
+ switch (object) {
+ case 0:
+ case "UNKNOWN_STATUS_TYPE":
+ return StatusUpdate_StatusType.UNKNOWN_STATUS_TYPE;
+ case 1:
+ case "AUTOMATIC":
+ return StatusUpdate_StatusType.AUTOMATIC;
+ case 2:
+ case "DO_NOT_DISTURB":
+ return StatusUpdate_StatusType.DO_NOT_DISTURB;
+ case 3:
+ case "ALWAYS_ONLINE":
+ return StatusUpdate_StatusType.ALWAYS_ONLINE;
+ case 4:
+ case "INACTIVE":
+ return StatusUpdate_StatusType.INACTIVE;
+ case -1:
+ case "UNRECOGNIZED":
+ default:
+ return StatusUpdate_StatusType.UNRECOGNIZED;
+ }
+}
+
+export function statusUpdate_StatusTypeToJSON(
+ object: StatusUpdate_StatusType
+): string {
+ switch (object) {
+ case StatusUpdate_StatusType.UNKNOWN_STATUS_TYPE:
+ return "UNKNOWN_STATUS_TYPE";
+ case StatusUpdate_StatusType.AUTOMATIC:
+ return "AUTOMATIC";
+ case StatusUpdate_StatusType.DO_NOT_DISTURB:
+ return "DO_NOT_DISTURB";
+ case StatusUpdate_StatusType.ALWAYS_ONLINE:
+ return "ALWAYS_ONLINE";
+ case StatusUpdate_StatusType.INACTIVE:
+ return "INACTIVE";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+const baseStatusUpdate: object = { clock: 0, statusType: 0, customText: "" };
+
+export const StatusUpdate = {
+ encode(
+ message: StatusUpdate,
+ writer: _m0.Writer = _m0.Writer.create()
+ ): _m0.Writer {
+ if (message.clock !== 0) {
+ writer.uint32(8).uint64(message.clock);
+ }
+ if (message.statusType !== 0) {
+ writer.uint32(16).int32(message.statusType);
+ }
+ if (message.customText !== "") {
+ writer.uint32(26).string(message.customText);
+ }
+ return writer;
+ },
+
+ decode(input: _m0.Reader | Uint8Array, length?: number): StatusUpdate {
+ const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input);
+ let end = length === undefined ? reader.len : reader.pos + length;
+ const message = { ...baseStatusUpdate } as StatusUpdate;
+ while (reader.pos < end) {
+ const tag = reader.uint32();
+ switch (tag >>> 3) {
+ case 1:
+ message.clock = longToNumber(reader.uint64() as Long);
+ break;
+ case 2:
+ message.statusType = reader.int32() as any;
+ break;
+ case 3:
+ message.customText = reader.string();
+ break;
+ default:
+ reader.skipType(tag & 7);
+ break;
+ }
+ }
+ return message;
+ },
+
+ fromJSON(object: any): StatusUpdate {
+ const message = { ...baseStatusUpdate } as StatusUpdate;
+ if (object.clock !== undefined && object.clock !== null) {
+ message.clock = Number(object.clock);
+ } else {
+ message.clock = 0;
+ }
+ if (object.statusType !== undefined && object.statusType !== null) {
+ message.statusType = statusUpdate_StatusTypeFromJSON(object.statusType);
+ } else {
+ message.statusType = 0;
+ }
+ if (object.customText !== undefined && object.customText !== null) {
+ message.customText = String(object.customText);
+ } else {
+ message.customText = "";
+ }
+ return message;
+ },
+
+ toJSON(message: StatusUpdate): unknown {
+ const obj: any = {};
+ message.clock !== undefined && (obj.clock = message.clock);
+ message.statusType !== undefined &&
+ (obj.statusType = statusUpdate_StatusTypeToJSON(message.statusType));
+ message.customText !== undefined && (obj.customText = message.customText);
+ return obj;
+ },
+
+ fromPartial(object: DeepPartial): StatusUpdate {
+ const message = { ...baseStatusUpdate } as StatusUpdate;
+ if (object.clock !== undefined && object.clock !== null) {
+ message.clock = object.clock;
+ } else {
+ message.clock = 0;
+ }
+ if (object.statusType !== undefined && object.statusType !== null) {
+ message.statusType = object.statusType;
+ } else {
+ message.statusType = 0;
+ }
+ if (object.customText !== undefined && object.customText !== null) {
+ message.customText = object.customText;
+ } else {
+ message.customText = "";
+ }
+ return message;
+ },
+};
+
+declare var self: any | undefined;
+declare var window: any | undefined;
+declare var global: any | undefined;
+var globalThis: any = (() => {
+ if (typeof globalThis !== "undefined") return globalThis;
+ if (typeof self !== "undefined") return self;
+ if (typeof window !== "undefined") return window;
+ if (typeof global !== "undefined") return global;
+ throw "Unable to locate global object";
+})();
+
+type Builtin =
+ | Date
+ | Function
+ | Uint8Array
+ | string
+ | number
+ | boolean
+ | undefined;
+export type DeepPartial = T extends Builtin
+ ? T
+ : T extends Array
+ ? Array>
+ : T extends ReadonlyArray
+ ? ReadonlyArray>
+ : T extends {}
+ ? { [K in keyof T]?: DeepPartial }
+ : Partial;
+
+function longToNumber(long: Long): number {
+ if (long.gt(Number.MAX_SAFE_INTEGER)) {
+ throw new globalThis.Error("Value is larger than Number.MAX_SAFE_INTEGER");
+ }
+ return long.toNumber();
+}
+
+if (_m0.util.Long !== Long) {
+ _m0.util.Long = Long as any;
+ _m0.configure();
+}
diff --git a/packages/status-communities/src/wire/status_update.ts b/packages/status-communities/src/wire/status_update.ts
new file mode 100644
index 00000000..25d5e357
--- /dev/null
+++ b/packages/status-communities/src/wire/status_update.ts
@@ -0,0 +1,49 @@
+import { Reader } from "protobufjs";
+
+import * as proto from "../proto/communities/v1/status_update";
+
+export class StatusUpdate {
+ public constructor(public proto: proto.StatusUpdate) {}
+
+ public static create(
+ statusType: proto.StatusUpdate_StatusType,
+ customText: string
+ ): StatusUpdate {
+ const clock = Date.now();
+
+ const proto = {
+ clock,
+ statusType,
+ customText,
+ };
+
+ return new StatusUpdate(proto);
+ }
+
+ /**
+ * Decode the payload as CommunityChat message.
+ *
+ * @throws
+ */
+ static decode(bytes: Uint8Array): StatusUpdate {
+ const protoBuf = proto.StatusUpdate.decode(Reader.create(bytes));
+
+ return new StatusUpdate(protoBuf);
+ }
+
+ encode(): Uint8Array {
+ return proto.StatusUpdate.encode(this.proto).finish();
+ }
+
+ public get clock(): number | undefined {
+ return this.proto.clock;
+ }
+
+ public get statusType(): proto.StatusUpdate_StatusType | undefined {
+ return this.proto.statusType;
+ }
+
+ public get customText(): string | undefined {
+ return this.proto.customText;
+ }
+}