Fix hook problems (#216)

This commit is contained in:
Szymon Szlachtowicz 2022-02-03 08:57:05 +01:00 committed by GitHub
parent 030a33f95a
commit bf7c15bc29
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 354 additions and 252 deletions

View File

@ -30,7 +30,7 @@ export function ActivityCenter({
activities.filter(
(activity) => !contacts?.[activity.user]?.blocked ?? true
),
[contacts, activities, activities.length]
[contacts, activities]
);
const [hideRead, setHideRead] = useState(false);

View File

@ -3,9 +3,9 @@ import styled from "styled-components";
import { useMessengerContext } from "../../contexts/messengerProvider";
import { useModal } from "../../contexts/modalProvider";
import { useScrollToMessage } from "../../contexts/scrollProvider";
import { ActivityAction } from "../../hooks/useActivities";
import { useClickOutside } from "../../hooks/useClickOutside";
import { useScrollToMessage } from "../../hooks/useScrollToMessage";
import { Activity } from "../../models/Activity";
import { equalDate } from "../../utils/equalDate";
import { DownloadButton } from "../Buttons/DownloadButton";
@ -169,7 +169,10 @@ export function ActivityMessage({
<ActivityText>
{"message" in activity && activity.message?.content && (
<div
onClick={() => scroll(activity.message, activity.channel.id)}
onClick={() => {
scroll(activity.message, activity.channel.id);
setShowActivityCenter(false);
}}
>
{elements.map((el) => el)}
</div>

View File

@ -1,8 +1,7 @@
import { utils } from "@waku/status-communities/dist/cjs";
import React, { useMemo } from "react";
import styled from "styled-components";
import { useIdentity } from "../../contexts/identityProvider";
import { useUserPublicKey } from "../../contexts/identityProvider";
import { useMessengerContext } from "../../contexts/messengerProvider";
import { useNarrow } from "../../contexts/narrowProvider";
import { ChannelData } from "../../models/ChannelData";
@ -16,17 +15,16 @@ type ChannelBeggingTextProps = {
};
function ChannelBeggingText({ channel }: ChannelBeggingTextProps) {
const identity = useIdentity();
const userPK = useUserPublicKey();
const { contacts } = useMessengerContext();
const members = useMemo(() => {
if (channel?.members && identity) {
const publicKey = utils.bufToHex(identity.publicKey);
if (channel?.members && userPK) {
return channel.members
.filter((contact) => contact.id !== publicKey)
.filter((contact) => contact.id !== userPK)
.map((member) => contacts?.[member.id] ?? member);
}
return [];
}, [channel, contacts]);
}, [channel, contacts, userPK]);
switch (channel.type) {
case "dm":
@ -40,8 +38,7 @@ function ChannelBeggingText({ channel }: ChannelBeggingTextProps) {
case "group":
return (
<EmptyTextGroup>
{identity && <span>{utils.bufToHex(identity.publicKey)}</span>}{" "}
created a group with{" "}
{userPK && <span>{userPK}</span>} created a group with{" "}
{members.map((contact, idx) => (
<span key={contact.id}>
{contact?.customName ?? contact.trueName.slice(0, 10)}

View File

@ -116,19 +116,21 @@ export function ChatBody({
activeChannel={activeChannel}
/>
) : (
<ChatTopbar
onClick={onClick}
setEditGroup={setEditGroup}
showMembers={showMembers}
showState={showState}
switchShowState={switchShowState}
/>
<>
<ChatTopbar
onClick={onClick}
setEditGroup={setEditGroup}
showMembers={showMembers}
showState={showState}
switchShowState={switchShowState}
/>
<ChatBodyContent
showState={showState}
switchShowState={switchShowState}
channel={activeChannel}
/>
</>
)}
<ChatBodyContent
showState={showState}
switchShowState={switchShowState}
channel={activeChannel}
/>
</ChatBodyWrapper>
{!permission && (
<BluredWrapper>

View File

@ -1,9 +1,8 @@
import { bufToHex } from "@waku/status-communities/dist/cjs/utils";
import React, { useCallback, useState } from "react";
import React, { useCallback, useMemo, useState } from "react";
import styled from "styled-components";
import { ChatState, useChatState } from "../../contexts/chatStateProvider";
import { useIdentity } from "../../contexts/identityProvider";
import { useUserPublicKey } from "../../contexts/identityProvider";
import { useMessengerContext } from "../../contexts/messengerProvider";
import { useNarrow } from "../../contexts/narrowProvider";
import { ChannelData } from "../../models/ChannelData";
@ -27,19 +26,23 @@ export function ChatCreation({
activeChannel,
}: ChatCreationProps) {
const narrow = useNarrow();
const identity = useIdentity();
const userPK = useUserPublicKey();
const [query, setQuery] = useState("");
const [styledGroup, setStyledGroup] = useState<string[]>(
activeChannel?.members?.map(
(member) => member?.customName ?? member.trueName
) ?? []
const [groupChatMembersIds, setGroupChatMembersIds] = useState<string[]>(
activeChannel?.members?.map((member) => member.id) ?? []
);
const { contacts, createGroupChat, addMembers } = useMessengerContext();
const groupChatMembers = useMemo(
() => groupChatMembersIds.map((id) => contacts[id]).filter((e) => !!e),
[groupChatMembersIds, contacts]
);
const setChatState = useChatState()[1];
const addMember = useCallback(
(member: string) => {
setStyledGroup((prevMembers: string[]) => {
setGroupChatMembersIds((prevMembers: string[]) => {
if (
prevMembers.find((mem) => mem === member) ||
prevMembers.length >= 5
@ -50,33 +53,44 @@ export function ChatCreation({
}
});
setQuery("");
if (activeChannel) addMembers(styledGroup, activeChannel.id);
},
[setStyledGroup, styledGroup]
[setGroupChatMembersIds]
);
const removeMember = useCallback(
(member: string) => {
setStyledGroup((prev) => prev.filter((e) => e != member));
setGroupChatMembersIds((prev) => prev.filter((e) => e != member));
},
[setStyledGroup]
[setGroupChatMembersIds]
);
const createChat = useCallback(
(group: string[]) => {
if (identity) {
if (userPK) {
const newGroup = group.slice();
newGroup.push(bufToHex(identity.publicKey));
newGroup.push(userPK);
createGroupChat(newGroup);
setChatState(ChatState.ChatBody);
}
},
[identity, createGroupChat]
[userPK, createGroupChat, setChatState]
);
const handleCreationClick = useCallback(() => {
if (!activeChannel) {
createChat(groupChatMembers.map((member) => member.id));
} else {
addMembers(
groupChatMembers.map((member) => member.id),
activeChannel.id
);
}
setEditGroup?.(false);
}, [activeChannel, groupChatMembers, createChat, addMembers, setEditGroup]);
return (
<CreationWrapper className={`${narrow && "narrow"}`}>
<CreationBar
className={`${styledGroup.length === 5 && narrow && "limit"}`}
className={`${groupChatMembers.length === 5 && narrow && "limit"}`}
>
{narrow && (
<BackButton
@ -88,16 +102,19 @@ export function ChatCreation({
<InputBar>
<InputText>To:</InputText>
<StyledList>
{styledGroup.map((member) => (
<StyledMember key={member}>
<StyledName>{member.slice(0, 10)}</StyledName>
<CloseButton onClick={() => removeMember(member)}>
{groupChatMembers.map((member) => (
<StyledMember key={member.id}>
<StyledName>
{member?.customName?.slice(0, 10) ??
member.trueName.slice(0, 10)}
</StyledName>
<CloseButton onClick={() => removeMember(member.id)}>
<CrossIcon memberView={true} />
</CloseButton>
</StyledMember>
))}
</StyledList>
{styledGroup.length < 5 && (
{groupChatMembers.length < 5 && (
<SearchMembers>
<Input
value={query}
@ -105,31 +122,24 @@ export function ChatCreation({
/>
</SearchMembers>
)}
{!narrow && styledGroup.length === 5 && (
{!narrow && groupChatMembers.length === 5 && (
<LimitAlert>5 user Limit reached</LimitAlert>
)}
</InputBar>
{narrow && styledGroup.length === 5 && (
{narrow && groupChatMembers.length === 5 && (
<LimitAlert className="narrow">5 user Limit reached</LimitAlert>
)}
</Column>
<CreationBtn
disabled={styledGroup.length === 0}
onClick={() => {
if (!activeChannel) {
createChat(styledGroup);
} else {
addMembers(styledGroup, activeChannel.id);
}
setEditGroup?.(false);
}}
disabled={groupChatMembers.length === 0}
onClick={handleCreationClick}
>
Confirm
</CreationBtn>
{!narrow && <ActivityButton className="creation" />}
<SearchBlock
query={query}
discludeList={styledGroup}
discludeList={groupChatMembersIds}
onClick={addMember}
/>
</CreationBar>
@ -137,13 +147,11 @@ export function ChatCreation({
<Contacts>
<ContactsHeading>Contacts</ContactsHeading>
<ContactsList>
{identity &&
{userPK &&
!query &&
Object.values(contacts)
.filter(
(e) =>
e.id != bufToHex(identity.publicKey) &&
!styledGroup.includes(e.id)
(e) => e.id != userPK && !groupChatMembersIds.includes(e.id)
)
.map((contact) => (
<Member
@ -157,7 +165,10 @@ export function ChatCreation({
</Contacts>
)}
{!activeChannel && (
<ChatInput createChat={createChat} group={styledGroup} />
<ChatInput
createChat={createChat}
group={groupChatMembers.map((member) => member.id)}
/>
)}
</CreationWrapper>
);

View File

@ -1,3 +1,4 @@
import { EmojiData } from "emoji-mart";
import React, {
useCallback,
useEffect,
@ -72,16 +73,23 @@ export function ChatInput({
[imageUint]
);
const addEmoji = useCallback((e: any) => {
const sym = e.unified.split("-");
const codesArray: any[] = [];
sym.forEach((el: string) => codesArray.push("0x" + el));
const emoji = String.fromCodePoint(...codesArray);
if (inputRef.current) {
inputRef.current.appendChild(document.createTextNode(emoji));
}
setContent((p) => p + emoji);
}, []);
const addEmoji = useCallback(
(e: EmojiData) => {
if ("unified" in e) {
const sym = e.unified.split("-");
const codesArray: string[] = [];
sym.forEach((el: string) => codesArray.push("0x" + el));
const emoji = String.fromCodePoint(
...(codesArray as unknown as number[])
);
if (inputRef.current) {
inputRef.current.appendChild(document.createTextNode(emoji));
}
setContent((p) => p + emoji);
}
},
[setContent]
);
const resizeTextArea = useCallback((target: HTMLDivElement) => {
target.style.height = "40px";
@ -91,31 +99,37 @@ export function ChatInput({
const rowHeight = inputHeight + (image ? 73 : 0);
const onInputChange = useCallback((e: React.ChangeEvent<HTMLDivElement>) => {
const element = document.getSelection();
const inputElement = inputRef.current;
if (inputElement && element && element.rangeCount > 0) {
const selection = element?.getRangeAt(0)?.startOffset;
const parentElement = element.anchorNode?.parentElement;
if (parentElement && parentElement.tagName === "B") {
parentElement.outerHTML = parentElement.innerText;
const range = document.createRange();
const sel = window.getSelection();
if (element.anchorNode.firstChild) {
const childNumber =
element.focusOffset === 0 ? 0 : element.focusOffset - 1;
range.setStart(element.anchorNode.childNodes[childNumber], selection);
}
range.collapse(true);
const onInputChange = useCallback(
(e: React.ChangeEvent<HTMLDivElement>) => {
const element = document.getSelection();
const inputElement = inputRef.current;
if (inputElement && element && element.rangeCount > 0) {
const selection = element?.getRangeAt(0)?.startOffset;
const parentElement = element.anchorNode?.parentElement;
if (parentElement && parentElement.tagName === "B") {
parentElement.outerHTML = parentElement.innerText;
const range = document.createRange();
const sel = window.getSelection();
if (element.anchorNode.firstChild) {
const childNumber =
element.focusOffset === 0 ? 0 : element.focusOffset - 1;
range.setStart(
element.anchorNode.childNodes[childNumber],
selection
);
}
range.collapse(true);
sel?.removeAllRanges();
sel?.addRange(range);
sel?.removeAllRanges();
sel?.addRange(range);
}
}
}
const target = e.target;
resizeTextArea(target);
setContent(target.textContent ?? "");
}, []);
const target = e.target;
resizeTextArea(target);
setContent(target.textContent ?? "");
},
[resizeTextArea]
);
const onInputKeyPress = useCallback(
(e: React.KeyboardEvent<HTMLDivElement>) => {
@ -137,7 +151,16 @@ export function ChatInput({
}
}
},
[content, imageUint]
[
content,
imageUint,
createChat,
group,
sendMessage,
reply?.id,
setChatState,
setReply,
]
);
const [selectedElement, setSelectedElement] = useState<{
@ -176,7 +199,7 @@ export function ChatInput({
}
}, []);
useEffect(handleCursorChange, [content]);
useEffect(handleCursorChange, [content, handleCursorChange]);
const addMention = useCallback(
(contact: string) => {
@ -215,7 +238,7 @@ export function ChatInput({
}
}
},
[inputRef, inputRef?.current, content, selectedElement]
[inputRef, content, selectedElement, resizeTextArea]
);
return (

View File

@ -1,10 +1,9 @@
import { utils } from "@waku/status-communities/dist/cjs";
import { decode } from "html-entities";
import React, { useEffect, useMemo, useRef, useState } from "react";
import styled from "styled-components";
import { useFetchMetadata } from "../../contexts/fetchMetadataProvider";
import { useIdentity } from "../../contexts/identityProvider";
import { useUserPublicKey } from "../../contexts/identityProvider";
import { useMessengerContext } from "../../contexts/messengerProvider";
import { useClickOutside } from "../../hooks/useClickOutside";
import { ChatMessage } from "../../models/ChatMessage";
@ -23,13 +22,13 @@ export function Mention({ id, setMentioned, className }: MentionProps) {
const { contacts } = useMessengerContext();
const contact = useMemo(() => contacts[id.slice(1)], [id, contacts]);
const [showMenu, setShowMenu] = useState(false);
const identity = useIdentity();
const userPK = useUserPublicKey();
useEffect(() => {
if (identity && contact) {
if (contact.id === utils.bufToHex(identity.publicKey)) setMentioned(true);
if (userPK && contact) {
if (contact.id === userPK) setMentioned(true);
}
}, [contact, identity]);
}, [contact, userPK, setMentioned]);
const ref = useRef(null);
useClickOutside(ref, () => setShowMenu(false));
@ -92,7 +91,7 @@ export function ChatMessageContent({
newSplit.pop();
setLink(link);
setElements(newSplit);
}, [content]);
}, [content, setLink, setMentioned, setElements, setLinkOpen]);
useEffect(() => {
const updatePreview = async () => {
@ -108,7 +107,7 @@ export function ChatMessageContent({
}
};
updatePreview();
}, [link]);
}, [link, fetchMetadata]);
return (
<ContentWrapper>

View File

@ -1,4 +1,4 @@
import { Picker } from "emoji-mart";
import { EmojiData, Picker } from "emoji-mart";
import React from "react";
import { useTheme } from "styled-components";
@ -7,7 +7,7 @@ import { lightTheme, Theme } from "../../styles/themes";
type EmojiPickerProps = {
showEmoji: boolean;
addEmoji: (e: any) => void;
addEmoji: (e: EmojiData) => void;
bottom: number;
};

View File

@ -10,6 +10,7 @@ import { IdentityProvider } from "../contexts/identityProvider";
import { MessengerProvider } from "../contexts/messengerProvider";
import { ModalProvider } from "../contexts/modalProvider";
import { NarrowProvider } from "../contexts/narrowProvider";
import { ScrollProvider } from "../contexts/scrollProvider";
import { ToastProvider } from "../contexts/toastProvider";
import { Metadata } from "../models/Metadata";
import { GlobalStyle } from "../styles/GlobalStyle";
@ -43,8 +44,10 @@ export function CommunityChat({
<IdentityProvider>
<MessengerProvider communityKey={communityKey}>
<ChatStateProvider>
<CommunityChatRoom />
<div id="modal-root" />
<ScrollProvider>
<CommunityChatRoom />
<div id="modal-root" />
</ScrollProvider>
</ChatStateProvider>
</MessengerProvider>
</IdentityProvider>

View File

@ -1,8 +1,7 @@
import { bufToHex } from "@waku/status-communities/dist/cjs/utils";
import React, { useMemo } from "react";
import styled from "styled-components";
import { useIdentity } from "../../contexts/identityProvider";
import { useUserPublicKey } from "../../contexts/identityProvider";
import { useMessengerContext } from "../../contexts/messengerProvider";
import { useModal } from "../../contexts/modalProvider";
import { AddContactIcon } from "../Icons/AddContactIcon";
@ -25,16 +24,16 @@ type ContactMenuProps = {
};
export function ContactMenu({ id, setShowMenu }: ContactMenuProps) {
const identity = useIdentity();
const userPK = useUserPublicKey();
const { contacts, contactsDispatch } = useMessengerContext();
const contact = useMemo(() => contacts[id], [id, contacts]);
const isUser = useMemo(() => {
if (identity) {
return id === bufToHex(identity.publicKey);
if (userPK) {
return id === userPK;
} else {
return false;
}
}, [id, identity]);
}, [id, userPK]);
const { setModal } = useModal(ProfileModalName);

View File

@ -1,9 +1,8 @@
import { utils } from "@waku/status-communities/dist/cjs";
import { BaseEmoji } from "emoji-mart";
import React, { useRef } from "react";
import React, { useMemo, useRef } from "react";
import styled from "styled-components";
import { useIdentity } from "../../contexts/identityProvider";
import { useUserPublicKey } from "../../contexts/identityProvider";
import { useMessengerContext } from "../../contexts/messengerProvider";
import { useClickOutside } from "../../hooks/useClickOutside";
import { useClickPosition } from "../../hooks/useClickPosition";
@ -33,23 +32,27 @@ export const MessageMenu = ({
setReply,
messageRef,
}: MessageMenuProps) => {
const identity = useIdentity();
const userPK = useUserPublicKey();
const { activeChannel } = useMessengerContext();
const { showMenu, setShowMenu } = useContextMenu(message.id);
const { topPosition, leftPosition } = useClickPosition(messageRef);
const menuStyle = {
top: topPosition,
left: leftPosition,
};
const menuStyle = useMemo(() => {
return {
top: topPosition,
left: leftPosition,
};
}, [topPosition, leftPosition]);
const ref = useRef(null);
useClickOutside(ref, () => setShowMenu(false));
const userMessage =
identity && message.sender === utils.bufToHex(identity.publicKey);
const userMessage = useMemo(
() => !!userPK && message.sender === userPK,
[userPK, message]
);
return identity && showMenu ? (
return userPK && showMenu ? (
<div ref={ref} id={"messageDropdown"}>
<MessageDropdown style={menuStyle}>
<MenuItem className="picker">

View File

@ -1,8 +1,7 @@
import { bufToHex } from "@waku/status-communities/dist/cjs/utils";
import React, { useMemo } from "react";
import styled from "styled-components";
import { useIdentity } from "../../contexts/identityProvider";
import { useUserPublicKey } from "../../contexts/identityProvider";
import { useMessengerContext } from "../../contexts/messengerProvider";
import { useModal } from "../../contexts/modalProvider";
import { Contact } from "../../models/Contact";
@ -14,16 +13,12 @@ import { Member } from "./Member";
export function MembersList() {
const { contacts, nickname, activeChannel } = useMessengerContext();
const identity = useIdentity();
const userPK = useMemo(
() => (identity ? bufToHex(identity?.publicKey) : undefined),
[identity]
);
const userPK = useUserPublicKey();
const { setModal } = useModal(LogoutModalName);
const members = useMemo(() => {
const contactsArray = Object.values(contacts);
if (identity) {
if (userPK) {
if (
activeChannel &&
activeChannel.type === "group" &&
@ -40,7 +35,7 @@ export function MembersList() {
return contactsArray.filter((e) => e.id !== userPK);
}
return contactsArray;
}, [activeChannel, contacts, identity, userPK]);
}, [activeChannel, contacts, userPK]);
const onlineContacts = useMemo(
() => members.filter((e) => e.online),
@ -53,15 +48,15 @@ export function MembersList() {
return (
<MembersListWrap>
{identity && (
{userPK && (
<MemberCategory>
<MemberCategoryName>You</MemberCategoryName>
<Row>
<Member
contact={{
id: userPK ?? "",
id: userPK,
customName: nickname,
trueName: userPK ?? "",
trueName: userPK,
}}
isYou={true}
/>

View File

@ -45,7 +45,7 @@ export function UserLogo({
return "offline";
}
return "";
}, [contact]);
}, [contact, showOnlineStatus]);
return (
<Wrapper radius={radius} conicGradient={conicGradient}>

View File

@ -2,7 +2,7 @@ import React from "react";
import styled from "styled-components";
import { useMessengerContext } from "../../contexts/messengerProvider";
import { useScrollToMessage } from "../../hooks/useScrollToMessage";
import { useScrollToMessage } from "../../contexts/scrollProvider";
import { ChatMessage } from "../../models/ChatMessage";
import { ReplyOn, ReplyTo } from "../Chat/ChatInput";
import { QuoteSvg } from "../Icons/QuoteIcon";

View File

@ -30,7 +30,7 @@ export function MessagesList({ setReply, channel }: MessagesListProps) {
messages.filter(
(message) => !contacts?.[message.sender]?.blocked ?? true
),
[contacts, messages, messages.length]
[contacts, messages]
);
const [image, setImage] = useState("");
@ -41,8 +41,14 @@ export function MessagesList({ setReply, channel }: MessagesListProps) {
const { setModal: setLinkModal, isVisible: showLinkModal } =
useModal(LinkModalName);
useEffect(() => (!image ? undefined : setPictureModal(true)), [image]);
useEffect(() => (!link ? undefined : setLinkModal(true)), [link]);
useEffect(
() => (!image ? undefined : setPictureModal(true)),
[image, setPictureModal]
);
useEffect(
() => (!link ? undefined : setLinkModal(true)),
[link, setLinkModal]
);
useEffect(
() => (!showPictureModal ? setImage("") : undefined),

View File

@ -27,9 +27,9 @@ export const EditModal = () => {
const [groupName, setGroupName] = useState("");
const [image, setImage] = useState("");
const handleChange = (e: any) => {
if (e.target.files.length) {
setImage(URL.createObjectURL(e.target.files[0]));
const handleChange = (e: React.FormEvent<HTMLInputElement>) => {
if (e.currentTarget?.files?.length) {
setImage(URL.createObjectURL(e.currentTarget.files[0]));
}
};

View File

@ -1,11 +1,10 @@
import { utils } from "@waku/status-communities/dist/cjs";
import React from "react";
import styled from "styled-components";
import {
useIdentity,
useSetIdentity,
useSetNikcname,
useUserPublicKey,
} from "../../contexts/identityProvider";
import { useMessengerContext } from "../../contexts/messengerProvider";
import { useModal } from "../../contexts/modalProvider";
@ -28,10 +27,10 @@ export const LogoutModal = () => {
const { setModal } = useModal(LogoutModalName);
const logout = useSetIdentity();
const setNickname = useSetNikcname();
const identity = useIdentity();
const userPK = useUserPublicKey();
const { nickname } = useMessengerContext();
if (identity) {
if (userPK) {
return (
<Modal name={LogoutModalName}>
<Section>
@ -42,9 +41,9 @@ export const LogoutModal = () => {
<UserSection>
<UserLogo
contact={{
id: utils.bufToHex(identity.publicKey),
id: userPK,
customName: nickname,
trueName: utils.bufToHex(identity.publicKey),
trueName: userPK,
}}
radius={80}
colorWheel={[
@ -61,8 +60,8 @@ export const LogoutModal = () => {
<UserAddressWrapper className="small">
<UserAddress className="small">
{" "}
Chatkey: {identity.privateKey.slice(0, 10)}...
{identity.privateKey.slice(-3)}{" "}
Chatkey: {userPK.slice(0, 10)}...
{userPK.slice(-3)}{" "}
</UserAddress>
</UserAddressWrapper>
<EmojiKey className="small">🎩🍞🥑🦍🌈📡💅🏻🔔👵🅱</EmojiKey>

View File

@ -1,8 +1,7 @@
import { bufToHex } from "@waku/status-communities/dist/cjs/utils";
import React, { useEffect, useMemo, useState } from "react";
import styled from "styled-components";
import { useIdentity } from "../../contexts/identityProvider";
import { useUserPublicKey } from "../../contexts/identityProvider";
import { useMessengerContext } from "../../contexts/messengerProvider";
import { useModal } from "../../contexts/modalProvider";
import { useToasts } from "../../contexts/toastProvider";
@ -51,14 +50,14 @@ export const ProfileModal = () => {
const { setToasts } = useToasts();
const { setModal } = useModal(ProfileModalName);
const identity = useIdentity();
const userPK = useUserPublicKey();
const isUser = useMemo(() => {
if (identity) {
return id === bufToHex(identity.publicKey);
if (userPK) {
return id === userPK;
} else {
return false;
}
}, [id, identity]);
}, [id, userPK]);
const [renaming, setRenaming] = useState(renamingState ?? false);

View File

@ -3,9 +3,9 @@ import React, { useState } from "react";
import styled from "styled-components";
import {
useIdentity,
useSetIdentity,
useSetNikcname,
useUserPublicKey,
useWalletIdentity,
} from "../../contexts/identityProvider";
import { useModal } from "../../contexts/modalProvider";
@ -38,7 +38,7 @@ export const UserCreationModalName = "UserCreationModal";
export function UserCreationModal() {
const walletIdentity = useWalletIdentity();
const identity = useIdentity();
const userPK = useUserPublicKey();
const setIdentity = useSetIdentity();
const setNickname = useSetNikcname();
@ -107,12 +107,12 @@ export function UserCreationModal() {
<NameError error={error} />
{nextStep && identity && (
{nextStep && userPK && (
<>
<UserAddress>
{" "}
Chatkey: {identity.privateKey.slice(0, 10)}...
{identity.privateKey.slice(-3)}{" "}
Chatkey: {userPK.slice(0, 10)}...
{userPK.slice(-3)}{" "}
</UserAddress>
<ChainIcons>
<ChainIcon className="transformed" />

View File

@ -26,7 +26,7 @@ export function WalletModal() {
const { setModal } = useModal(WalletModalName);
const setIdentity = useSetIdentity();
const setWalletIdentity = useSetWalletIdentity();
const userCreationModal = useModal(UserCreationModalName);
const { setModal: setUserCreationModal } = useModal(UserCreationModalName);
const { setModal: setWalleConnectModal } = useModal(WalletConnectModalName);
const { setModal: setCoinbaseModal } = useModal(CoinbaseModalName);
const { messenger } = useMessengerContext();
@ -92,7 +92,7 @@ export function WalletModal() {
setIdentity(loadedIdentity);
} else {
setWalletIdentity(loadedIdentity);
userCreationModal.setModal(true);
setUserCreationModal(true);
}
setModal(false);
return;
@ -102,7 +102,14 @@ export function WalletModal() {
}
}
alert("Metamask not found");
}, [messenger]);
}, [
messenger,
dappUrl,
setIdentity,
setModal,
setWalletIdentity,
setUserCreationModal,
]);
return (
<Modal name={WalletModalName}>

View File

@ -36,7 +36,7 @@ export function ReactionPicker({
? setMessageReactions((prev) => prev.filter((e) => e != emoji))
: setMessageReactions((prev) => [...prev, emoji]);
},
[messageReactions]
[messageReactions, setMessageReactions]
);
return (

View File

@ -1,9 +1,8 @@
import { utils } from "@waku/status-communities/dist/cjs";
import { BaseEmoji } from "emoji-mart";
import React from "react";
import React, { useMemo } from "react";
import styled from "styled-components";
import { useIdentity } from "../../contexts/identityProvider";
import { useUserPublicKey } from "../../contexts/identityProvider";
import { useMessengerContext } from "../../contexts/messengerProvider";
import { Reply } from "../../hooks/useReply";
import { ChatMessage } from "../../models/ChatMessage";
@ -28,11 +27,13 @@ export function Reactions({
messageReactions,
setMessageReactions,
}: ReactionsProps) {
const identity = useIdentity();
const userPK = useUserPublicKey();
const { activeChannel } = useMessengerContext();
const userMessage =
identity && message.sender === utils.bufToHex(identity.publicKey);
const userMessage = useMemo(
() => !!userPK && message.sender === userPK,
[userPK, message]
);
return (
<Wrapper>

View File

@ -1,4 +1,4 @@
import React, { useMemo } from "react";
import React from "react";
import styled from "styled-components";
import { useToasts } from "../../contexts/toastProvider";
@ -8,11 +8,9 @@ import { ToastMessage } from "./ToastMessage";
export function ToastMessageList() {
const { toasts } = useToasts();
const shownToasts = useMemo(() => toasts, [toasts, toasts.length]);
return (
<ToastsWrapper>
{shownToasts.map((toast) => (
{toasts.map((toast) => (
<ToastMessage key={toast.id} toast={toast} />
))}
</ToastsWrapper>

View File

@ -1,9 +1,11 @@
import { Identity } from "@waku/status-communities/dist/cjs";
import React, { createContext, useContext, useState } from "react";
import { bufToHex } from "@waku/status-communities/dist/cjs/utils";
import React, { createContext, useContext, useMemo, useState } from "react";
const IdentityContext = createContext<{
identity: Identity | undefined;
setIdentity: React.Dispatch<React.SetStateAction<Identity | undefined>>;
publicKey: string | undefined;
walletIdentity: Identity | undefined;
setWalletIdentity: React.Dispatch<React.SetStateAction<Identity | undefined>>;
nickname: string | undefined;
@ -11,6 +13,7 @@ const IdentityContext = createContext<{
}>({
identity: undefined,
setIdentity: () => undefined,
publicKey: undefined,
walletIdentity: undefined,
setWalletIdentity: () => undefined,
nickname: undefined,
@ -21,6 +24,10 @@ export function useIdentity() {
return useContext(IdentityContext).identity;
}
export function useUserPublicKey() {
return useContext(IdentityContext).publicKey;
}
export function useSetIdentity() {
return useContext(IdentityContext).setIdentity;
}
@ -47,6 +54,10 @@ interface IdentityProviderProps {
export function IdentityProvider({ children }: IdentityProviderProps) {
const [identity, setIdentity] = useState<Identity | undefined>(undefined);
const publicKey = useMemo(
() => (identity ? bufToHex(identity.publicKey) : undefined),
[identity]
);
const [walletIdentity, setWalletIdentity] = useState<Identity | undefined>(
undefined
);
@ -57,6 +68,7 @@ export function IdentityProvider({ children }: IdentityProviderProps) {
value={{
identity,
setIdentity,
publicKey,
nickname,
setNickname,
walletIdentity,

View File

@ -44,7 +44,7 @@ export function useModal<T extends string>(name: T) {
};
});
},
[name, modals]
[name, setModals]
);
const isVisible = useMemo(() => !!modals?.[name], [modals, name]);

View File

@ -1,9 +1,27 @@
import { useCallback, useEffect, useState } from "react";
import React, {
createContext,
useCallback,
useContext,
useEffect,
useState,
} from "react";
import { useMessengerContext } from "../contexts/messengerProvider";
import { ChatMessage } from "../models/ChatMessage";
const ScrollContext = createContext<
(msg: ChatMessage, channelId?: string) => void
>(() => undefined);
export function useScrollToMessage() {
return useContext(ScrollContext);
}
interface ScrollProviderProps {
children: React.ReactNode;
}
export function ScrollProvider({ children }: ScrollProviderProps) {
const scrollToDivId = useCallback((id: string) => {
const quoteDiv = document.getElementById(id);
if (quoteDiv) {
@ -35,17 +53,18 @@ export function useScrollToMessage() {
setMessageChannel("");
}
}
}, [activeChannel, scrollToMessage, messageChannel]);
const scroll = useCallback((msg: ChatMessage, channelId?: string) => {
if (!channelId) {
scrollToDivId(msg.id);
} else {
setMessageChannel(channelId);
setScrollToMessage(msg.id);
channelsDispatch({ type: "ChangeActive", payload: channelId });
}
}, []);
return scroll;
}, [activeChannel, scrollToMessage, messageChannel, scrollToDivId]);
const scroll = useCallback(
(msg: ChatMessage, channelId?: string) => {
if (!channelId || activeChannel?.id === channelId) {
scrollToDivId(msg.id);
} else {
setMessageChannel(channelId);
setScrollToMessage(msg.id);
channelsDispatch({ type: "ChangeActive", payload: channelId });
}
},
[scrollToDivId, channelsDispatch, activeChannel]
);
return <ScrollContext.Provider value={scroll} children={children} />;
}

View File

@ -101,7 +101,7 @@ export function useContacts(
const [contacts, contactsDispatch] = useReducer(contactsReducer, {});
const contactsClass = useMemo(() => {
if (messenger) {
if (messenger && messenger.identity === identity) {
const newContacts = new ContactsClass(
identity,
messenger.waku,
@ -120,7 +120,7 @@ export function useContacts(
);
return newContacts;
}
}, [messenger, identity]);
}, [messenger, identity, newNickname]);
return { contacts, contactsDispatch, contactsClass, nickname };
}

View File

@ -84,7 +84,7 @@ export function useGroupChats(
handleMessage
);
}
}, [messenger, identity, contactsClass]);
}, [messenger, identity, contactsClass, addChatMessage, dispatch]);
const createGroupChat = useCallback(
(members: string[]) => {

View File

@ -64,7 +64,7 @@ export function useLoadPrevDay(
}
}
},
[messenger]
[messenger, groupChats]
);
return { loadingMessages, loadPrevDay };
}

View File

@ -14,7 +14,9 @@ import { useNotifications } from "./useNotifications";
export function useMessages(
chatId: string,
identity: Identity | undefined,
subscriptions: ((msg: ChatMessage, id: string) => void)[],
subscriptions: React.MutableRefObject<
((msg: ChatMessage, id: string) => void)[]
>,
contacts?: Contacts
) {
const [messages, setMessages] = useState<{ [chatId: string]: ChatMessage[] }>(
@ -23,18 +25,22 @@ export function useMessages(
const { notifications, incNotification, clearNotifications } =
useNotifications();
const mentions = useNotifications();
const {
notifications: mentions,
incNotification: incMentions,
clearNotifications: clearMentions,
} = useNotifications();
const addChatMessage = useCallback(
(newMessage: ChatMessage | undefined, id: string) => {
if (newMessage) {
contacts?.addContact(newMessage.sender);
if (newMessage.responseTo) {
newMessage.quote = messages[id].find(
(msg) => msg.id === newMessage.responseTo
);
}
setMessages((prev) => {
if (newMessage.responseTo && prev[id]) {
newMessage.quote = prev[id].find(
(msg) => msg.id === newMessage.responseTo
);
}
return {
...prev,
[id]: binarySetInsert(
@ -45,17 +51,19 @@ export function useMessages(
),
};
});
subscriptions.forEach((subscription) => subscription(newMessage, id));
subscriptions.current.forEach((subscription) =>
subscription(newMessage, id)
);
incNotification(id);
if (
identity &&
newMessage.content.includes(`@${bufToHex(identity.publicKey)}`)
) {
mentions.incNotification(id);
incMentions(id);
}
}
},
[contacts, identity, subscriptions]
[contacts, identity, subscriptions, incMentions, incNotification]
);
const addMessage = useCallback(
@ -63,21 +71,23 @@ export function useMessages(
const newMessage = ChatMessage.fromMetadataMessage(msg, date);
addChatMessage(newMessage, id);
},
[contacts, identity]
[addChatMessage]
);
const activeMessages = useMemo(
() => messages?.[chatId] ?? [],
[messages, chatId]
);
const activeMessages = useMemo(() => {
if (messages?.[chatId]) {
return [...messages[chatId]];
}
return [];
}, [messages, chatId]);
return {
messages: activeMessages,
addMessage,
notifications,
clearNotifications,
mentions: mentions.notifications,
clearMentions: mentions.clearNotifications,
mentions,
clearMentions,
addChatMessage,
};
}

View File

@ -6,7 +6,14 @@ import {
Identity,
Messenger,
} from "@waku/status-communities/dist/cjs";
import { useCallback, useEffect, useMemo, useReducer, useState } from "react";
import {
useCallback,
useEffect,
useMemo,
useReducer,
useRef,
useState,
} from "react";
import { useConfig } from "../../contexts/configProvider";
import { ChannelData, ChannelsData } from "../../models/ChannelData";
@ -60,24 +67,33 @@ function useCreateMessenger(identity: Identity | undefined) {
createMessenger(identity, environment).then((e) => {
setMessenger(e);
});
}, [identity]);
}, [identity, environment]);
return messenger;
}
function useCreateCommunity(
messenger: Messenger | undefined,
identity: Identity | undefined,
communityKey: string | undefined,
addMessage: (msg: ApplicationMetadataMessage, id: string, date: Date) => void,
contactsClass: ContactsClass | undefined
) {
const [community, setCommunity] = useState<Community | undefined>(undefined);
useEffect(() => {
if (messenger && communityKey && contactsClass) {
if (
messenger &&
communityKey &&
contactsClass &&
addMessage &&
messenger.identity === identity
) {
createCommunity(communityKey, addMessage, messenger).then((comm) => {
setCommunity(comm);
});
}
}, [messenger, communityKey, addMessage, contactsClass]);
}, [messenger, communityKey, addMessage, contactsClass, identity]);
const communityData = useMemo(() => {
if (community?.description) {
@ -101,7 +117,7 @@ function useCreateCommunity(
} else {
return undefined;
}
}, [community]);
}, [community, contactsClass]);
return { community, communityData };
}
@ -153,7 +169,11 @@ export function useMessenger(
subscriptionReducer,
{}
);
const subList = useMemo(() => Object.values(subscriptions), [subscriptions]);
const subList = useRef<((msg: ChatMessage, id: string) => void)[]>([]);
useEffect(() => {
subList.current = Object.values(subscriptions);
}, [subscriptions]);
const [channelsState, channelsDispatch] = useChannelsReducer();
const messenger = useCreateMessenger(identity);
const { contacts, contactsDispatch, contactsClass, nickname } = useContacts(
@ -188,6 +208,7 @@ export function useMessenger(
const { community, communityData } = useCreateCommunity(
messenger,
identity,
communityKey,
addMessage,
contactsClass
@ -207,7 +228,7 @@ export function useMessenger(
});
}
}
}, [community]);
}, [community, channelsDispatch]);
useEffect(() => {
Object.values(channelsState.channels)
@ -227,7 +248,7 @@ export function useMessenger(
});
}
});
}, [contacts, channelsState.channels]);
}, [contacts, channelsState.channels, channelsDispatch]);
const {
groupChat,
@ -255,7 +276,7 @@ export function useMessenger(
loadPrevDay(id)
);
}
}, [messenger, community]);
}, [messenger, community, loadPrevDay]);
const sendMessage = useCallback(
async (messageText?: string, image?: Uint8Array, responseTo?: string) => {
@ -299,7 +320,7 @@ export function useMessenger(
clearMentions(channelsState.activeChannel.id);
}
}
}, [notifications, channelsState]);
}, [notifications, channelsState, clearNotifications, clearMentions]);
const loadingMessenger = useMemo(() => {
return Boolean(
@ -307,7 +328,7 @@ export function useMessenger(
!messenger ||
(communityKey && !channelsState.activeChannel.id)
);
}, [communityData, messenger, channelsState]);
}, [communityData, messenger, channelsState, communityKey]);
return {
messenger,

View File

@ -1,7 +1,6 @@
import { bufToHex } from "@waku/status-communities/dist/cjs/utils";
import { useEffect, useMemo, useReducer } from "react";
import { useIdentity } from "../contexts/identityProvider";
import { useUserPublicKey } from "../contexts/identityProvider";
import { useMessengerContext } from "../contexts/messengerProvider";
import { Activities, Activity, ActivityStatus } from "../models/Activity";
import { ChatMessage } from "../models/ChatMessage";
@ -65,17 +64,13 @@ export function useActivities() {
() => Object.values(activitiesObj),
[activitiesObj]
);
const identity = useIdentity();
const userPK = useMemo(
() => (identity ? bufToHex(identity.publicKey) : undefined),
[identity]
);
const userPK = useUserPublicKey();
const { subscriptionsDispatch, channels } = useMessengerContext();
useEffect(() => {
if (identity) {
if (userPK) {
const subscribeFunction = (message: ChatMessage, id: string) => {
if (message.quote && identity && message.quote.sender === userPK) {
if (message.quote && message.quote.sender === userPK) {
const newActivity: Activity = {
id: message.date.getTime().toString() + message.content,
type: "reply",
@ -114,7 +109,7 @@ export function useActivities() {
type: "removeSubscription",
payload: { name: "activityCenter" },
});
}, [subscriptionsDispatch, identity]);
}, [subscriptionsDispatch, userPK, channels]);
return { activities, activityDispatch: dispatch };
}

View File

@ -14,10 +14,10 @@ export function useChatScrollHandle(
if (ref && ref.current && scrollOnBot) {
ref.current.scrollTop = ref.current.scrollHeight;
}
}, [messages.length, scrollOnBot]);
}, [messages.length, scrollOnBot, ref]);
useEffect(() => {
if (!loadingMessages && activeChannel) {
if (activeChannel) {
if (
(ref?.current?.clientHeight ?? 0) >= (ref?.current?.scrollHeight ?? 0)
) {
@ -25,9 +25,10 @@ export function useChatScrollHandle(
loadPrevDay(activeChannel.id, activeChannel.type !== "channel");
}
}
}, [messages.length, activeChannel]);
}, [messages.length, activeChannel, loadPrevDay, setScrollOnBot, ref]);
useEffect(() => {
const currentRef = ref.current;
const setScroll = () => {
if (ref?.current && activeChannel) {
if (ref.current.scrollTop <= 0) {
@ -47,8 +48,8 @@ export function useChatScrollHandle(
}
}
};
ref.current?.addEventListener("scroll", setScroll);
return () => ref.current?.removeEventListener("scroll", setScroll);
}, [ref, scrollOnBot, activeChannel]);
currentRef?.addEventListener("scroll", setScroll);
return () => currentRef?.removeEventListener("scroll", setScroll);
}, [ref, scrollOnBot, activeChannel, loadPrevDay]);
return loadingMessages;
}

View File

@ -2,8 +2,7 @@ import { RefObject, useCallback, useEffect } from "react";
export const useClickOutside = (
ref: RefObject<HTMLDivElement>,
callback: () => void,
deps?: any[]
callback: () => void
) => {
const handleClick = useCallback(
(e: MouseEvent) => {
@ -20,5 +19,5 @@ export const useClickOutside = (
return () => {
document.removeEventListener("mousedown", handleClick);
};
}, deps);
}, [handleClick]);
};

View File

@ -15,7 +15,7 @@ export const useClickPosition = (ref: RefObject<HTMLDivElement>) => {
setTopPosition(imgTarget ? 0 : e.clientY - rect.top);
}
},
[setTopPosition, setLeftPosition]
[setTopPosition, setLeftPosition, ref]
);
useEffect(() => {

View File

@ -21,7 +21,7 @@ export const useContextMenu = (elementId: string) => {
document.removeEventListener("click", () => setShowMenu(false));
setShowMenu(false);
};
}, [elementId]);
}, [elementId, handleContextMenu]);
return { showMenu, setShowMenu };
};

View File

@ -12,9 +12,9 @@ export enum NameErrors {
export function useNameError(name: string) {
const { contacts } = useMessengerContext();
const RegName = new RegExp("^[a-z0-9_-]+$");
const error = useMemo(() => {
const RegName = new RegExp("^[a-z0-9_-]+$");
if (name === "") {
return NameErrors.NoError;
}

View File

@ -20,7 +20,7 @@ export function useRefBreak(dimension: number, sizeThreshold: number) {
return () => {
window.removeEventListener("resize", checkDimensions);
};
}, [dimension, widthBreak]);
}, [dimension, widthBreak, sizeThreshold]);
return widthBreak;
}