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

View File

@ -23,8 +23,9 @@ export function ChatCreation({
const identity = useIdentity(); const identity = useIdentity();
const [query, setQuery] = useState(""); const [query, setQuery] = useState("");
const [styledGroup, setStyledGroup] = useState<string[]>( 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 { contacts, createGroupChat, addMembers } = useMessengerContext();
const setChatState = useChatState()[1]; const setChatState = useChatState()[1];

View File

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

View File

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

View File

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

View File

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

View File

@ -15,7 +15,7 @@ export function ColorChatSvg({ width, height }: ColorChatSvgProps) {
fill="none" fill="none"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
> >
<g clip-path="url(#clip0_1307_429352)"> <g clipPath="url(#clip0_1307_429352)">
<path <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" 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)" fill="url(#paint0_linear_1307_429352)"
@ -62,8 +62,8 @@ export function ColorChatSvg({ width, height }: ColorChatSvgProps) {
y2="68.3915" y2="68.3915"
gradientUnits="userSpaceOnUse" gradientUnits="userSpaceOnUse"
> >
<stop stop-color="#A7F3CE" /> <stop stopColor="#A7F3CE" />
<stop offset="1" stop-color="#61DB99" /> <stop offset="1" stopColor="#61DB99" />
</linearGradient> </linearGradient>
<linearGradient <linearGradient
id="paint1_linear_1307_429352" id="paint1_linear_1307_429352"
@ -73,8 +73,8 @@ export function ColorChatSvg({ width, height }: ColorChatSvgProps) {
y2="34.356" y2="34.356"
gradientUnits="userSpaceOnUse" gradientUnits="userSpaceOnUse"
> >
<stop stop-color="#61DB99" stop-opacity="0" /> <stop stopColor="#61DB99" stopOpacity="0" />
<stop offset="1" stop-color="#009E74" /> <stop offset="1" stopColor="#009E74" />
</linearGradient> </linearGradient>
<linearGradient <linearGradient
id="paint2_linear_1307_429352" id="paint2_linear_1307_429352"
@ -84,8 +84,8 @@ export function ColorChatSvg({ width, height }: ColorChatSvgProps) {
y2="55.5561" y2="55.5561"
gradientUnits="userSpaceOnUse" gradientUnits="userSpaceOnUse"
> >
<stop stop-color="#62E1FB" /> <stop stopColor="#62E1FB" />
<stop offset="1" stop-color="#00A2F3" /> <stop offset="1" stopColor="#00A2F3" />
</linearGradient> </linearGradient>
<linearGradient <linearGradient
id="paint3_linear_1307_429352" id="paint3_linear_1307_429352"
@ -95,8 +95,8 @@ export function ColorChatSvg({ width, height }: ColorChatSvgProps) {
y2="19.9482" y2="19.9482"
gradientUnits="userSpaceOnUse" gradientUnits="userSpaceOnUse"
> >
<stop stop-color="#00A2F3" stop-opacity="0" /> <stop stopColor="#00A2F3" stopOpacity="0" />
<stop offset="1" stop-color="#0075CD" /> <stop offset="1" stopColor="#0075CD" />
</linearGradient> </linearGradient>
<linearGradient <linearGradient
id="paint4_linear_1307_429352" id="paint4_linear_1307_429352"
@ -106,8 +106,8 @@ export function ColorChatSvg({ width, height }: ColorChatSvgProps) {
y2="19.9478" y2="19.9478"
gradientUnits="userSpaceOnUse" gradientUnits="userSpaceOnUse"
> >
<stop stop-color="#00A2F3" stop-opacity="0" /> <stop stopColor="#00A2F3" stopOpacity="0" />
<stop offset="1" stop-color="#2A353D" /> <stop offset="1" stopColor="#2A353D" />
</linearGradient> </linearGradient>
<linearGradient <linearGradient
id="paint5_linear_1307_429352" id="paint5_linear_1307_429352"
@ -117,8 +117,8 @@ export function ColorChatSvg({ width, height }: ColorChatSvgProps) {
y2="19.9479" y2="19.9479"
gradientUnits="userSpaceOnUse" gradientUnits="userSpaceOnUse"
> >
<stop stop-color="#00A2F3" stop-opacity="0" /> <stop stopColor="#00A2F3" stopOpacity="0" />
<stop offset="1" stop-color="#0075CD" /> <stop offset="1" stopColor="#0075CD" />
</linearGradient> </linearGradient>
<clipPath id="clip0_1307_429352"> <clipPath id="clip0_1307_429352">
<rect <rect

View File

@ -1,9 +1,8 @@
import React, { useMemo } from "react"; import React, { useMemo } from "react";
import { utils } from "status-communities/dist/cjs";
import { bufToHex } from "status-communities/dist/cjs/utils"; import { bufToHex } from "status-communities/dist/cjs/utils";
import styled from "styled-components"; import styled from "styled-components";
import { useIdentity } from "../../contexts/identityProvider"; import { useIdentity, useNickname } from "../../contexts/identityProvider";
import { useMessengerContext } from "../../contexts/messengerProvider"; import { useMessengerContext } from "../../contexts/messengerProvider";
import { UserIcon } from "../Icons/UserIcon"; import { UserIcon } from "../Icons/UserIcon";
@ -16,6 +15,7 @@ interface MembersListProps {
export function MembersList({ switchShowMembers }: MembersListProps) { export function MembersList({ switchShowMembers }: MembersListProps) {
const { contacts } = useMessengerContext(); const { contacts } = useMessengerContext();
const identity = useIdentity(); const identity = useIdentity();
const nickname = useNickname();
const userContacts = useMemo(() => { const userContacts = useMemo(() => {
if (identity) { if (identity) {
return Object.values(contacts).filter( return Object.values(contacts).filter(
@ -42,9 +42,7 @@ export function MembersList({ switchShowMembers }: MembersListProps) {
<MemberIcon> <MemberIcon>
<UserIcon memberView={true} /> <UserIcon memberView={true} />
</MemberIcon> </MemberIcon>
{identity && ( {identity && <MemberName>{nickname}</MemberName>}
<MemberName>{utils.bufToHex(identity.publicKey)}</MemberName>
)}
</MemberData> </MemberData>
</MemberCategory> </MemberCategory>
{onlineContacts.length > 0 && ( {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> <ContentWrapper>
<MessageHeaderWrapper> <MessageHeaderWrapper>
<UserNameWrapper> <UserNameWrapper>
<UserName> <UserName> {contact?.customName ?? contact.trueName}</UserName>
{" "} <UserAddress>
{contact?.customName ?? message.sender.slice(0, 10)} {message.sender.slice(0, 5)}...{message.sender.slice(-3)}
</UserName> </UserAddress>
{contact?.customName && (
<UserAddress>
{message.sender.slice(0, 5)}...{message.sender.slice(-3)}
</UserAddress>
)}
{contact.isUntrustworthy && <UntrustworthIcon />} {contact.isUntrustworthy && <UntrustworthIcon />}
</UserNameWrapper> </UserNameWrapper>
<TimeWrapper>{message.date.toLocaleString()}</TimeWrapper> <TimeWrapper>{message.date.toLocaleString()}</TimeWrapper>

View File

@ -1,5 +1,6 @@
import styled, { css } from "styled-components"; import styled, { css } from "styled-components";
import { buttonStyles } from "../Buttons/buttonStyle";
import { textMediumStyles } from "../Text"; import { textMediumStyles } from "../Text";
export const Section = styled.div` export const Section = styled.div`
@ -23,6 +24,36 @@ export const Text = styled.p`
${textMediumStyles} ${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)` export const ButtonSection = styled(Section)`
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;

View File

@ -7,6 +7,7 @@ import { useIdentity } from "../../contexts/identityProvider";
import { useModal } from "../../contexts/modalProvider"; import { useModal } from "../../contexts/modalProvider";
import { useToasts } from "../../contexts/toastProvider"; import { useToasts } from "../../contexts/toastProvider";
import { useManageContact } from "../../hooks/useManageContact"; import { useManageContact } from "../../hooks/useManageContact";
import { NameInput } from "../../styles/Inputs";
import { copy } from "../../utils"; import { copy } from "../../utils";
import { buttonStyles } from "../Buttons/buttonStyle"; import { buttonStyles } from "../Buttons/buttonStyle";
import { ClearSvg } from "../Icons/ClearIcon"; import { ClearSvg } from "../Icons/ClearIcon";
@ -19,6 +20,8 @@ import { textMediumStyles, textSmallStyles } from "../Text";
import { Modal } from "./Modal"; import { Modal } from "./Modal";
import { import {
BackBtn,
Btn,
ButtonSection, ButtonSection,
Heading, Heading,
Hint, Hint,
@ -84,7 +87,7 @@ export const ProfileModal = () => {
className={`${!requestCreation && "profile"}`} className={`${!requestCreation && "profile"}`}
> >
<Section> <Section>
<Heading>{id.slice(0, 10)}s Profile</Heading> <Heading>{contact.trueName}s Profile</Heading>
</Section> </Section>
<ProfileSection> <ProfileSection>
@ -101,7 +104,7 @@ export const ProfileModal = () => {
)} )}
<UserNameWrapper> <UserNameWrapper>
<UserName className={`${requestCreation && "small"}`}> <UserName className={`${requestCreation && "small"}`}>
{contact?.customName ?? id.slice(0, 10)} {contact?.customName ?? contact.trueName}
</UserName> </UserName>
{contact.isUntrustworthy && <UntrustworthIcon />} {contact.isUntrustworthy && <UntrustworthIcon />}
{!renaming && ( {!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` const ClearBtn = styled.button`
position: absolute; position: absolute;
right: 16px; right: 16px;
@ -448,13 +421,6 @@ const NameInputWrapper = styled.div`
position: relative; position: relative;
`; `;
const NameInput = styled.input`
width: 328px;
padding: 11px 16px;
${inputStyles}
`;
const RequestSection = styled.div` const RequestSection = styled.div`
width: 100%; width: 100%;
display: flex; 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 React from "react";
import styled from "styled-components"; import styled from "styled-components";
import { import { useModal } from "../../contexts/modalProvider";
UserCreationState,
useUserCreationState,
} from "../../contexts/userCreationStateProvider";
import { ColorChatIcon } from "../Icons/ColorChatIcon"; import { ColorChatIcon } from "../Icons/ColorChatIcon";
import { UserCreationModalName } from "../Modals/UserCreationModal";
export function UserCreation() { export function UserCreation() {
const state = useUserCreationState(); const { setModal } = useModal(UserCreationModalName);
return ( return (
<Background> <Background>
<Wrapper> <Wrapper>
@ -16,7 +14,7 @@ export function UserCreation() {
<ColorChatIcon /> <ColorChatIcon />
</IconWrapper> </IconWrapper>
<TitleWrapper>Want in on the discussion ?</TitleWrapper> <TitleWrapper>Want in on the discussion ?</TitleWrapper>
<ThrowAwayButton onClick={() => state[1](UserCreationState.Creating)}> <ThrowAwayButton onClick={() => setModal(true)}>
Use a throwaway account Use a throwaway account
</ThrowAwayButton> </ThrowAwayButton>
</Wrapper> </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"; 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() { 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 { interface IdentityProviderProps {
children: React.ReactNode; children: React.ReactNode;
identity: Identity | undefined;
} }
export function IdentityProvider({ export function IdentityProvider({ children }: IdentityProviderProps) {
children, const [identity, setIdentity] = useState<Identity | undefined>(undefined);
identity, const [nickname, setNickname] = useState<string | undefined>(undefined);
}: IdentityProviderProps) {
return <IdentityContext.Provider value={identity} children={children} />; return (
<IdentityContext.Provider
value={{ identity, setIdentity, nickname, setNickname }}
children={children}
/>
);
} }

View File

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

View File

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

View File

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

View File

@ -99,7 +99,8 @@ function useCreateCommunity(
export function useMessenger( export function useMessenger(
communityKey: string | undefined, communityKey: string | undefined,
identity: Identity | undefined identity: Identity | undefined,
nickname: string | undefined
) { ) {
const [activeChannel, setActiveChannel] = useState<ChannelData>({ const [activeChannel, setActiveChannel] = useState<ChannelData>({
id: "", id: "",
@ -112,7 +113,8 @@ export function useMessenger(
const { contacts, setContacts, contactsClass } = useContacts( const { contacts, setContacts, contactsClass } = useContacts(
messenger, messenger,
identity identity,
nickname
); );
const { 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; iconColor: string;
iconUserColor: string; iconUserColor: string;
iconTextColor: string; iconTextColor: string;
logoColor: string;
activeChannelBackground: string; activeChannelBackground: string;
notificationColor: string; notificationColor: string;
inputColor: string; inputColor: string;
@ -37,6 +38,7 @@ export const lightTheme: Theme = {
iconColor: "#D37EF4", iconColor: "#D37EF4",
iconUserColor: "#717199", iconUserColor: "#717199",
iconTextColor: "rgba(255, 255, 255, 0.7)", iconTextColor: "rgba(255, 255, 255, 0.7)",
logoColor: "#51D0F0",
activeChannelBackground: "#E9EDF1", activeChannelBackground: "#E9EDF1",
notificationColor: "#4360DF", notificationColor: "#4360DF",
inputColor: "#EEF2F5", inputColor: "#EEF2F5",
@ -66,6 +68,7 @@ export const darkTheme: Theme = {
guestNameColor: "#887AF9", guestNameColor: "#887AF9",
iconColor: "#D37EF4", iconColor: "#D37EF4",
iconUserColor: "#717199", iconUserColor: "#717199",
logoColor: "#51D0F0",
iconTextColor: "rgba(0, 0, 0, 0.7)", iconTextColor: "rgba(0, 0, 0, 0.7)",
activeChannelBackground: "#2C2C2C", activeChannelBackground: "#2C2C2C",
notificationColor: "#887AF9", 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 { idToContactCodeTopic } from "./contentTopic";
import { Identity } from "./identity"; import { Identity } from "./identity";
import { StatusUpdate_StatusType } from "./proto/communities/v1/status_update"; import { StatusUpdate_StatusType } from "./proto/communities/v1/status_update";
import { bufToHex } from "./utils"; import { bufToHex } from "./utils";
import { ChatIdentity } from "./wire/chat_identity";
import { StatusUpdate } from "./wire/status_update"; import { StatusUpdate } from "./wire/status_update";
const STATUS_BROADCAST_INTERVAL = 30000; const STATUS_BROADCAST_INTERVAL = 30000;
@ -11,7 +12,9 @@ const STATUS_BROADCAST_INTERVAL = 30000;
export class Contacts { export class Contacts {
waku: Waku; waku: Waku;
identity: Identity | undefined; identity: Identity | undefined;
nickname?: string;
private callback: (publicKey: string, clock: number) => void; private callback: (publicKey: string, clock: number) => void;
private callbackNickname: (publicKey: string, nickname: string) => void;
private contacts: string[] = []; private contacts: string[] = [];
/** /**
@ -30,11 +33,15 @@ export class Contacts {
public constructor( public constructor(
identity: Identity | undefined, identity: Identity | undefined,
waku: Waku, 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.waku = waku;
this.identity = identity; this.identity = identity;
this.nickname = nickname;
this.callback = callback; this.callback = callback;
this.callbackNickname = callbackNickname;
this.startBroadcast(); this.startBroadcast();
if (identity) { if (identity) {
this.addContact(bufToHex(identity.publicKey)); this.addContact(bufToHex(identity.publicKey));
@ -66,6 +73,26 @@ export class Contacts {
endTime: now, 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)]); this.waku.relay.addObserver(callback, [idToContactCodeTopic(publicKey)]);
} }
} }
@ -84,6 +111,27 @@ export class Contacts {
this.waku.relay.send(msg); 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(); send();
setInterval(send, STATUS_BROADCAST_INTERVAL); setInterval(send, STATUS_BROADCAST_INTERVAL);
} }

View File

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