Add user creation modal (#159)

This commit is contained in:
Szymon Szlachtowicz 2021-12-21 13:39:46 +01:00 committed by GitHub
parent 35cc40329e
commit 51802fa03d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 387 additions and 112 deletions

View File

@ -13,6 +13,7 @@ import { Members } from "./Members/Members";
import { CommunityModal } from "./Modals/CommunityModal";
import { EditModal } from "./Modals/EditModal";
import { ProfileModal } from "./Modals/ProfileModal";
import { UserCreationModal } from "./Modals/UserCreationModal";
import { ToastMessageList } from "./ToastMessages/ToastMessageList";
import { UserCreation } from "./UserCreation/UserCreation";
@ -20,6 +21,7 @@ function Modals() {
return (
<>
<CommunityModal subtitle="Public Community" />
<UserCreationModal />
<EditModal />
<ProfileModal />
</>

View File

@ -23,8 +23,9 @@ export function ChatCreation({
const identity = useIdentity();
const [query, setQuery] = useState("");
const [styledGroup, setStyledGroup] = useState<string[]>(
activeChannel?.members?.map((member) => member?.customName ?? member.id) ??
[]
activeChannel?.members?.map(
(member) => member?.customName ?? member.trueName
) ?? []
);
const { contacts, createGroupChat, addMembers } = useMessengerContext();
const setChatState = useChatState()[1];

View File

@ -34,7 +34,7 @@ export function Mention({ id, setMentioned, className }: MentionProps) {
return (
<MentionBLock onClick={() => setShowMenu(!showMenu)} className={className}>
{`@${contact?.customName ?? contact.id}`}
{`@${contact?.customName ?? contact.trueName}`}
{showMenu && <ContactMenu id={id.slice(1)} setShowMenu={setShowMenu} />}
</MentionBLock>
);

View File

@ -1,5 +1,4 @@
import React, { useState } from "react";
import { Identity } from "status-communities/dist/cjs";
import React from "react";
import { ChatStateProvider } from "../contexts/chatStateProvider";
import { IdentityProvider } from "../contexts/identityProvider";
@ -17,13 +16,12 @@ interface ChatLoaderProps {
}
export function ChatLoader({ communityKey }: ChatLoaderProps) {
const [identity, setIdentity] = useState<Identity | undefined>(undefined);
const [userCreationState] = useUserCreationState();
if (userCreationState === UserCreationState.NotCreating)
return (
<IdentityProvider identity={identity}>
<MessengerProvider identity={identity} communityKey={communityKey}>
<IdentityProvider>
<MessengerProvider communityKey={communityKey}>
<ChatStateProvider>
<Chat />
</ChatStateProvider>
@ -31,7 +29,7 @@ export function ChatLoader({ communityKey }: ChatLoaderProps) {
</IdentityProvider>
);
if (userCreationState === UserCreationState.Creating) {
return <IdentityLoader setIdentity={setIdentity} />;
return <IdentityLoader />;
}
return null;

View File

@ -43,7 +43,7 @@ export function ContactMenu({ id, setShowMenu }: ContactMenuProps) {
<ContactInfo>
<UserIcon />
<UserNameWrapper>
<UserName>{contact?.customName ?? id.slice(0, 10)}</UserName>
<UserName>{contact?.customName ?? contact.trueName}</UserName>
{contact.isUntrustworthy && <UntrustworthIcon />}
</UserNameWrapper>
{contact?.customName && (

View File

@ -2,6 +2,7 @@ import React, { useCallback, useEffect, useState } from "react";
import { Identity } from "status-communities/dist/cjs";
import styled from "styled-components";
import { useSetIdentity } from "../../contexts/identityProvider";
import {
UserCreationState,
useUserCreationState,
@ -12,11 +13,8 @@ import {
saveIdentity,
} from "../../utils";
interface IdentityLoaderProps {
setIdentity: (e: Identity) => void;
}
export function IdentityLoader({ setIdentity }: IdentityLoaderProps) {
export function IdentityLoader() {
const setIdentity = useSetIdentity();
const [password, setPassword] = useState("");
const state = useUserCreationState();

View File

@ -15,7 +15,7 @@ export function ColorChatSvg({ width, height }: ColorChatSvgProps) {
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g clip-path="url(#clip0_1307_429352)">
<g clipPath="url(#clip0_1307_429352)">
<path
d="M43.0453 20.5165C54.7313 20.338 64.2693 29.5356 64.4955 41.2207C64.5288 42.9363 64.3573 44.6062 64.004 46.2079C63.4058 48.92 63.0501 51.6799 63.0501 54.4573V59.8258C63.0501 60.6483 62.3834 61.315 61.5609 61.315H56.1924C53.415 61.315 50.6552 61.6707 47.943 62.2689C46.3414 62.6222 44.6715 62.7936 42.9559 62.7604C31.2709 62.5343 22.0733 52.9965 22.2516 41.3108C22.4254 29.9046 31.6392 20.6907 43.0453 20.5165Z"
fill="url(#paint0_linear_1307_429352)"
@ -62,8 +62,8 @@ export function ColorChatSvg({ width, height }: ColorChatSvgProps) {
y2="68.3915"
gradientUnits="userSpaceOnUse"
>
<stop stop-color="#A7F3CE" />
<stop offset="1" stop-color="#61DB99" />
<stop stopColor="#A7F3CE" />
<stop offset="1" stopColor="#61DB99" />
</linearGradient>
<linearGradient
id="paint1_linear_1307_429352"
@ -73,8 +73,8 @@ export function ColorChatSvg({ width, height }: ColorChatSvgProps) {
y2="34.356"
gradientUnits="userSpaceOnUse"
>
<stop stop-color="#61DB99" stop-opacity="0" />
<stop offset="1" stop-color="#009E74" />
<stop stopColor="#61DB99" stopOpacity="0" />
<stop offset="1" stopColor="#009E74" />
</linearGradient>
<linearGradient
id="paint2_linear_1307_429352"
@ -84,8 +84,8 @@ export function ColorChatSvg({ width, height }: ColorChatSvgProps) {
y2="55.5561"
gradientUnits="userSpaceOnUse"
>
<stop stop-color="#62E1FB" />
<stop offset="1" stop-color="#00A2F3" />
<stop stopColor="#62E1FB" />
<stop offset="1" stopColor="#00A2F3" />
</linearGradient>
<linearGradient
id="paint3_linear_1307_429352"
@ -95,8 +95,8 @@ export function ColorChatSvg({ width, height }: ColorChatSvgProps) {
y2="19.9482"
gradientUnits="userSpaceOnUse"
>
<stop stop-color="#00A2F3" stop-opacity="0" />
<stop offset="1" stop-color="#0075CD" />
<stop stopColor="#00A2F3" stopOpacity="0" />
<stop offset="1" stopColor="#0075CD" />
</linearGradient>
<linearGradient
id="paint4_linear_1307_429352"
@ -106,8 +106,8 @@ export function ColorChatSvg({ width, height }: ColorChatSvgProps) {
y2="19.9478"
gradientUnits="userSpaceOnUse"
>
<stop stop-color="#00A2F3" stop-opacity="0" />
<stop offset="1" stop-color="#2A353D" />
<stop stopColor="#00A2F3" stopOpacity="0" />
<stop offset="1" stopColor="#2A353D" />
</linearGradient>
<linearGradient
id="paint5_linear_1307_429352"
@ -117,8 +117,8 @@ export function ColorChatSvg({ width, height }: ColorChatSvgProps) {
y2="19.9479"
gradientUnits="userSpaceOnUse"
>
<stop stop-color="#00A2F3" stop-opacity="0" />
<stop offset="1" stop-color="#0075CD" />
<stop stopColor="#00A2F3" stopOpacity="0" />
<stop offset="1" stopColor="#0075CD" />
</linearGradient>
<clipPath id="clip0_1307_429352">
<rect

View File

@ -1,9 +1,8 @@
import React, { useMemo } from "react";
import { utils } from "status-communities/dist/cjs";
import { bufToHex } from "status-communities/dist/cjs/utils";
import styled from "styled-components";
import { useIdentity } from "../../contexts/identityProvider";
import { useIdentity, useNickname } from "../../contexts/identityProvider";
import { useMessengerContext } from "../../contexts/messengerProvider";
import { UserIcon } from "../Icons/UserIcon";
@ -16,6 +15,7 @@ interface MembersListProps {
export function MembersList({ switchShowMembers }: MembersListProps) {
const { contacts } = useMessengerContext();
const identity = useIdentity();
const nickname = useNickname();
const userContacts = useMemo(() => {
if (identity) {
return Object.values(contacts).filter(
@ -42,9 +42,7 @@ export function MembersList({ switchShowMembers }: MembersListProps) {
<MemberIcon>
<UserIcon memberView={true} />
</MemberIcon>
{identity && (
<MemberName>{utils.bufToHex(identity.publicKey)}</MemberName>
)}
{identity && <MemberName>{nickname}</MemberName>}
</MemberData>
</MemberCategory>
{onlineContacts.length > 0 && (

View File

@ -0,0 +1,70 @@
import React, { useMemo } from "react";
import styled from "styled-components";
type UserLogoProps = {
radius: number;
colorWheel: [string, number][];
icon?: string;
text?: string;
};
export function UserLogo({ icon, text, radius, colorWheel }: UserLogoProps) {
const conicGradient = useMemo(() => {
const colors = colorWheel
.map((color, idx) => {
const prevDeg = idx === 0 ? "0deg" : `${colorWheel[idx - 1][1]}deg`;
return `${color[0]} ${prevDeg} ${color[1]}deg`;
})
.join(",");
return `conic-gradient(${colors})`;
}, [colorWheel]);
return (
<Wrapper radius={radius} conicGradient={conicGradient}>
<Logo icon={icon} radius={radius}>
{!icon && text && <TextWrapper>{text}</TextWrapper>}
</Logo>
</Wrapper>
);
}
const TextWrapper = styled.div`
font-family: Inter;
font-style: normal;
font-weight: bold;
font-size: 32px;
line-height: 38px;
display: flex;
align-items: center;
text-align: center;
letter-spacing: -0.4px;
color: rgba(255, 255, 255, 0.7);
`;
const Logo = styled.div<{ radius: number; icon?: string }>`
width: calc(${({ radius }) => radius}px - 6px);
height: calc(${({ radius }) => radius}px - 6px);
align-items: center;
justify-content: center;
flex-shrink: 0;
border-radius: 50%;
font-weight: bold;
font-size: 15px;
line-height: 20px;
background-color: ${({ theme }) => theme.logoColor};
background-size: cover;
background-repeat: no-repeat;
background-image: ${({ icon }) => icon && `url(${icon}`};
color: ${({ theme }) => theme.iconTextColor};
margin: auto;
display: flex;
`;
const Wrapper = styled.div<{ radius: number; conicGradient: string }>`
width: ${({ radius }) => radius}px;
height: ${({ radius }) => radius}px;
display: flex;
margin-left: auto;
margin-right: auto;
border-radius: 50%;
background: ${({ conicGradient }) => conicGradient};
`;

View File

@ -121,15 +121,10 @@ export function UiMessage({
<ContentWrapper>
<MessageHeaderWrapper>
<UserNameWrapper>
<UserName>
{" "}
{contact?.customName ?? message.sender.slice(0, 10)}
</UserName>
{contact?.customName && (
<UserName> {contact?.customName ?? contact.trueName}</UserName>
<UserAddress>
{message.sender.slice(0, 5)}...{message.sender.slice(-3)}
</UserAddress>
)}
{contact.isUntrustworthy && <UntrustworthIcon />}
</UserNameWrapper>
<TimeWrapper>{message.date.toLocaleString()}</TimeWrapper>

View File

@ -1,5 +1,6 @@
import styled, { css } from "styled-components";
import { buttonStyles } from "../Buttons/buttonStyle";
import { textMediumStyles } from "../Text";
export const Section = styled.div`
@ -23,6 +24,36 @@ export const Text = styled.p`
${textMediumStyles}
`;
export const Btn = styled.button`
padding: 11px 24px;
margin-left: 8px;
${buttonStyles}
&:hover {
background: ${({ theme }) => theme.buttonBgHover};
}
&:disabled {
background: ${({ theme }) => theme.border};
color: ${({ theme }) => theme.secondary};
}
`;
export const BackBtn = styled(Btn)`
position: absolute;
left: 16px;
top: 16px;
width: 44px;
height: 44px;
border-radius: 50%;
padding: 8px;
margin-left: 0;
& > svg {
fill: ${({ theme }) => theme.tertiary};
}
`;
export const ButtonSection = styled(Section)`
display: flex;
justify-content: flex-end;

View File

@ -7,6 +7,7 @@ import { useIdentity } from "../../contexts/identityProvider";
import { useModal } from "../../contexts/modalProvider";
import { useToasts } from "../../contexts/toastProvider";
import { useManageContact } from "../../hooks/useManageContact";
import { NameInput } from "../../styles/Inputs";
import { copy } from "../../utils";
import { buttonStyles } from "../Buttons/buttonStyle";
import { ClearSvg } from "../Icons/ClearIcon";
@ -19,6 +20,8 @@ import { textMediumStyles, textSmallStyles } from "../Text";
import { Modal } from "./Modal";
import {
BackBtn,
Btn,
ButtonSection,
Heading,
Hint,
@ -84,7 +87,7 @@ export const ProfileModal = () => {
className={`${!requestCreation && "profile"}`}
>
<Section>
<Heading>{id.slice(0, 10)}s Profile</Heading>
<Heading>{contact.trueName}s Profile</Heading>
</Section>
<ProfileSection>
@ -101,7 +104,7 @@ export const ProfileModal = () => {
)}
<UserNameWrapper>
<UserName className={`${requestCreation && "small"}`}>
{contact?.customName ?? id.slice(0, 10)}
{contact?.customName ?? contact.trueName}
</UserName>
{contact.isUntrustworthy && <UntrustworthIcon />}
{!renaming && (
@ -402,36 +405,6 @@ const CopyButton = styled.button`
}
`;
const Btn = styled.button`
padding: 11px 24px;
margin-left: 8px;
${buttonStyles}
&:hover {
background: ${({ theme }) => theme.buttonBgHover};
}
&:disabled {
background: ${({ theme }) => theme.border};
color: ${({ theme }) => theme.secondary};
}
`;
const BackBtn = styled(Btn)`
position: absolute;
left: 16px;
top: 16px;
width: 44px;
height: 44px;
border-radius: 50%;
padding: 8px;
margin-left: 0;
& > svg {
fill: ${({ theme }) => theme.tertiary};
}
`;
const ClearBtn = styled.button`
position: absolute;
right: 16px;
@ -448,13 +421,6 @@ const NameInputWrapper = styled.div`
position: relative;
`;
const NameInput = styled.input`
width: 328px;
padding: 11px 16px;
${inputStyles}
`;
const RequestSection = styled.div`
width: 100%;
display: flex;

View File

@ -0,0 +1,117 @@
import React, { useState } from "react";
import { Identity } from "status-communities/dist/cjs";
import styled from "styled-components";
import {
useSetIdentity,
useSetNikcname,
} from "../../contexts/identityProvider";
import { useModal } from "../../contexts/modalProvider";
import { NameInput } from "../../styles/Inputs";
import { LeftIconSvg } from "../Icons/LeftIcon";
import { UserLogo } from "../Members/UserLogo";
import { Modal } from "./Modal";
import {
BackBtn,
Btn,
ButtonSection,
Heading,
Hint,
Section,
Text,
} from "./ModalStyle";
export const UserCreationModalName = "UserCreationModal";
export function UserCreationModal() {
const setIdentity = useSetIdentity();
const setNickname = useSetNikcname();
const [customNameInput, setCustomNameInput] = useState("");
const { setModal } = useModal(UserCreationModalName);
return (
<Modal name={UserCreationModalName}>
<Section>
<Heading>Create a Status Profile</Heading>
</Section>
<MiddleSection>
<Content>
<Title>Your profile</Title>
<StyledHint>
Longer and unusual names are better as they are less likely to be
used by someone else.
</StyledHint>
<LogoWrapper>
<UserLogo
text={customNameInput.slice(0, 2)}
radius={80}
colorWheel={[
["red", 150],
["blue", 250],
["green", 360],
]}
/>
</LogoWrapper>
<NameInput
placeholder="Display name"
value={customNameInput}
onChange={(e) => setCustomNameInput(e.currentTarget.value)}
/>
</Content>
</MiddleSection>
<ButtonSection style={{ marginBottom: "40px" }}>
<BackBtn onClick={() => setModal(false)}>
<LeftIconSvg width={28} height={28} />
</BackBtn>
<Btn
onClick={() => {
setIdentity(Identity.generate());
setNickname(customNameInput);
setModal(false);
}}
>
Next
</Btn>
</ButtonSection>
</Modal>
);
}
const MiddleSection = styled(Section)`
margin-bottom: 102px;
`;
const LogoWrapper = styled.div`
position: relative;
display: flex;
margin-top: 32px;
margin-bottom: 38px;
`;
const StyledHint = styled(Hint)`
font-family: Inter;
font-style: normal;
font-weight: normal;
font-size: 15px;
line-height: 22px;
text-align: center;
`;
const Title = styled(Text)`
font-family: Inter;
font-style: normal;
font-weight: bold;
font-size: 22px;
line-height: 30px;
text-align: center;
letter-spacing: -0.2px;
margin-bottom: 16px;
`;
const Content = styled.div`
margin-top: 16px;
margin-left: 65px;
margin-right: 65px;
`;

View File

@ -1,14 +1,12 @@
import React from "react";
import styled from "styled-components";
import {
UserCreationState,
useUserCreationState,
} from "../../contexts/userCreationStateProvider";
import { useModal } from "../../contexts/modalProvider";
import { ColorChatIcon } from "../Icons/ColorChatIcon";
import { UserCreationModalName } from "../Modals/UserCreationModal";
export function UserCreation() {
const state = useUserCreationState();
const { setModal } = useModal(UserCreationModalName);
return (
<Background>
<Wrapper>
@ -16,7 +14,7 @@ export function UserCreation() {
<ColorChatIcon />
</IconWrapper>
<TitleWrapper>Want in on the discussion ?</TitleWrapper>
<ThrowAwayButton onClick={() => state[1](UserCreationState.Creating)}>
<ThrowAwayButton onClick={() => setModal(true)}>
Use a throwaway account
</ThrowAwayButton>
</Wrapper>

View File

@ -1,20 +1,46 @@
import React, { createContext, useContext } from "react";
import React, { createContext, useContext, useState } from "react";
import { Identity } from "status-communities/dist/cjs";
const IdentityContext = createContext<Identity | undefined>(undefined);
const IdentityContext = createContext<{
identity: Identity | undefined;
setIdentity: React.Dispatch<React.SetStateAction<Identity | undefined>>;
nickname: string | undefined;
setNickname: React.Dispatch<React.SetStateAction<string | undefined>>;
}>({
identity: undefined,
setIdentity: () => undefined,
nickname: undefined,
setNickname: () => undefined,
});
export function useIdentity() {
return useContext(IdentityContext);
return useContext(IdentityContext).identity;
}
export function useSetIdentity() {
return useContext(IdentityContext).setIdentity;
}
export function useNickname() {
return useContext(IdentityContext).nickname;
}
export function useSetNikcname() {
return useContext(IdentityContext).setNickname;
}
interface IdentityProviderProps {
children: React.ReactNode;
identity: Identity | undefined;
}
export function IdentityProvider({
children,
identity,
}: IdentityProviderProps) {
return <IdentityContext.Provider value={identity} children={children} />;
export function IdentityProvider({ children }: IdentityProviderProps) {
const [identity, setIdentity] = useState<Identity | undefined>(undefined);
const [nickname, setNickname] = useState<string | undefined>(undefined);
return (
<IdentityContext.Provider
value={{ identity, setIdentity, nickname, setNickname }}
children={children}
/>
);
}

View File

@ -1,8 +1,9 @@
import React, { createContext, useContext } from "react";
import { Identity } from "status-communities/dist/cjs";
import { MessengerType, useMessenger } from "../hooks/messenger/useMessenger";
import { useIdentity, useNickname } from "./identityProvider";
const MessengerContext = createContext<MessengerType>({
messenger: undefined,
messages: [],
@ -37,14 +38,14 @@ export function useMessengerContext() {
interface MessengerProviderProps {
children: React.ReactNode;
communityKey?: string;
identity?: Identity;
}
export function MessengerProvider({
children,
communityKey,
identity,
}: MessengerProviderProps) {
const messenger = useMessenger(communityKey, identity);
const identity = useIdentity();
const nickname = useNickname();
const messenger = useMessenger(communityKey, identity, nickname);
return <MessengerContext.Provider value={messenger} children={children} />;
}

View File

@ -9,10 +9,11 @@ import { Contacts } from "../../models/Contact";
export function useContacts(
messenger: Messenger | undefined,
identity: Identity | undefined
identity: Identity | undefined,
nickname: string | undefined
) {
const [internalContacts, setInternalContacts] = useState<{
[id: string]: number;
[id: string]: { clock: number; nickname?: string };
}>({});
const contactsClass = useMemo(() => {
@ -22,9 +23,15 @@ export function useContacts(
messenger.waku,
(id, clock) => {
setInternalContacts((prev) => {
return { ...prev, [id]: clock };
return { ...prev, [id]: { ...prev[id], clock } };
});
}
},
(id, nickname) => {
setInternalContacts((prev) => {
return { ...prev, [id]: { ...prev[id], nickname } };
});
},
nickname
);
return newContacts;
}
@ -36,11 +43,11 @@ export function useContacts(
const now = Date.now();
setContacts((prev) => {
const newContacts: Contacts = {};
Object.entries(internalContacts).forEach(([id, clock]) => {
Object.entries(internalContacts).forEach(([id, { clock, nickname }]) => {
newContacts[id] = {
id,
online: clock > now - 301000,
trueName: id.slice(0, 10),
trueName: nickname ?? id.slice(0, 10),
isUntrustworthy: false,
blocked: false,
};

View File

@ -24,7 +24,7 @@ export function useLoadPrevDay(
async (id: string, groupChat?: boolean) => {
if (messenger) {
const endTime = lastLoadTime.current[id] ?? new Date();
const startTime = new Date(endTime.getTime() - _MS_PER_DAY);
const startTime = new Date(endTime.getTime() - _MS_PER_DAY * 5);
const timeDiff = Math.floor(
(new Date().getTime() - endTime.getTime()) / _MS_PER_DAY
);

View File

@ -99,7 +99,8 @@ function useCreateCommunity(
export function useMessenger(
communityKey: string | undefined,
identity: Identity | undefined
identity: Identity | undefined,
nickname: string | undefined
) {
const [activeChannel, setActiveChannel] = useState<ChannelData>({
id: "",
@ -112,7 +113,8 @@ export function useMessenger(
const { contacts, setContacts, contactsClass } = useContacts(
messenger,
identity
identity,
nickname
);
const {

View File

@ -0,0 +1,10 @@
import styled from "styled-components";
import { inputStyles } from "../components/Modals/ModalStyle";
export const NameInput = styled.input`
width: 328px;
padding: 11px 16px;
${inputStyles}
`;

View File

@ -8,6 +8,7 @@ export type Theme = {
iconColor: string;
iconUserColor: string;
iconTextColor: string;
logoColor: string;
activeChannelBackground: string;
notificationColor: string;
inputColor: string;
@ -37,6 +38,7 @@ export const lightTheme: Theme = {
iconColor: "#D37EF4",
iconUserColor: "#717199",
iconTextColor: "rgba(255, 255, 255, 0.7)",
logoColor: "#51D0F0",
activeChannelBackground: "#E9EDF1",
notificationColor: "#4360DF",
inputColor: "#EEF2F5",
@ -66,6 +68,7 @@ export const darkTheme: Theme = {
guestNameColor: "#887AF9",
iconColor: "#D37EF4",
iconUserColor: "#717199",
logoColor: "#51D0F0",
iconTextColor: "rgba(0, 0, 0, 0.7)",
activeChannelBackground: "#2C2C2C",
notificationColor: "#887AF9",

View File

@ -1,9 +1,10 @@
import { Waku, WakuMessage } from "js-waku";
import { PageDirection, 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 { ChatIdentity } from "./wire/chat_identity";
import { StatusUpdate } from "./wire/status_update";
const STATUS_BROADCAST_INTERVAL = 30000;
@ -11,7 +12,9 @@ const STATUS_BROADCAST_INTERVAL = 30000;
export class Contacts {
waku: Waku;
identity: Identity | undefined;
nickname?: string;
private callback: (publicKey: string, clock: number) => void;
private callbackNickname: (publicKey: string, nickname: string) => void;
private contacts: string[] = [];
/**
@ -30,11 +33,15 @@ export class Contacts {
public constructor(
identity: Identity | undefined,
waku: Waku,
callback: (publicKey: string, clock: number) => void
callback: (publicKey: string, clock: number) => void,
callbackNickname: (publicKey: string, nickname: string) => void,
nickname?: string
) {
this.waku = waku;
this.identity = identity;
this.nickname = nickname;
this.callback = callback;
this.callbackNickname = callbackNickname;
this.startBroadcast();
if (identity) {
this.addContact(bufToHex(identity.publicKey));
@ -66,6 +73,26 @@ export class Contacts {
endTime: now,
},
});
this.waku.store.queryHistory([idToContactCodeTopic(publicKey)], {
callback: (msgs) =>
msgs.some((e) => {
try {
if (e.payload) {
const chatIdentity = ChatIdentity.decode(e?.payload);
if (chatIdentity) {
this.callbackNickname(
publicKey,
chatIdentity?.displayName ?? ""
);
}
return true;
}
} catch {
return false;
}
}),
pageDirection: PageDirection.BACKWARD,
});
this.waku.relay.addObserver(callback, [idToContactCodeTopic(publicKey)]);
}
}
@ -84,6 +111,27 @@ export class Contacts {
this.waku.relay.send(msg);
}
};
const sendNickname = async (): Promise<void> => {
if (this.identity && this.nickname) {
const chatIdentity = new ChatIdentity({
clock: new Date().getTime(),
color: "",
description: "",
emoji: "",
images: {},
ensName: "",
displayName: this?.nickname ?? "",
});
const msg = await WakuMessage.fromBytes(
chatIdentity.encode(),
idToContactCodeTopic(bufToHex(this.identity.publicKey)),
{ sigPrivKey: this.identity.privateKey }
);
await this.waku.relay.send(msg);
}
};
sendNickname();
send();
setInterval(send, STATUS_BROADCAST_INTERVAL);
}

View File

@ -223,7 +223,7 @@ export class Messenger {
callback(messages.filter(isDefined));
}
};
console.log(`querying ${chat.contentTopic} for ${startTime} to ${endTime}`);
const allMessages = await this.waku.store.queryHistory(
[chat.contentTopic],
{
@ -231,6 +231,10 @@ export class Messenger {
callback: _callback,
}
);
console.log(
`ended querying ${chat.contentTopic} for ${startTime} to ${endTime}`
);
return allMessages.length;
}