Refactor useMessenger (#89)
This commit is contained in:
parent
255d1d30f9
commit
819b89562c
|
@ -2,7 +2,7 @@ import React, { useEffect, useMemo, useState } from "react";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
|
|
||||||
import { useNarrow } from "../contexts/narrowProvider";
|
import { useNarrow } from "../contexts/narrowProvider";
|
||||||
import { useMessenger } from "../hooks/useMessenger";
|
import { useMessenger } from "../hooks/messenger/useMessenger";
|
||||||
import { ChannelData } from "../models/ChannelData";
|
import { ChannelData } from "../models/ChannelData";
|
||||||
import { Metadata } from "../models/Metadata";
|
import { Metadata } from "../models/Metadata";
|
||||||
import { Theme } from "../styles/themes";
|
import { Theme } from "../styles/themes";
|
||||||
|
@ -37,7 +37,7 @@ export function Chat({ theme, communityKey, fetchMetadata }: ChatProps) {
|
||||||
sendMessage,
|
sendMessage,
|
||||||
notifications,
|
notifications,
|
||||||
clearNotifications,
|
clearNotifications,
|
||||||
loadNextDay,
|
loadPrevDay,
|
||||||
loadingMessages,
|
loadingMessages,
|
||||||
community,
|
community,
|
||||||
} = useMessenger(activeChannel?.id ?? "", communityKey);
|
} = useMessenger(activeChannel?.id ?? "", communityKey);
|
||||||
|
@ -120,7 +120,7 @@ export function Chat({ theme, communityKey, fetchMetadata }: ChatProps) {
|
||||||
showMembers={showMembers}
|
showMembers={showMembers}
|
||||||
community={communityData}
|
community={communityData}
|
||||||
showCommunity={!showChannels}
|
showCommunity={!showChannels}
|
||||||
loadNextDay={() => loadNextDay(activeChannel.name)}
|
loadPrevDay={() => loadPrevDay(activeChannel.name)}
|
||||||
onCommunityClick={showModal}
|
onCommunityClick={showModal}
|
||||||
fetchMetadata={fetchMetadata}
|
fetchMetadata={fetchMetadata}
|
||||||
loadingMessages={loadingMessages}
|
loadingMessages={loadingMessages}
|
||||||
|
|
|
@ -33,7 +33,7 @@ interface ChatBodyProps {
|
||||||
notifications: { [id: string]: number };
|
notifications: { [id: string]: number };
|
||||||
setActiveChannel: (val: ChannelData) => void;
|
setActiveChannel: (val: ChannelData) => void;
|
||||||
activeChannelId: string;
|
activeChannelId: string;
|
||||||
loadNextDay: () => void;
|
loadPrevDay: () => void;
|
||||||
onCommunityClick: () => void;
|
onCommunityClick: () => void;
|
||||||
fetchMetadata?: (url: string) => Promise<Metadata | undefined>;
|
fetchMetadata?: (url: string) => Promise<Metadata | undefined>;
|
||||||
loadingMessages: boolean;
|
loadingMessages: boolean;
|
||||||
|
@ -54,7 +54,7 @@ export function ChatBody({
|
||||||
notifications,
|
notifications,
|
||||||
setActiveChannel,
|
setActiveChannel,
|
||||||
activeChannelId,
|
activeChannelId,
|
||||||
loadNextDay,
|
loadPrevDay,
|
||||||
onCommunityClick,
|
onCommunityClick,
|
||||||
fetchMetadata,
|
fetchMetadata,
|
||||||
loadingMessages,
|
loadingMessages,
|
||||||
|
@ -131,7 +131,7 @@ export function ChatBody({
|
||||||
messenger ? (
|
messenger ? (
|
||||||
<ChatMessages
|
<ChatMessages
|
||||||
messages={messages}
|
messages={messages}
|
||||||
loadNextDay={loadNextDay}
|
loadPrevDay={loadPrevDay}
|
||||||
fetchMetadata={fetchMetadata}
|
fetchMetadata={fetchMetadata}
|
||||||
loadingMessages={loadingMessages}
|
loadingMessages={loadingMessages}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -13,14 +13,14 @@ import { ChatMessageContent } from "./ChatMessageContent";
|
||||||
|
|
||||||
type ChatMessagesProps = {
|
type ChatMessagesProps = {
|
||||||
messages: ChatMessage[];
|
messages: ChatMessage[];
|
||||||
loadNextDay: () => void;
|
loadPrevDay: () => void;
|
||||||
fetchMetadata?: (url: string) => Promise<Metadata | undefined>;
|
fetchMetadata?: (url: string) => Promise<Metadata | undefined>;
|
||||||
loadingMessages: boolean;
|
loadingMessages: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function ChatMessages({
|
export function ChatMessages({
|
||||||
messages,
|
messages,
|
||||||
loadNextDay,
|
loadPrevDay,
|
||||||
fetchMetadata,
|
fetchMetadata,
|
||||||
loadingMessages,
|
loadingMessages,
|
||||||
}: ChatMessagesProps) {
|
}: ChatMessagesProps) {
|
||||||
|
@ -38,7 +38,7 @@ export function ChatMessages({
|
||||||
if (
|
if (
|
||||||
(ref?.current?.clientHeight ?? 0) >= (ref?.current?.scrollHeight ?? 0)
|
(ref?.current?.clientHeight ?? 0) >= (ref?.current?.scrollHeight ?? 0)
|
||||||
) {
|
) {
|
||||||
loadNextDay();
|
loadPrevDay();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [messages, messages.length, loadingMessages]);
|
}, [messages, messages.length, loadingMessages]);
|
||||||
|
@ -47,7 +47,7 @@ export function ChatMessages({
|
||||||
const setScroll = () => {
|
const setScroll = () => {
|
||||||
if (ref && ref.current) {
|
if (ref && ref.current) {
|
||||||
if (ref.current.scrollTop <= 0) {
|
if (ref.current.scrollTop <= 0) {
|
||||||
loadNextDay();
|
loadPrevDay();
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
ref.current.scrollTop + ref.current.clientHeight ==
|
ref.current.scrollTop + ref.current.clientHeight ==
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
import { useCallback, useEffect, useRef, useState } from "react";
|
||||||
|
import { Messenger } from "status-communities/dist/cjs";
|
||||||
|
|
||||||
|
const _MS_PER_DAY = 1000 * 60 * 60 * 24;
|
||||||
|
|
||||||
|
export function useLoadPrevDay(
|
||||||
|
chatId: string,
|
||||||
|
messenger: Messenger | undefined
|
||||||
|
) {
|
||||||
|
const loadingPreviousMessages = useRef<{
|
||||||
|
[chatId: string]: boolean;
|
||||||
|
}>({});
|
||||||
|
const lastLoadTime = useRef<{
|
||||||
|
[chatId: string]: Date;
|
||||||
|
}>({});
|
||||||
|
const [loadingMessages, setLoadingMessages] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setLoadingMessages(loadingPreviousMessages.current[chatId]);
|
||||||
|
}, [chatId]);
|
||||||
|
|
||||||
|
const loadPrevDay = useCallback(
|
||||||
|
async (id: string) => {
|
||||||
|
if (messenger) {
|
||||||
|
const endTime = lastLoadTime.current[id] ?? new Date();
|
||||||
|
const startTime = new Date(endTime.getTime() - _MS_PER_DAY);
|
||||||
|
const timeDiff = Math.floor(
|
||||||
|
(new Date().getTime() - endTime.getTime()) / _MS_PER_DAY
|
||||||
|
);
|
||||||
|
if (timeDiff < 30) {
|
||||||
|
if (!loadingPreviousMessages.current[id]) {
|
||||||
|
loadingPreviousMessages.current[id] = true;
|
||||||
|
setLoadingMessages(true);
|
||||||
|
const amountOfMessages = await messenger.retrievePreviousMessages(
|
||||||
|
id,
|
||||||
|
startTime,
|
||||||
|
endTime
|
||||||
|
);
|
||||||
|
lastLoadTime.current[id] = startTime;
|
||||||
|
loadingPreviousMessages.current[id] = false;
|
||||||
|
setLoadingMessages(false);
|
||||||
|
if (amountOfMessages === 0) {
|
||||||
|
loadPrevDay(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[lastLoadTime, messenger]
|
||||||
|
);
|
||||||
|
return { loadingMessages, loadPrevDay };
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
import { useCallback, useMemo, useState } from "react";
|
||||||
|
import { ApplicationMetadataMessage } from "status-communities/dist/cjs";
|
||||||
|
|
||||||
|
import { ChatMessage } from "../../models/ChatMessage";
|
||||||
|
import { binarySetInsert } from "../../utils";
|
||||||
|
|
||||||
|
import { useNotifications } from "./useNotifications";
|
||||||
|
|
||||||
|
export function useMessages(chatId: string) {
|
||||||
|
const [messages, setMessages] = useState<{ [chatId: string]: ChatMessage[] }>(
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
const { notifications, incNotification, clearNotifications } =
|
||||||
|
useNotifications();
|
||||||
|
|
||||||
|
const addMessage = useCallback(
|
||||||
|
(msg: ApplicationMetadataMessage, id: string, date: Date) => {
|
||||||
|
const newMessage = ChatMessage.fromMetadataMessage(msg, date);
|
||||||
|
if (newMessage) {
|
||||||
|
setMessages((prev) => {
|
||||||
|
return {
|
||||||
|
...prev,
|
||||||
|
[id]: binarySetInsert(
|
||||||
|
prev?.[id] ?? [],
|
||||||
|
newMessage,
|
||||||
|
(a, b) => a.date < b.date,
|
||||||
|
(a, b) => a.date.getTime() === b.date.getTime()
|
||||||
|
),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
incNotification(id);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
const activeMessages = useMemo(
|
||||||
|
() => messages?.[chatId] ?? [],
|
||||||
|
[messages, chatId]
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
messages: activeMessages,
|
||||||
|
addMessage,
|
||||||
|
notifications,
|
||||||
|
clearNotifications,
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,105 @@
|
||||||
|
// import { StoreCodec } from "js-waku";
|
||||||
|
import { StoreCodec } from "js-waku";
|
||||||
|
import { useCallback, useEffect, useState } from "react";
|
||||||
|
import { Community, Identity, Messenger } from "status-communities/dist/cjs";
|
||||||
|
|
||||||
|
import { loadIdentity, saveIdentity } from "../../utils";
|
||||||
|
|
||||||
|
import { useLoadPrevDay } from "./useLoadPrevDay";
|
||||||
|
import { useMessages } from "./useMessages";
|
||||||
|
|
||||||
|
export function useMessenger(chatId: string, communityKey: string) {
|
||||||
|
const [messenger, setMessenger] = useState<Messenger | undefined>(undefined);
|
||||||
|
const { addMessage, clearNotifications, notifications, messages } =
|
||||||
|
useMessages(chatId);
|
||||||
|
const [community, setCommunity] = useState<Community | undefined>(undefined);
|
||||||
|
const { loadPrevDay, loadingMessages } = useLoadPrevDay(chatId, messenger);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const createMessenger = async () => {
|
||||||
|
// Test password for now
|
||||||
|
// Need design for password input
|
||||||
|
|
||||||
|
let identity = await loadIdentity("test");
|
||||||
|
if (!identity) {
|
||||||
|
identity = Identity.generate();
|
||||||
|
await saveIdentity(identity, "test");
|
||||||
|
}
|
||||||
|
const messenger = await Messenger.create(identity, {
|
||||||
|
libp2p: {
|
||||||
|
config: {
|
||||||
|
pubsub: {
|
||||||
|
enabled: true,
|
||||||
|
emitSelf: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
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
|
||||||
|
);
|
||||||
|
setCommunity(community);
|
||||||
|
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
|
||||||
|
);
|
||||||
|
clearNotifications(chat.id);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
setMessenger(messenger);
|
||||||
|
};
|
||||||
|
createMessenger();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (messenger && community?.chats) {
|
||||||
|
Array.from(community?.chats.values()).forEach(({ id }) =>
|
||||||
|
loadPrevDay(id)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}, [messenger]);
|
||||||
|
|
||||||
|
const sendMessage = useCallback(
|
||||||
|
async (messageText?: string, image?: Uint8Array) => {
|
||||||
|
if (messageText) {
|
||||||
|
await messenger?.sendMessage(chatId, {
|
||||||
|
text: messageText,
|
||||||
|
contentType: 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (image) {
|
||||||
|
await messenger?.sendMessage(chatId, {
|
||||||
|
image,
|
||||||
|
imageType: 1,
|
||||||
|
contentType: 2,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[chatId, messenger]
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
messenger,
|
||||||
|
messages,
|
||||||
|
sendMessage,
|
||||||
|
notifications,
|
||||||
|
clearNotifications,
|
||||||
|
loadPrevDay,
|
||||||
|
loadingMessages,
|
||||||
|
community,
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
|
|
||||||
|
export function useNotifications() {
|
||||||
|
const [notifications, setNotifications] = useState<{
|
||||||
|
[chatId: string]: number;
|
||||||
|
}>({});
|
||||||
|
const incNotification = useCallback((id: string) => {
|
||||||
|
setNotifications((prevNotifications) => {
|
||||||
|
return {
|
||||||
|
...prevNotifications,
|
||||||
|
[id]: prevNotifications[id] + 1,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
const clearNotifications = useCallback((id: string) => {
|
||||||
|
setNotifications((prevNotifications) => {
|
||||||
|
return {
|
||||||
|
...prevNotifications,
|
||||||
|
[id]: 0,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
return { notifications, incNotification, clearNotifications };
|
||||||
|
}
|
|
@ -1,237 +0,0 @@
|
||||||
// import { StoreCodec } from "js-waku";
|
|
||||||
import { StoreCodec } from "js-waku";
|
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
||||||
import { Community, Identity, Messenger } from "status-communities/dist/cjs";
|
|
||||||
import { ApplicationMetadataMessage } from "status-communities/dist/cjs";
|
|
||||||
|
|
||||||
import { ChatMessage } from "../models/ChatMessage";
|
|
||||||
import { loadIdentity, saveIdentity } from "../utils/identityStorage";
|
|
||||||
import { uintToImgUrl } from "../utils/uintToImgUrl";
|
|
||||||
|
|
||||||
const _MS_PER_DAY = 1000 * 60 * 60 * 24;
|
|
||||||
|
|
||||||
function binarySetInsert<T>(
|
|
||||||
arr: T[],
|
|
||||||
val: T,
|
|
||||||
compFunc: (a: T, b: T) => boolean,
|
|
||||||
eqFunc: (a: T, b: T) => boolean
|
|
||||||
) {
|
|
||||||
let low = 0;
|
|
||||||
let high = arr.length;
|
|
||||||
while (low < high) {
|
|
||||||
const mid = (low + high) >> 1;
|
|
||||||
if (compFunc(arr[mid], val)) {
|
|
||||||
low = mid + 1;
|
|
||||||
} else {
|
|
||||||
high = mid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (arr.length === low || !eqFunc(arr[low], val)) {
|
|
||||||
arr.splice(low, 0, val);
|
|
||||||
}
|
|
||||||
return arr;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useMessenger(chatId: string, communityKey: string) {
|
|
||||||
const [messenger, setMessenger] = useState<Messenger | undefined>(undefined);
|
|
||||||
const [messages, setMessages] = useState<{ [chatId: string]: ChatMessage[] }>(
|
|
||||||
{}
|
|
||||||
);
|
|
||||||
const [community, setCommunity] = useState<Community | undefined>(undefined);
|
|
||||||
const [notifications, setNotifications] = useState<{
|
|
||||||
[chatId: string]: number;
|
|
||||||
}>({});
|
|
||||||
const loadingPreviousMessages = useRef<{
|
|
||||||
[chatId: string]: boolean;
|
|
||||||
}>({});
|
|
||||||
const lastLoadTime = useRef<{
|
|
||||||
[chatId: string]: Date;
|
|
||||||
}>({});
|
|
||||||
const [lastMessage, setLastMessage] = useState(new Date());
|
|
||||||
const [loadingMessages, setLoadingMessages] = useState(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (lastLoadTime.current?.[chatId]) {
|
|
||||||
setLastMessage(lastLoadTime.current?.[chatId]);
|
|
||||||
}
|
|
||||||
}, [chatId]);
|
|
||||||
const clearNotifications = useCallback((id: string) => {
|
|
||||||
setNotifications((prevNotifications) => {
|
|
||||||
return {
|
|
||||||
...prevNotifications,
|
|
||||||
[id]: 0,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const addNewMessage = useCallback(
|
|
||||||
(msg: ApplicationMetadataMessage, id: string, date: Date) => {
|
|
||||||
if (
|
|
||||||
msg.signer &&
|
|
||||||
(msg.chatMessage?.text || msg.chatMessage?.image) &&
|
|
||||||
msg.chatMessage.clock
|
|
||||||
) {
|
|
||||||
const content = msg.chatMessage.text ?? "";
|
|
||||||
let image: string | undefined = undefined;
|
|
||||||
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 newMessage = { sender, content, date, image };
|
|
||||||
setMessages((prev) => {
|
|
||||||
return {
|
|
||||||
...prev,
|
|
||||||
[id]: binarySetInsert(
|
|
||||||
prev?.[id] ?? [],
|
|
||||||
newMessage,
|
|
||||||
(a, b) => a.date < b.date,
|
|
||||||
(a, b) => a.date.getTime() === b.date.getTime()
|
|
||||||
),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
setNotifications((prev) => {
|
|
||||||
return {
|
|
||||||
...prev,
|
|
||||||
[id]: prev[id] + 1,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
|
|
||||||
const loadNextDay = useCallback(
|
|
||||||
async (id: string) => {
|
|
||||||
if (messenger) {
|
|
||||||
const endTime = lastLoadTime.current[id] ?? new Date();
|
|
||||||
const startTime = new Date(endTime.getTime() - _MS_PER_DAY);
|
|
||||||
const timeDiff = Math.floor(
|
|
||||||
(new Date().getTime() - endTime.getTime()) / _MS_PER_DAY
|
|
||||||
);
|
|
||||||
if (timeDiff < 30) {
|
|
||||||
if (!loadingPreviousMessages.current[id]) {
|
|
||||||
loadingPreviousMessages.current[id] = true;
|
|
||||||
setLoadingMessages(true);
|
|
||||||
const amountOfMessages = await messenger.retrievePreviousMessages(
|
|
||||||
id,
|
|
||||||
startTime,
|
|
||||||
endTime
|
|
||||||
);
|
|
||||||
lastLoadTime.current[id] = startTime;
|
|
||||||
if (id === chatId) {
|
|
||||||
setLastMessage(startTime);
|
|
||||||
}
|
|
||||||
loadingPreviousMessages.current[id] = false;
|
|
||||||
setLoadingMessages(false);
|
|
||||||
if (amountOfMessages === 0) {
|
|
||||||
loadNextDay(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[lastLoadTime, messenger, chatId]
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const createMessenger = async () => {
|
|
||||||
// Test password for now
|
|
||||||
// Need design for password input
|
|
||||||
|
|
||||||
let identity = await loadIdentity("test");
|
|
||||||
if (!identity) {
|
|
||||||
identity = Identity.generate();
|
|
||||||
await saveIdentity(identity, "test");
|
|
||||||
}
|
|
||||||
const messenger = await Messenger.create(identity, {
|
|
||||||
libp2p: {
|
|
||||||
config: {
|
|
||||||
pubsub: {
|
|
||||||
enabled: true,
|
|
||||||
emitSelf: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
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
|
|
||||||
);
|
|
||||||
setCommunity(community);
|
|
||||||
await Promise.all(
|
|
||||||
Array.from(community.chats.values()).map(async (chat) => {
|
|
||||||
await messenger.joinChat(chat);
|
|
||||||
lastLoadTime.current[chat.id] = new Date();
|
|
||||||
messenger.addObserver(
|
|
||||||
(msg, date) => addNewMessage(msg, chat.id, date),
|
|
||||||
chat.id
|
|
||||||
);
|
|
||||||
clearNotifications(chat.id);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
setMessenger(messenger);
|
|
||||||
};
|
|
||||||
createMessenger();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (messenger && community?.chats) {
|
|
||||||
Array.from(community?.chats.values()).forEach(({ id }) =>
|
|
||||||
loadNextDay(id)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}, [messenger]);
|
|
||||||
|
|
||||||
const sendMessage = useCallback(
|
|
||||||
async (messageText?: string, image?: Uint8Array) => {
|
|
||||||
if (messageText) {
|
|
||||||
await messenger?.sendMessage(chatId, {
|
|
||||||
text: messageText,
|
|
||||||
contentType: 0,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (image) {
|
|
||||||
await messenger?.sendMessage(chatId, {
|
|
||||||
image,
|
|
||||||
imageType: 1,
|
|
||||||
contentType: 2,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[chatId, messenger]
|
|
||||||
);
|
|
||||||
|
|
||||||
const activeMessages = useMemo(
|
|
||||||
() => messages?.[chatId] ?? [],
|
|
||||||
[messages, chatId]
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setLoadingMessages(loadingPreviousMessages.current[chatId]);
|
|
||||||
}, [chatId]);
|
|
||||||
|
|
||||||
return {
|
|
||||||
messenger,
|
|
||||||
messages: activeMessages,
|
|
||||||
sendMessage,
|
|
||||||
notifications,
|
|
||||||
clearNotifications,
|
|
||||||
loadNextDay,
|
|
||||||
lastMessage,
|
|
||||||
loadingMessages,
|
|
||||||
community,
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,6 +1,41 @@
|
||||||
export type ChatMessage = {
|
import { ApplicationMetadataMessage } from "status-communities/dist/cjs";
|
||||||
|
|
||||||
|
import { uintToImgUrl } from "../utils";
|
||||||
|
|
||||||
|
export class ChatMessage {
|
||||||
content: string;
|
content: string;
|
||||||
date: Date;
|
date: Date;
|
||||||
sender: string;
|
sender: string;
|
||||||
image?: string;
|
image?: string;
|
||||||
};
|
|
||||||
|
constructor(content: string, date: Date, sender: string, image?: string) {
|
||||||
|
this.content = content;
|
||||||
|
this.date = date;
|
||||||
|
this.sender = sender;
|
||||||
|
this.image = image;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static fromMetadataMessage(
|
||||||
|
msg: ApplicationMetadataMessage,
|
||||||
|
date: Date
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
msg.signer &&
|
||||||
|
(msg.chatMessage?.text || msg.chatMessage?.image) &&
|
||||||
|
msg.chatMessage.clock
|
||||||
|
) {
|
||||||
|
const content = msg.chatMessage.text ?? "";
|
||||||
|
let image: string | undefined = undefined;
|
||||||
|
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"
|
||||||
|
);
|
||||||
|
return new ChatMessage(content, date, sender, image);
|
||||||
|
} else {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
export function binarySetInsert<T>(
|
||||||
|
arr: T[],
|
||||||
|
val: T,
|
||||||
|
compFunc: (a: T, b: T) => boolean,
|
||||||
|
eqFunc: (a: T, b: T) => boolean
|
||||||
|
) {
|
||||||
|
let low = 0;
|
||||||
|
let high = arr.length;
|
||||||
|
while (low < high) {
|
||||||
|
const mid = (low + high) >> 1;
|
||||||
|
if (compFunc(arr[mid], val)) {
|
||||||
|
low = mid + 1;
|
||||||
|
} else {
|
||||||
|
high = mid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (arr.length === low || !eqFunc(arr[low], val)) {
|
||||||
|
arr.splice(low, 0, val);
|
||||||
|
}
|
||||||
|
return arr;
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
export { binarySetInsert } from "./binarySetInsert";
|
||||||
|
export { copy } from "./copy";
|
||||||
|
export { copyImg } from "./copyImg";
|
||||||
|
export { downloadImg } from "./downloadImg";
|
||||||
|
export { saveIdentity, loadIdentity } from "./identityStorage";
|
||||||
|
export { reduceString } from "./reduceString";
|
||||||
|
export { uintToImgUrl } from "./uintToImgUrl";
|
Loading…
Reference in New Issue