From 9c74cf4685fc47317886405ea5548a6c1d3b96be Mon Sep 17 00:00:00 2001 From: Pavel <14926950+prichodko@users.noreply.github.com> Date: Tue, 12 Apr 2022 15:51:49 +0200 Subject: [PATCH] Dialogs (#247) * Add community dialog * feat(react): add welcome dialog * feat(react): add connect wallet dialog * feat(react): add disconnect dialog * feat(react): add create profile dialog * feat(react): add welcome dialog * feat(react): add load throwaway profile dialog * feat(react): add sync status profile dialog * fix(react): disconnect dialog spacing * feat(react): add get started section to main sidebar * feat(react): add user profile dialog * feat(react): add edit group dialog * feat(react): support opening dialogs programmatically * feat(react): delete legacy components --- .../src/components/Modals/AgreementModal.tsx | 158 ------- .../src/components/Modals/CoinbaseModal.tsx | 18 - .../src/components/Modals/CommunityModal.tsx | 75 ---- .../src/components/Modals/ConnectModal.tsx | 32 -- .../src/components/Modals/EditModal.tsx | 158 ------- .../src/components/Modals/LeavingModal.tsx | 47 -- .../src/components/Modals/LinkModal.tsx | 48 -- .../src/components/Modals/LogoutModal.tsx | 100 ----- .../src/components/Modals/Modal.tsx | 115 ----- .../src/components/Modals/ModalStyle.tsx | 85 ---- .../src/components/Modals/PictureModal.tsx | 32 -- .../components/Modals/ProfileFoundModal.tsx | 131 ------ .../src/components/Modals/ProfileModal.tsx | 415 ------------------ .../src/components/Modals/SizeLimitModal.tsx | 21 - .../src/components/Modals/StatusModal.tsx | 89 ---- .../components/Modals/UserCreationModal.tsx | 214 --------- .../Modals/UserCreationStartModal.tsx | 26 -- .../components/Modals/WalletConnectModal.tsx | 19 - .../src/components/Modals/WalletModal.tsx | 165 ------- .../chat-menu/edit-group-chat-dialog.tsx | 21 + .../src/components/chat-menu/index.tsx | 121 +++-- .../create-profile-dialog/index.tsx | 63 +++ .../community-info/community-dialog.tsx | 60 +++ .../get-started/connect-wallet-dialog.tsx | 68 +++ .../components/get-started/index.tsx | 166 +++++++ .../sync-status-profile-dialog.tsx | 70 +++ .../throwaway-profile-found-dialog.tsx | 47 ++ .../src/components/main-sidebar/index.tsx | 1 + .../member-sidebar/disconnect-dialog.tsx | 28 ++ .../components/member-sidebar/user-item.tsx | 40 +- .../components/user-profile-dialog/index.tsx | 28 ++ .../src/components/welcome-dialog/index.tsx | 46 ++ .../src/contexts/dialog-context.tsx | 36 ++ .../src/modules/community/index.tsx | 51 ++- .../src/modules/community/messenger.tsx | 34 -- .../src/system/dialog/alert-dialog.tsx | 54 ++- .../status-react/src/system/dialog/dialog.tsx | 30 +- .../status-react/src/system/dialog/index.tsx | 4 +- .../status-react/src/system/dialog/styles.tsx | 8 +- packages/status-react/src/system/index.tsx | 2 + 40 files changed, 838 insertions(+), 2088 deletions(-) delete mode 100644 packages/status-react/src/components/Modals/AgreementModal.tsx delete mode 100644 packages/status-react/src/components/Modals/CoinbaseModal.tsx delete mode 100644 packages/status-react/src/components/Modals/CommunityModal.tsx delete mode 100644 packages/status-react/src/components/Modals/ConnectModal.tsx delete mode 100644 packages/status-react/src/components/Modals/EditModal.tsx delete mode 100644 packages/status-react/src/components/Modals/LeavingModal.tsx delete mode 100644 packages/status-react/src/components/Modals/LinkModal.tsx delete mode 100644 packages/status-react/src/components/Modals/LogoutModal.tsx delete mode 100644 packages/status-react/src/components/Modals/Modal.tsx delete mode 100644 packages/status-react/src/components/Modals/ModalStyle.tsx delete mode 100644 packages/status-react/src/components/Modals/PictureModal.tsx delete mode 100644 packages/status-react/src/components/Modals/ProfileFoundModal.tsx delete mode 100644 packages/status-react/src/components/Modals/ProfileModal.tsx delete mode 100644 packages/status-react/src/components/Modals/SizeLimitModal.tsx delete mode 100644 packages/status-react/src/components/Modals/StatusModal.tsx delete mode 100644 packages/status-react/src/components/Modals/UserCreationModal.tsx delete mode 100644 packages/status-react/src/components/Modals/UserCreationStartModal.tsx delete mode 100644 packages/status-react/src/components/Modals/WalletConnectModal.tsx delete mode 100644 packages/status-react/src/components/Modals/WalletModal.tsx create mode 100644 packages/status-react/src/components/chat-menu/edit-group-chat-dialog.tsx create mode 100644 packages/status-react/src/components/create-profile-dialog/index.tsx create mode 100644 packages/status-react/src/components/main-sidebar/components/community-info/community-dialog.tsx create mode 100644 packages/status-react/src/components/main-sidebar/components/get-started/connect-wallet-dialog.tsx create mode 100644 packages/status-react/src/components/main-sidebar/components/get-started/index.tsx create mode 100644 packages/status-react/src/components/main-sidebar/components/get-started/sync-status-profile-dialog.tsx create mode 100644 packages/status-react/src/components/main-sidebar/components/get-started/throwaway-profile-found-dialog.tsx create mode 100644 packages/status-react/src/components/member-sidebar/disconnect-dialog.tsx create mode 100644 packages/status-react/src/components/user-profile-dialog/index.tsx create mode 100644 packages/status-react/src/components/welcome-dialog/index.tsx create mode 100644 packages/status-react/src/contexts/dialog-context.tsx diff --git a/packages/status-react/src/components/Modals/AgreementModal.tsx b/packages/status-react/src/components/Modals/AgreementModal.tsx deleted file mode 100644 index 65001b87..00000000 --- a/packages/status-react/src/components/Modals/AgreementModal.tsx +++ /dev/null @@ -1,158 +0,0 @@ -import React, { useState } from 'react' - -import HCaptcha from '@hcaptcha/react-hcaptcha' -import styled, { useTheme } from 'styled-components' - -import { useMessengerContext } from '../../contexts/messengerProvider' -import { useModal } from '../../contexts/modalProvider' -import { lightTheme } from '../../styles/themes' -import { Logo } from '../CommunityIdentity' -import { textMediumStyles } from '../Text' -import { Modal } from './Modal' -import { Btn, ButtonSection, Heading, Section, Text } from './ModalStyle' - -import type { Theme } from '../../styles/themes' - -export const AgreementModalName = 'AgreementModal' - -export function AgreementModal() { - const theme = useTheme() as Theme - const { communityData } = useMessengerContext() - const { setModal } = useModal(AgreementModalName) - - const [checked, setChecked] = useState(false) - const [token, setToken] = useState('') - - return ( - -
- Welcome to {communityData?.name} -
-
- - - {' '} - {communityData?.icon === undefined && - communityData?.name.slice(0, 1).toUpperCase()} - - - - {communityData?.description} - - - - setChecked(e.target.checked)} - required - /> - I agree with the above - - -
- - -
-
- - { - setModal(false) - }} - disabled={!token || !checked} - > - Join {communityData?.name} - - -
- ) -} - -const LogoWrapper = styled.div` - display: flex; - justify-content: center; - margin-bottom: 24px; -` - -const CommunityLogo = styled(Logo)` - width: 64px; - height: 64px; -` - -const AgreementSection = styled.div` - margin-bottom: 24px; -` - -const Agreements = styled.div` - display: flex; - justify-content: center; - align-items: center; -` - -const Agreement = styled.label` - display: flex; - align-items: center; - position: relative; - color: ${({ theme }) => theme.primary}; - padding-left: 26px; - margin-right: 48px; - - ${textMediumStyles} - - & input:checked ~ span { - background-color: ${({ theme }) => theme.tertiary}; - border: 1px solid ${({ theme }) => theme.tertiary}; - border-radius: 2px; - } - - & input:checked ~ span:after { - display: block; - } -` - -const AgreementInput = styled.input` - position: absolute; - opacity: 0; - height: 0; - width: 0; -` - -const Checkmark = styled.span` - position: absolute; - top: 2px; - left: 0; - width: 18px; - height: 18px; - - background-color: ${({ theme }) => theme.inputColor}; - border: 1px solid ${({ theme }) => theme.inputColor}; - border-radius: 2px; - margin: 0 8px 0 0; - - &:after { - content: ''; - position: absolute; - display: none; - - left: 5px; - top: 1px; - width: 4px; - height: 9px; - border: solid ${({ theme }) => theme.bodyBackgroundColor}; - border-width: 0 2px 2px 0; - transform: rotate(45deg); - } -` diff --git a/packages/status-react/src/components/Modals/CoinbaseModal.tsx b/packages/status-react/src/components/Modals/CoinbaseModal.tsx deleted file mode 100644 index 3b880ace..00000000 --- a/packages/status-react/src/components/Modals/CoinbaseModal.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import React from 'react' - -import { ConnectModal } from './ConnectModal' -import { Modal } from './Modal' - -export const CoinbaseModalName = 'CoinbaseModal' - -export function CoinbaseModal() { - return ( - - - - ) -} diff --git a/packages/status-react/src/components/Modals/CommunityModal.tsx b/packages/status-react/src/components/Modals/CommunityModal.tsx deleted file mode 100644 index f1ce87b6..00000000 --- a/packages/status-react/src/components/Modals/CommunityModal.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import React from 'react' - -import styled from 'styled-components' - -import { useMessengerContext } from '../../contexts/messengerProvider' -import { useNarrow } from '../../contexts/narrowProvider' -import { DownloadButton } from '../Buttons/DownloadButton' -import { CommunityIdentity } from '../CommunityIdentity' -import { CopyInput } from '../Form/CopyInput' -import { StatusLogo } from '../Icons/StatusLogo' -import { textSmallStyles } from '../Text' -import { Modal } from './Modal' -import { Section, Text } from './ModalStyle' - -import type { CommunityIdentityProps } from '../CommunityIdentity' - -export const CommunityModalName = 'CommunityModal' - -type CommunityModalProps = CommunityIdentityProps - -export const CommunityModal = ({ subtitle }: CommunityModalProps) => { - const narrow = useNarrow() - const { communityData } = useMessengerContext() - return ( - -
- -
-
- {communityData?.description} -
-
- - - To access this community, paste community public key in Status desktop - or mobile app. - {narrow && } - -
- {!narrow && ( - - - - - )} -
- ) -} - -const BottomSection = styled(Section)` - display: flex; - flex-direction: column; - align-items: center; -` - -const StyledDownloadButton = styled(DownloadButton)` - display: inline; - padding: 0; - margin-left: 4px; - background: none; - font-size: 13px; - line-height: 18px; - text-decoration: underline; - color: ${({ theme }) => theme.secondary}; -` - -const Hint = styled.p` - margin-top: 16px; - color: ${({ theme }) => theme.secondary}; - - ${textSmallStyles} -` diff --git a/packages/status-react/src/components/Modals/ConnectModal.tsx b/packages/status-react/src/components/Modals/ConnectModal.tsx deleted file mode 100644 index e2fc89b2..00000000 --- a/packages/status-react/src/components/Modals/ConnectModal.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import React from 'react' - -import QRCode from 'qrcode.react' - -import { CopyInput } from '../Form/CopyInput' -import { Heading, MiddleSection, QRWrapper, Section, Text } from './ModalStyle' - -export const ConnectModalName = 'ConnectModal' - -interface ConnectModalProps { - name: string - address: string - text: string -} - -export function ConnectModal({ name, address, text }: ConnectModalProps) { - return ( - <> -
- Connect with {name} -
- - {text} - - {' '} - - - - - - ) -} diff --git a/packages/status-react/src/components/Modals/EditModal.tsx b/packages/status-react/src/components/Modals/EditModal.tsx deleted file mode 100644 index c405cef2..00000000 --- a/packages/status-react/src/components/Modals/EditModal.tsx +++ /dev/null @@ -1,158 +0,0 @@ -import React, { useState } from 'react' - -import styled from 'styled-components' - -import { useMessengerContext } from '../../contexts/messengerProvider' -import { useModal } from '../../contexts/modalProvider' -import { buttonStyles } from '../Buttons/buttonStyle' -import { ChannelLogo } from '../Channels/ChannelIcon' -import { inputStyles } from '../Form/inputStyles' -import { AddIcon } from '../Icons/AddIcon' -import { textMediumStyles } from '../Text' -import { Modal } from './Modal' -import { AddWrapper, ButtonSection, Heading, Hint, Section } from './ModalStyle' - -export const EditModalName = 'editModal' - -export const EditModal = () => { - const { activeChannel, changeGroupChatName } = useMessengerContext() - const { setModal } = useModal(EditModalName) - - const [groupName, setGroupName] = useState('') - const [image, setImage] = useState('') - - const handleChange = (e: React.FormEvent) => { - if (e.currentTarget?.files?.length) { - setImage(URL.createObjectURL(e.currentTarget.files[0])) - } - } - - const handleUpload = () => { - if (activeChannel) { - if (image) { - activeChannel.icon = image // Need function to send image to waku - setImage('') - } - if (groupName) { - changeGroupChatName(groupName, activeChannel.id) - setGroupName('') - } - setModal(false) - } - } - - return ( - -
- Edit group name and image -
- -
- - - - {groupName.length}/30 - - - setGroupName(e.currentTarget.value)} - /> - - - - - {!activeChannel?.icon && - !image && - activeChannel?.name?.slice(0, 1)?.toUpperCase()} - {image && } - - - - - - -
- - Save changes - -
- ) -} - -const NameSection = styled.div` - display: flex; - flex-direction: column; - margin-bottom: 16px; -` - -const LabelGroup = styled.div` - display: flex; - justify-content: space-between; - align-items: center; -` - -const Label = styled.p` - color: ${({ theme }) => theme.primary}; - padding: 10px 0; - - ${textMediumStyles} -` - -const NameInput = styled.input` - padding: 14px 70px 14px 8px; - - ${inputStyles} -` - -const LogoSection = styled.div` - display: flex; - flex-direction: column; - justify-content: center; - margin-bottom: 8px; -` - -const GroupLogo = styled(ChannelLogo)` - width: 128px; - height: 128px; - font-weight: bold; - font-size: 80px; - position: relative; - align-self: center; - margin-right: 0; -` - -const LogoPreview = styled.img` - width: 128px; - height: 128px; - border-radius: 50%; -` - -const AddPictureInputWrapper = styled(AddWrapper)` - top: 0; - right: 8px; -` - -const AddPictureInput = styled.input` - position: absolute; - top: 0; - right: 0; - width: 40px; - height: 40px; - opacity: 0; - z-index: 2; - cursor: pointer; -` - -const SaveBtn = styled.button` - padding: 11px 24px; - - ${buttonStyles} -` diff --git a/packages/status-react/src/components/Modals/LeavingModal.tsx b/packages/status-react/src/components/Modals/LeavingModal.tsx deleted file mode 100644 index 45e5b087..00000000 --- a/packages/status-react/src/components/Modals/LeavingModal.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import React from 'react' - -import { useMessengerContext } from '../../contexts/messengerProvider' -import { useModal } from '../../contexts/modalProvider' -import { ButtonNo } from '../Buttons/buttonStyle' -import { Modal } from './Modal' -import { ButtonSection, Heading, Section, Text } from './ModalStyle' - -export const LeavingModalName = 'LeavingModal' - -export const LeavingModal = () => { - const { setModal } = useModal(LeavingModalName) - const { activeChannel, removeChannel } = useMessengerContext() - - if (activeChannel) - return ( - -
- - {activeChannel.type === 'dm' ? 'Delete chat' : 'Leave group'} - -
-
- - Are you sure you want to{' '} - {activeChannel.type === 'dm' - ? 'delete this chat' - : 'leave this group'} - ? - -
- - { - removeChannel(activeChannel.id) - setModal(false) - }} - > - {activeChannel.type === 'dm' ? 'Delete' : 'Leave'} - - -
- ) - else { - return null - } -} diff --git a/packages/status-react/src/components/Modals/LinkModal.tsx b/packages/status-react/src/components/Modals/LinkModal.tsx deleted file mode 100644 index ee95dfbc..00000000 --- a/packages/status-react/src/components/Modals/LinkModal.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import React from 'react' - -import styled from 'styled-components' - -import { useModal } from '../../contexts/modalProvider' -import { ButtonNo, ButtonYes } from '../Buttons/buttonStyle' -import { textMediumStyles } from '../Text' -import { Modal } from './Modal' -import { ButtonSection, Heading, Section } from './ModalStyle' - -export const LinkModalName = 'LinkModal' - -interface LinkModalProps { - link: string -} - -export const LinkModal = ({ link }: LinkModalProps) => { - const { setModal } = useModal(LinkModalName) - return ( - -
- Are you sure you want to visit this website? -
-
- {link} -
- - setModal(false)}>No - { - window?.open(link, '_blank', 'noopener')?.focus() - setModal(false) - }} - > - Yes, take me there - - -
- ) -} - -const Link = styled.a` - text-decoration: none; - word-break: break-all; - color: ${({ theme }) => theme.primary}; - - ${textMediumStyles} -` diff --git a/packages/status-react/src/components/Modals/LogoutModal.tsx b/packages/status-react/src/components/Modals/LogoutModal.tsx deleted file mode 100644 index dd0a932a..00000000 --- a/packages/status-react/src/components/Modals/LogoutModal.tsx +++ /dev/null @@ -1,100 +0,0 @@ -import React from 'react' - -import styled from 'styled-components' - -import { - useSetIdentity, - useSetNikcname, - useUserPublicKey, -} from '../../contexts/identityProvider' -import { useMessengerContext } from '../../contexts/messengerProvider' -import { useModal } from '../../contexts/modalProvider' -import { ButtonNo, ButtonYes } from '../Buttons/buttonStyle' -import { UserLogo } from '../Members/UserLogo' -import { Modal } from './Modal' -import { ButtonSection, Heading, Section, Text } from './ModalStyle' -import { - EmojiKey, - UserAddress, - UserAddressWrapper, - UserName, - UserNameWrapper, -} from './ProfileModal' - -export const LogoutModalName = 'LogoutModal' - -export const LogoutModal = () => { - const { setModal } = useModal(LogoutModalName) - const logout = useSetIdentity() - const setNickname = useSetNikcname() - const userPK = useUserPublicKey() - const { nickname } = useMessengerContext() - - if (userPK) { - return ( - -
- Disconnect -
-
- Do you want to disconnect your profile? - - - - {' '} - {nickname} - - - - - {' '} - Chatkey: {userPK.slice(0, 10)}... - {userPK.slice(-3)}{' '} - - - 🎩🍞πŸ₯‘πŸ¦πŸŒˆπŸ“‘πŸ’…πŸ»β™£οΈπŸ””β›ΈπŸ‘΅πŸ…± - -
- - { - setModal(false) - logout(undefined) - setNickname(undefined) - }} - > - Disconnect - - { - setModal(false) - }} - > - Stay Connected - - -
- ) - } - - return null -} - -const UserSection = styled.div` - display: flex; - flex-direction: column; - align-items: center; - margin: 8px 0; -` diff --git a/packages/status-react/src/components/Modals/Modal.tsx b/packages/status-react/src/components/Modals/Modal.tsx deleted file mode 100644 index 3ce7351f..00000000 --- a/packages/status-react/src/components/Modals/Modal.tsx +++ /dev/null @@ -1,115 +0,0 @@ -import React, { useCallback, useEffect } from 'react' -import { createPortal } from 'react-dom' - -import styled from 'styled-components' - -import { useModal } from '../../contexts/modalProvider' -import { CrossIcon } from '../Icons/CrossIcon' - -import type { ReactNode } from 'react' - -export interface BasicModalProps { - name: string - className?: string -} - -export interface ModalProps extends BasicModalProps { - children: ReactNode -} - -export const Modal = ({ name, children, className }: ModalProps) => { - const { isVisible, setModal } = useModal(name) - - const listenKeyboard = useCallback( - (event: KeyboardEvent) => { - if (event.key === 'Escape' || event.keyCode === 27) { - setModal(false) - } - }, - [setModal] - ) - - useEffect(() => { - if (isVisible) { - window.addEventListener('keydown', listenKeyboard, true) - return () => { - window.removeEventListener('keydown', listenKeyboard, true) - } - } - }, [isVisible, listenKeyboard]) - - if (!isVisible) return null - - const element = document.getElementById('modal-root') - - if (element) { - return createPortal( - - setModal(false)} /> - - setModal(false)} className={className}> - - - {children} - - , - element - ) - } - return null -} - -const ModalView = styled.div` - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - width: 100%; - height: 100%; - z-index: 9999; -` - -const ModalBody = styled.div` - position: absolute; - top: 50%; - left: 50%; - max-width: 480px; - width: 100%; - transform: translate(-50%, -50%); - background: ${({ theme }) => theme.bodyBackgroundColor}; - border-radius: 8px; - overflow-y: auto; - - &.picture { - max-width: 820px; - border-radius: 0; - } - - &.wide { - max-width: 640px; - } -` - -const ModalOverlay = styled.div` - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - width: 100%; - height: 100%; - background: ${({ theme }) => theme.primary}; - opacity: 0.4; -` - -const CloseButton = styled.button` - position: absolute; - top: 12px; - right: 12px; - padding: 10px; - - &.picture { - display: none; - } -` diff --git a/packages/status-react/src/components/Modals/ModalStyle.tsx b/packages/status-react/src/components/Modals/ModalStyle.tsx deleted file mode 100644 index f28c6e12..00000000 --- a/packages/status-react/src/components/Modals/ModalStyle.tsx +++ /dev/null @@ -1,85 +0,0 @@ -import styled from 'styled-components' - -import { buttonStyles } from '../Buttons/buttonStyle' -import { textMediumStyles } from '../Text' - -export const Section = styled.div` - padding: 16px; - - & + & { - border-top: 1px solid ${({ theme }) => theme.border}; - } -` - -export const MiddleSection = styled(Section)` - display: flex; - flex-direction: column; - align-items: center; -` - -export const Heading = styled.p` - color: ${({ theme }) => theme.primary}; - font-weight: bold; - font-size: 17px; - line-height: 24px; -` - -export const Text = styled.p` - color: ${({ theme }) => theme.primary}; - - ${textMediumStyles} -` - -export const Btn = styled.button` - padding: 11px 24px; - margin-left: 8px; - ${buttonStyles} - - &: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; - align-items: center; - position: relative; -` - -export const Hint = styled.p` - color: ${({ theme }) => theme.secondary}; - font-size: 12px; - line-height: 16px; -` - -export const AddWrapper = styled.div` - display: flex; - justify-content: center; - align-items: center; - position: absolute; - width: 40px; - height: 40px; - background: ${({ theme }) => theme.tertiary}; - border-radius: 50%; -` - -export const QRWrapper = styled.div` - margin: 30px 0; -` diff --git a/packages/status-react/src/components/Modals/PictureModal.tsx b/packages/status-react/src/components/Modals/PictureModal.tsx deleted file mode 100644 index 3b38ff7a..00000000 --- a/packages/status-react/src/components/Modals/PictureModal.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import React from 'react' - -import styled from 'styled-components' - -import { Modal } from './Modal' - -export const PictureModalName = 'PictureModal' as const - -export interface PictureModalProps { - image: string -} - -export const PictureModal = ({ image }: PictureModalProps) => { - return ( - - - - - - ) -} - -const ModalImageWrapper = styled.div` - display: flex; - max-width: 820px; - max-height: 820px; -` - -const ModalImage = styled.img` - width: 100%; - height: 100%; -` diff --git a/packages/status-react/src/components/Modals/ProfileFoundModal.tsx b/packages/status-react/src/components/Modals/ProfileFoundModal.tsx deleted file mode 100644 index a4481f17..00000000 --- a/packages/status-react/src/components/Modals/ProfileFoundModal.tsx +++ /dev/null @@ -1,131 +0,0 @@ -import React, { useEffect, useMemo, useState } from 'react' - -import { bufToHex } from '@status-im/core' -import styled from 'styled-components' - -import { useNickname, useSetIdentity } from '../../contexts/identityProvider' -import { useModal } from '../../contexts/modalProvider' -import { decryptIdentity, loadEncryptedIdentity } from '../../utils' -import { buttonTransparentStyles } from '../Buttons/buttonStyle' -import { UserLogo } from '../Members/UserLogo' -import { textMediumStyles } from '../Text' -import { Modal } from './Modal' -import { - Btn, - ButtonSection, - Heading, - MiddleSection, - Section, - Text, -} from './ModalStyle' -import { - EmojiKey, - UserAddress, - UserAddressWrapper, - UserName, -} from './ProfileModal' -import { UserCreationModalName } from './UserCreationModal' - -import type { Identity } from '@status-im/core' - -export const ProfileFoundModalName = 'ProfileFoundModal' - -export function ProfileFoundModal() { - const { setModal } = useModal(ProfileFoundModalName) - const { setModal: setCreationModal } = useModal(UserCreationModalName) - - const setIdentity = useSetIdentity() - const encryptedIdentity = useMemo(() => loadEncryptedIdentity(), []) - const nickname = useNickname() - - const [decryptedIdentity, setDecryptedIdentity] = useState< - Identity | undefined - >(undefined) - - useEffect(() => { - if (encryptedIdentity) - (async () => { - setDecryptedIdentity( - await decryptIdentity(encryptedIdentity, 'noPassword') - ) - })() - }, [encryptedIdentity]) - - if (decryptedIdentity) { - return ( - -
- Throwaway Profile found -
- - - - {nickname} - - - - {' '} - Chatkey: {decryptedIdentity.privateKey.slice(0, 10)}... - {decryptedIdentity.privateKey.slice(-3)}{' '} - - - 🎩🍞πŸ₯‘πŸ¦πŸŒˆπŸ“‘πŸ’…πŸ»β™£οΈπŸ””β›ΈπŸ‘΅πŸ…± - - - Throwaway Profile is found in your local browser’s cache. Would you - like to load it and use it?{' '} - - - - { - setCreationModal(true) - setModal(false) - }} - > - Skip - - { - setIdentity(decryptedIdentity) - setModal(false) - }} - > - Load Throwaway Profile - - -
- ) - } else { - return null - } -} - -const Logo = styled(UserLogo)` - margin-bottom: 8px; -` - -const Name = styled(UserName)` - margin-bottom: 8px; -` - -const EmojiKeyBlock = styled(EmojiKey)` - margin-bottom: 24px; -` - -const SkipBtn = styled.button` - ${buttonTransparentStyles} - ${textMediumStyles} -` diff --git a/packages/status-react/src/components/Modals/ProfileModal.tsx b/packages/status-react/src/components/Modals/ProfileModal.tsx deleted file mode 100644 index a905f93d..00000000 --- a/packages/status-react/src/components/Modals/ProfileModal.tsx +++ /dev/null @@ -1,415 +0,0 @@ -import React, { useEffect, useMemo, useState } from 'react' - -import styled from 'styled-components' - -import { useUserPublicKey } from '../../contexts/identityProvider' -import { useMessengerContext } from '../../contexts/messengerProvider' -import { useModal } from '../../contexts/modalProvider' -import { useToasts } from '../../contexts/toastProvider' -import { copy } from '../../utils' -import { buttonStyles } from '../Buttons/buttonStyle' -import { - ClearBtn, - inputStyles, - NameInput, - NameInputWrapper, -} from '../Form/inputStyles' -import { ClearSvgFull } from '../Icons/ClearIconFull' -import { CopyIcon } from '../Icons/CopyIcon' -import { EditIcon } from '../Icons/EditIcon' -import { LeftIcon } from '../Icons/LeftIcon' -import { UntrustworthIcon } from '../Icons/UntrustworthIcon' -import { UserIcon } from '../Icons/UserIcon' -import { textMediumStyles, textSmallStyles } from '../Text' -import { Modal } from './Modal' -import { - BackBtn, - Btn, - ButtonSection, - Heading, - Hint, - Section, -} from './ModalStyle' - -export const ProfileModalName = 'profileModal' as const - -export type ProfileModalProps = { - id: string - image?: string - renamingState?: boolean - requestState?: boolean -} - -export const ProfileModal = () => { - const { props } = useModal(ProfileModalName) - const { id, image, renamingState, requestState } = useMemo( - () => (props ? props : { id: '' }), - [props] - ) - - const { setToasts } = useToasts() - const { setModal } = useModal(ProfileModalName) - - const userPK = useUserPublicKey() - const isUser = useMemo(() => { - if (userPK) { - return id === userPK - } else { - return false - } - }, [id, userPK]) - - const [renaming, setRenaming] = useState(renamingState ?? false) - - useEffect(() => { - setRenaming(renamingState ?? false) - }, [renamingState]) - - const [request, setRequest] = useState('') - const [requestCreation, setRequestCreation] = useState(requestState ?? false) - - useEffect(() => { - setRequestCreation(requestState ?? false) - }, [requestState]) - - const { contacts, contactsDispatch } = useMessengerContext() - const contact = useMemo(() => contacts[id], [id, contacts]) - const [customNameInput, setCustomNameInput] = useState('') - - if (!contact) return null - return ( - -
- {contact.trueName}’s Profile -
- - - - {image ? ( - - ) : ( - - )} - - - {contact?.customName ?? contact.trueName} - - {contact.isUntrustworthy && } - {!renaming && ( - - )} - - {contact?.customName && ( - {contact.trueName} - )} - - {renaming ? ( - - setCustomNameInput(e.currentTarget.value)} - /> - {customNameInput && ( - { - contactsDispatch({ - type: 'setCustomName', - payload: { id, customName: undefined }, - }) - setCustomNameInput('') - }} - > - - - )} - - ) : ( - <> - - {requestCreation ? ( - - {id.slice(0, 10)}...{id.slice(-3)} - - ) : ( - <> - - Chatkey: {id.slice(0, 30)} - - - copy(id)}> - - - - )} - - - 🎩🍞πŸ₯‘πŸ¦πŸŒˆπŸ“‘πŸ’…πŸ»β™£οΈπŸ””β›ΈπŸ‘΅πŸ…± - {' '} - - )} - {requestCreation && ( - - {request.length}/280 - setRequest(e.currentTarget.value)} - required - /> - - )} - - - {renaming ? ( - <> - setRenaming(false)}> - - - { - contactsDispatch({ - type: 'setCustomName', - payload: { id, customName: customNameInput }, - }) - setRenaming(false) - }} - > - Apply nickname - - - ) : requestCreation ? ( - <> - setRequestCreation(false)}> - - - { - setToasts(prev => [ - ...prev, - { - id: id + request, - type: 'confirmation', - text: 'Contact Request Sent', - }, - ]), - setRequestCreation(false), - setModal(false), - setRequest('') - }} - > - Send Contact Request - - - ) : ( - <> - {!contact.isFriend && !isUser && ( - { - contactsDispatch({ type: 'toggleBlocked', payload: { id } }) - }} - > - {contact.blocked ? 'Unblock' : 'Block'} - - )} - {contact.isFriend && ( - - contactsDispatch({ - type: 'setIsFriend', - payload: { id, isFriend: false }, - }) - } - > - Remove Contact - - )} - - contactsDispatch({ type: 'toggleTrustworthy', payload: { id } }) - } - > - {contact.isUntrustworthy - ? 'Remove Untrustworthy Mark' - : 'Mark as Untrustworthy'} - - {!contact.isFriend && ( - setRequestCreation(true)}> - Send Contact Request - - )} - - )} - -
- ) -} - -const ProfileSection = styled(Section)` - display: flex; - flex-direction: column; - align-items: center; -` - -const NameSection = styled.div` - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - margin-bottom: 16px; - - &.small { - margin-bottom: 0; - } -` - -const ProfileIcon = styled.div` - width: 80px; - height: 80px; - display: flex; - justify-content: center; - align-items: end; - border-radius: 50%; - background-color: #bcbdff; - background-size: contain; - background-position: center; - flex-shrink: 0; - position: relative; - cursor: pointer; - - &.small { - width: 64px; - height: 64px; - } -` - -export const UserNameWrapper = styled.div` - display: flex; - justify-content: center; - align-items: center; - margin-top: 4px; - - & > svg { - fill: ${({ theme }) => theme.tertiary}; - } - - &.logout { - margin: 8px 0; - } -` - -export const UserName = styled.p` - color: ${({ theme }) => theme.primary}; - font-weight: bold; - font-size: 22px; - line-height: 30px; - letter-spacing: -0.2px; - margin-right: 8px; - - &.small { - font-size: 17px; - line-height: 24px; - margin-right: 0; - } -` - -const UserTrueName = styled.p` - color: ${({ theme }) => theme.primary}; - font-size: 12px; - line-height: 16px; - letter-spacing: 0.1px; - margin-top: 8px; -` - -export const UserAddressWrapper = styled.div` - display: flex; - justify-content: center; - align-items: center; - margin-bottom: 24px; - - &.small { - margin-bottom: 8px; - } -` - -export const UserAddress = styled.p` - display: flex; - letter-spacing: 1px; - margin-right: 8px; - color: ${({ theme }) => theme.secondary}; - - ${textMediumStyles} - - &.small { - margin-right: 0; - - ${textSmallStyles} - } -` -export const EmojiKey = styled.div` - width: 116px; - gap: 8px; - font-size: 15px; - line-height: 14px; - letter-spacing: 0.2px; - - &.small { - width: 83px; - ${textSmallStyles} - } -` - -const ProfileBtn = styled.button` - padding: 11px 24px; - ${buttonStyles} - background: ${({ theme }) => theme.bodyBackgroundColor}; - border: 1px solid ${({ theme }) => theme.border}; - margin-left: 8px; - - &.red { - color: ${({ theme }) => theme.redColor}; - } - - &.red:hover { - background: ${({ theme }) => theme.buttonNoBgHover}; - } -` - -const CopyButton = styled.button` - & > svg { - fill: ${({ theme }) => theme.tertiary}; - } -` - -const RequestSection = styled.div` - width: 100%; - display: flex; - flex-direction: column; - align-items: flex-end; - margin: 16px 0; -` - -const RequestInput = styled.textarea` - width: 100%; - height: 152px; - padding: 10px 16px; - resize: none; - margin-top: 16px; - font-family: 'Inter'; - - ${inputStyles} -` diff --git a/packages/status-react/src/components/Modals/SizeLimitModal.tsx b/packages/status-react/src/components/Modals/SizeLimitModal.tsx deleted file mode 100644 index b718b1ef..00000000 --- a/packages/status-react/src/components/Modals/SizeLimitModal.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import React from 'react' - -import { useModal } from '../../contexts/modalProvider' -import { Modal } from './Modal' - -export const SizeLimitModalName = 'SizeLimitModal' - -export function SizeLimitModal() { - const { setModal } = useModal(SizeLimitModalName) - - return ( - - - - ) -} diff --git a/packages/status-react/src/components/Modals/StatusModal.tsx b/packages/status-react/src/components/Modals/StatusModal.tsx deleted file mode 100644 index 27f3a8c8..00000000 --- a/packages/status-react/src/components/Modals/StatusModal.tsx +++ /dev/null @@ -1,89 +0,0 @@ -import React, { useState } from 'react' - -import QRCode from 'qrcode.react' -import styled from 'styled-components' - -import { buttonStyles } from '../Buttons/buttonStyle' -import { LoginInstructions } from '../Form/LoginInstructions' -import { PasteInput } from '../Form/PasteInput' -import { Modal } from './Modal' -import { Heading, MiddleSection, Section } from './ModalStyle' - -export const StatusModalName = 'StatusModal' - -export enum StatusModalState { - Mobile, - Desktop, -} - -export function StatusModal() { - const [modalState, setModalState] = useState( - StatusModalState.Mobile - ) - - const mobileFlow = modalState === StatusModalState.Mobile - const desktopFlow = modalState === StatusModalState.Desktop - - const switchModalState = (state: StatusModalState) => { - setModalState(prev => (prev === state ? StatusModalState.Mobile : state)) - } - return ( - -
- Sync with Status profile -
- - - switchModalState(StatusModalState.Mobile)} - > - From mobile - - switchModalState(StatusModalState.Desktop)} - > - From desktop - - - - {mobileFlow && } - - {desktopFlow && } - - - -
- ) -} - -const MiddleSectionStatus = styled(MiddleSection)` - height: 514px; -` - -const Switch = styled.div` - display: flex; - align-items: center; - margin-bottom: 32px; -` - -const SwitchBtn = styled.button` - ${buttonStyles} - width: 159px; - padding: 7px 0; - text-align: center; - color: ${({ theme }) => theme.tertiary}; - background: ${({ theme }) => theme.buttonBg}; - position: relative; - - &.active { - background: ${({ theme }) => theme.tertiary}; - color: ${({ theme }) => theme.bodyBackgroundColor}; - z-index: 10000; - } -` - -const SwitchBtnMobile = styled(SwitchBtn)` - margin-left: -8px; -` diff --git a/packages/status-react/src/components/Modals/UserCreationModal.tsx b/packages/status-react/src/components/Modals/UserCreationModal.tsx deleted file mode 100644 index 2c157c7f..00000000 --- a/packages/status-react/src/components/Modals/UserCreationModal.tsx +++ /dev/null @@ -1,214 +0,0 @@ -import React, { useState } from 'react' - -import { Identity } from '@status-im/core' -import styled from 'styled-components' - -import { - useSetIdentity, - useSetNikcname, - useUserPublicKey, - useWalletIdentity, -} from '../../contexts/identityProvider' -import { useModal } from '../../contexts/modalProvider' -import { useNameError } from '../../hooks/useNameError' -import { saveIdentity } from '../../utils' -import { ClearBtn, NameInput, NameInputWrapper } from '../Form/inputStyles' -import { NameError } from '../Form/NameError' -import { AddIcon } from '../Icons/AddIcon' -import { ChainIcon } from '../Icons/ChainIcon' -import { ClearSvgFull } from '../Icons/ClearIconFull' -import { LeftIcon } from '../Icons/LeftIcon' -import { UserLogo } from '../Members/UserLogo' -import { AgreementModalName } from './AgreementModal' -import { Modal } from './Modal' -import { - AddWrapper, - BackBtn, - Btn, - ButtonSection, - Heading, - Hint, - Section, - Text, -} from './ModalStyle' -import { EmojiKey, UserAddress } from './ProfileModal' - -import type { Contact } from '../../models/Contact' - -export const UserCreationModalName = 'UserCreationModal' - -export function UserCreationModal() { - const walletIdentity = useWalletIdentity() - const userPK = useUserPublicKey() - const setIdentity = useSetIdentity() - const setNickname = useSetNikcname() - - const [customNameInput, setCustomNameInput] = useState('') - const error = useNameError(customNameInput) - const [nextStep, setNextStep] = useState(false) - const { setModal } = useModal(UserCreationModalName) - const { setModal: setAgreementModal } = useModal(AgreementModalName) - - return ( - -
- Create a Status Profile -
- - {nextStep ? ( - Your emojihash and identicon ring - ) : ( - Your profile - )} - {nextStep ? ( - - {' '} - This set of emojis and coloured ring around your avatar are unique - and represent your chat key, so your friends can easily distinguish - you from potential impersonators. - - ) : ( - - Longer and unusual names are better as they are
less likely - to be used by someone else. -
- )} - - - - {!nextStep && ( - - - - )} - - {!nextStep && ( - - setCustomNameInput(e.currentTarget.value)} - maxLength={24} - /> - {customNameInput && ( - setCustomNameInput('')}> - - - )} - - )} - - - - {nextStep && userPK && ( - <> - - {' '} - Chatkey: {userPK.slice(0, 10)}... - {userPK.slice(-3)}{' '} - - - - - - - 🎩🍞πŸ₯‘πŸ¦πŸŒˆπŸ“‘πŸ’…πŸ»β™£οΈπŸ””β›ΈπŸ‘΅πŸ…± - - - - )} -
- - setModal(false)}> - - - { - if (nextStep) { - setModal(false) - setAgreementModal(true) - } else { - const identity = walletIdentity || Identity.generate() - setNickname(customNameInput) - setIdentity(identity) - !walletIdentity && saveIdentity(identity, 'noPassword') - setNextStep(true) - } - }} - disabled={!customNameInput || error !== 0} - > - Next - - -
- ) -} - -const MiddleSection = styled(Section)` - height: 420px; - display: flex; - flex-direction: column; - align-items: center; - - &.initial { - padding: 32px; - } -` - -const Title = styled(Text)` - font-weight: bold; - font-size: 22px; - line-height: 30px; - letter-spacing: -0.2px; - margin-bottom: 16px; -` - -const StyledHint = styled(Hint)` - font-size: 15px; - line-height: 22px; - margin-bottom: 32px; - text-align: center; -` - -const LogoWrapper = styled.div` - position: relative; - display: flex; - margin-bottom: 32px; -` - -const AddIconWrapper = styled(AddWrapper)` - top: 0; - right: -50%; - transform: translateX(-50%); -` - -const ChainIcons = styled.div` - width: 104px; - display: flex; - justify-content: space-between; - align-items: center; - margin: 16px 0; -` - -const UserAttributes = styled.div` - width: 200px; - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 32px; -` diff --git a/packages/status-react/src/components/Modals/UserCreationStartModal.tsx b/packages/status-react/src/components/Modals/UserCreationStartModal.tsx deleted file mode 100644 index 46f55250..00000000 --- a/packages/status-react/src/components/Modals/UserCreationStartModal.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import React from 'react' - -import styled from 'styled-components' - -import { UserCreationButtons } from '../UserCreation/UserCreationButtons' -import { Modal } from './Modal' -import { Heading, Section } from './ModalStyle' - -export const UserCreationStartModalName = 'UserCreationStartModal' - -export const UserCreationStartModal = () => { - return ( - -
- Jump into the discussion -
- - - -
- ) -} - -const MiddleSection = styled(Section)` - padding: 48px 0; -` diff --git a/packages/status-react/src/components/Modals/WalletConnectModal.tsx b/packages/status-react/src/components/Modals/WalletConnectModal.tsx deleted file mode 100644 index 6d69ea18..00000000 --- a/packages/status-react/src/components/Modals/WalletConnectModal.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import React from 'react' - -import { ConnectModal } from './ConnectModal' -import { Modal } from './Modal' - -export const WalletConnectModalName = 'WalletConnectModal' - -export function WalletConnectModal() { - return ( - - - - ) -} diff --git a/packages/status-react/src/components/Modals/WalletModal.tsx b/packages/status-react/src/components/Modals/WalletModal.tsx deleted file mode 100644 index 2845c24c..00000000 --- a/packages/status-react/src/components/Modals/WalletModal.tsx +++ /dev/null @@ -1,165 +0,0 @@ -import React, { useCallback } from 'react' - -import { genPrivateKeyWithEntropy, Identity } from '@status-im/core' -import styled from 'styled-components' - -import { - useSetIdentity, - useSetWalletIdentity, -} from '../../contexts/identityProvider' -import { useMessengerContext } from '../../contexts/messengerProvider' -import { useModal } from '../../contexts/modalProvider' -import { CoinbaseLogo } from '../Icons/CoinbaseLogo' -import { MetamaskLogo } from '../Icons/MetamaskLogo' -import { WalletConnectLogo } from '../Icons/WalletConnectLogo' -import { CoinbaseModalName } from './CoinbaseModal' -import { Modal } from './Modal' -import { Heading, MiddleSection, Section, Text } from './ModalStyle' -import { UserCreationModalName } from './UserCreationModal' -import { WalletConnectModalName } from './WalletConnectModal' - -export const WalletModalName = 'WalletModal' - -export function WalletModal() { - const { setModal } = useModal(WalletModalName) - const setIdentity = useSetIdentity() - const setWalletIdentity = useSetWalletIdentity() - const { setModal: setUserCreationModal } = useModal(UserCreationModalName) - const { setModal: setWalleConnectModal } = useModal(WalletConnectModalName) - const { setModal: setCoinbaseModal } = useModal(CoinbaseModalName) - const { messenger } = useMessengerContext() - - const handleMetamaskClick = useCallback(async () => { - // TODO: Add types for global Ethereum object - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const dappUrl = window.location.hostname - - const ethereum = (window as any)?.ethereum as any | undefined - if (document.location.origin !== dappUrl) { - alert('You are not signing in from correct url!') - return - } - if (ethereum && messenger) { - try { - if (ethereum?.isMetaMask) { - const [account] = await ethereum.request({ - method: 'eth_requestAccounts', - }) - - const msgParams = JSON.stringify({ - domain: { - chainId: 1, - name: window.location.origin, - version: '1', - }, - message: { - action: 'Status CommunityChatRoom Key', - onlySignOn: dappUrl, - message: - "This signature will be used to decrypt chat communications; check that the 'onlySignOn' property of this message matches the current website address.", - }, - primaryType: 'Mail', - types: { - EIP712Domain: [ - { name: 'name', type: 'string' }, - { name: 'version', type: 'string' }, - { name: 'chainId', type: 'uint256' }, - ], - Mail: [ - { name: 'action', type: 'string' }, - { name: 'onlySignOn', type: 'string' }, - { name: 'message', type: 'string' }, - ], - }, - }) - - const params = [account, msgParams] - const method = 'eth_signTypedData_v4' - - const signature = await ethereum.request({ - method, - params, - from: account, - }) - const privateKey = genPrivateKeyWithEntropy(signature) - - const loadedIdentity = new Identity(privateKey) - - const userInNetwork = await messenger.checkIfUserInWakuNetwork( - loadedIdentity.publicKey - ) - - if (userInNetwork) { - setIdentity(loadedIdentity) - } else { - setWalletIdentity(loadedIdentity) - setUserCreationModal(true) - } - setModal(false) - return - } - } catch { - alert('Error') - } - } - alert('Metamask not found') - }, [ - messenger, - setIdentity, - setModal, - setWalletIdentity, - setUserCreationModal, - ]) - - return ( - -
- Connect an Ethereum Wallet -
- - Choose a way to chat using your Ethereum address. - - (setModal(false), setWalleConnectModal(true))}> - WalletConnect - - - (setModal(false), setCoinbaseModal(true))}> - Coinbase Wallet - - - - MetaMask - - - - -
- ) -} - -const MiddleSectionWallet = styled(MiddleSection)` - align-items: stretch; -` - -const Wallets = styled.div` - display: flex; - flex-direction: column; - align-items: center; - margin-top: 16px; -` - -const Wallet = styled.div` - width: 100%; - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 8px; - padding: 12px 16px; - border: 1px solid ${({ theme }) => theme.skeletonDark}; - border-radius: 8px; - cursor: pointer; - - &:hover { - background: ${({ theme }) => theme.buttonBgHover}; - } -` diff --git a/packages/status-react/src/components/chat-menu/edit-group-chat-dialog.tsx b/packages/status-react/src/components/chat-menu/edit-group-chat-dialog.tsx new file mode 100644 index 00000000..45492a0b --- /dev/null +++ b/packages/status-react/src/components/chat-menu/edit-group-chat-dialog.tsx @@ -0,0 +1,21 @@ +import React from 'react' + +import { Avatar, Dialog, TextInput } from '~/src/system' + +export const EditGroupChatDialog = () => { + return ( + + + + + + + Save changes + + + ) +} diff --git a/packages/status-react/src/components/chat-menu/index.tsx b/packages/status-react/src/components/chat-menu/index.tsx index f3478dee..ee9fad35 100644 --- a/packages/status-react/src/components/chat-menu/index.tsx +++ b/packages/status-react/src/components/chat-menu/index.tsx @@ -2,6 +2,11 @@ import React from 'react' import { BellIcon } from '~/src/icons/bell-icon' import { ContextMenu, DropdownMenu } from '~/src/system' +import { useAlertDialog } from '~/src/system/dialog/alert-dialog' +import { useDialog } from '~/src/system/dialog/dialog' + +import { UserProfileDialog } from '../user-profile-dialog' +import { EditGroupChatDialog } from './edit-group-chat-dialog' interface Props { type: 'dropdown' | 'context' @@ -13,57 +18,89 @@ export const ChatMenu = (props: Props) => { const Menu = type === 'dropdown' ? DropdownMenu : ContextMenu - const renderMenuItems = () => { - const commonMenuItems = ( - <> - }> - For 15 min - For 1 hour - For 8 hours - For 24 hours - Until I turn it back on - - }>Mark as Read - }> - Last 24 hours - Last 2 days - Last 3 days - Last 7 days - - - ) + const userProfileDialog = useDialog(UserProfileDialog) + const editGroupChatDialog = useDialog(EditGroupChatDialog) - if (chatType === 'channel') { - return commonMenuItems - } + const deleteChatDialog = useAlertDialog({ + title: 'Delete Chat', + description: 'Are you sure you want to delete this chat?', + actionLabel: 'Delete', + actionVariant: 'danger', + cancelLabel: 'Keep', + }) + const leaveGroupDialog = useAlertDialog({ + title: 'Leave Group', + description: 'Are you sure you want to leave this group chat?', + actionLabel: 'Leave', + actionVariant: 'danger', + cancelLabel: 'Stay', + }) - if (chatType === 'group-chat') { - return ( - <> - }>Add / remove from group - }>Edit name and image - - {commonMenuItems} - - } danger> - Leave Chat - - - ) - } + const commonMenuItems = ( + <> + }> + For 15 min + For 1 hour + For 8 hours + For 24 hours + Until I turn it back on + + }>Mark as Read + }> + Last 24 hours + Last 2 days + Last 3 days + Last 7 days + + + ) + if (chatType === 'channel') { + return {commonMenuItems} + } + + if (chatType === 'group-chat') { return ( - <> - }>View Profile + + }>Add / remove from group + } + onSelect={() => editGroupChatDialog.open({})} + > + Edit name and image + {commonMenuItems} - } danger> - Delete Chat + } + danger + onSelect={() => leaveGroupDialog.open()} + > + Leave Chat - + ) } - return {renderMenuItems()} + return ( + + } + onSelect={() => userProfileDialog.open({ contact: 'Satoshi' })} + > + View Profile + + + {commonMenuItems} + + } + danger + onSelect={() => deleteChatDialog.open()} + > + Delete Chat + + + ) } diff --git a/packages/status-react/src/components/create-profile-dialog/index.tsx b/packages/status-react/src/components/create-profile-dialog/index.tsx new file mode 100644 index 00000000..4176ca03 --- /dev/null +++ b/packages/status-react/src/components/create-profile-dialog/index.tsx @@ -0,0 +1,63 @@ +import React from 'react' + +import { + Avatar, + Dialog, + // EmojiHash, + Flex, + Heading, + Text, + TextInput, +} from '~/src/system' + +export const CreateProfileDialog = () => { + return ( + + {/* + + Your emojihash and identicon ring + + + This set of emojis and coloured ring around your avatar are unique and + represent your chat key, so your friends can easily distinguish you + from potential impersonators. + + + + + + Chatkey: 0x63FaC920149...fae4d52fe3BD377 + + + */} + + + + Your profile + + + Longer and unusual names are better as they +
+ are less likely to be used by someone else. +
+ + + + +
+ + Next + +
+ ) +} diff --git a/packages/status-react/src/components/main-sidebar/components/community-info/community-dialog.tsx b/packages/status-react/src/components/main-sidebar/components/community-info/community-dialog.tsx new file mode 100644 index 00000000..e2875b90 --- /dev/null +++ b/packages/status-react/src/components/main-sidebar/components/community-info/community-dialog.tsx @@ -0,0 +1,60 @@ +import React from 'react' + +import { useCommunity } from '~/src/protocol/use-community' +import { Button, CopyInput, Dialog, Flex, Grid, Text } from '~/src/system' + +export const CommunityDialog = () => { + const { name, description, publicKey } = useCommunity() + + return ( + + + {description} + + + + + + + To access this community, paste community public key in Status + desktop or mobile app + + + + + + + + + + + + + + + + ) +} diff --git a/packages/status-react/src/components/main-sidebar/components/get-started/connect-wallet-dialog.tsx b/packages/status-react/src/components/main-sidebar/components/get-started/connect-wallet-dialog.tsx new file mode 100644 index 00000000..e872e267 --- /dev/null +++ b/packages/status-react/src/components/main-sidebar/components/get-started/connect-wallet-dialog.tsx @@ -0,0 +1,68 @@ +import React, { useState } from 'react' + +import { styled } from '~/src/styles/config' +import { Dialog, Grid, Text } from '~/src/system' + +// TODO: Add wallet integration +export const ConnectWalletDialog = () => { + const [wallet, setWallet] = useState<'coinbase' | undefined>() + + console.log(wallet) + + // TODO: Add wallet logos + return ( + + + + Choose a way to chat using your Ethereum address. + + + WalletConnect + setWallet('coinbase')}> + Coinbase Wallet + + MetaMask + + + + ) +} + +// const CoinbaseWalletDialog = () => { +// return ( +// +// +// Scan QR code or copy and pase it in your Coinbase Wallet. +// +// +// ) +// } + +// const WalletConnectDialog = () => { +// return ( +// +// +// +// Scan QR code with a WallectConnect-compatible wallet or copy code and +// paste it in your hardware wallet. +// +// +// +// ) +// } + +const ButtonItem = styled('button', { + width: '100%', + padding: '12px 16px', + textAlign: 'left', + border: '1px solid $gray-3', + borderRadius: '$2', + color: '$accent-1', + fontSize: 17, + lineHeight: 1.5, + fontWeight: '$600', + + '&:hover': { + backgroundColor: '$primary-3', + }, +}) diff --git a/packages/status-react/src/components/main-sidebar/components/get-started/index.tsx b/packages/status-react/src/components/main-sidebar/components/get-started/index.tsx new file mode 100644 index 00000000..1802a1fb --- /dev/null +++ b/packages/status-react/src/components/main-sidebar/components/get-started/index.tsx @@ -0,0 +1,166 @@ +import React from 'react' + +import { CreateProfileDialog } from '~/src/components/create-profile-dialog' +import { useLocalStorage } from '~/src/hooks/use-local-storage' +import { Button, Flex } from '~/src/system' +import { DialogTrigger } from '~/src/system/dialog' +import { Grid } from '~/src/system/grid' +import { Heading } from '~/src/system/heading' + +import { ConnectWalletDialog } from './connect-wallet-dialog' +import { SyncStatusProfileDialog } from './sync-status-profile-dialog' +import { ThrowawayProfileFoundDialog } from './throwaway-profile-found-dialog' + +export const GetStarted = () => { + const [throwawayProfile] = useLocalStorage('cipherIdentityt', null) + + const handleSkip = () => { + // TODO: Add skip logic + } + + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Want to jump into the discussion? + + + + + + + + + + + + + + + {throwawayProfile ? ( + + ) : ( + + )} + + + + ) +} diff --git a/packages/status-react/src/components/main-sidebar/components/get-started/sync-status-profile-dialog.tsx b/packages/status-react/src/components/main-sidebar/components/get-started/sync-status-profile-dialog.tsx new file mode 100644 index 00000000..7d7ccce8 --- /dev/null +++ b/packages/status-react/src/components/main-sidebar/components/get-started/sync-status-profile-dialog.tsx @@ -0,0 +1,70 @@ +import React, { useState } from 'react' + +import { QRCodeSVG } from 'qrcode.react' + +import { styled } from '~/src/styles/config' +import { Box, ButtonGroup, Dialog, Text, TextInput } from '~/src/system' + +export const SyncStatusProfileDialog = () => { + const [platform, setPlatform] = useState<'mobile' | 'desktop'>('mobile') + + return ( + + + + From Mobile + From Desktop + + + {platform === 'mobile' && ( + <> + {/* TODO: Add mobile QR code */} + + + + {/* // TODO: Add icons to instructions */} + 1. Open Status App on your mobile + 2. Navigate yourself to tab + 3. Select + 4. Tap + 5. Scan the sync code from this screen ↑ + + + + )} + + {platform === 'desktop' && ( + <> + + + + + {/* TODO: Add icons to instructions */} + 1. Open Status App on your desktop + 2. Navigate yourself to tab + 3. Select + 4. Tap + 5. Scan the sync code from this screen ↑ + + + )} + + + ) +} + +const List = styled('ul', { + display: 'flex', + flexDirection: 'column', + gap: 20, +}) + +const ListItem = styled('li', Text, { + defaultVariants: { + color: 'gray', + }, +}) diff --git a/packages/status-react/src/components/main-sidebar/components/get-started/throwaway-profile-found-dialog.tsx b/packages/status-react/src/components/main-sidebar/components/get-started/throwaway-profile-found-dialog.tsx new file mode 100644 index 00000000..d8a89d56 --- /dev/null +++ b/packages/status-react/src/components/main-sidebar/components/get-started/throwaway-profile-found-dialog.tsx @@ -0,0 +1,47 @@ +import React from 'react' + +import { useProfile } from '~/src/protocol/use-profile' +import { Avatar, Dialog, EmojiHash, Flex, Heading, Text } from '~/src/system' + +interface Props { + onSkip: () => void +} + +export const ThrowawayProfileFoundDialog = (props: Props) => { + const { onSkip } = props + + const profile = useProfile() + + const handleLoadThrowawayProfile = () => { + // TODO: load throwaway profile + } + + return ( + + + + + {profile.name} + + Chatkey: 0x63FaC9201494f0bd17B9892B9fae4d52fe3BD377 + + + + + Throwaway profile is found in your local {"browser's"} storage. +
+ Would you like to load it and use it? +
+
+ + + + Skip + + + Load Throwaway Profile + + +
+ ) +} diff --git a/packages/status-react/src/components/main-sidebar/index.tsx b/packages/status-react/src/components/main-sidebar/index.tsx index 3183fb6d..55a6e194 100644 --- a/packages/status-react/src/components/main-sidebar/index.tsx +++ b/packages/status-react/src/components/main-sidebar/index.tsx @@ -38,4 +38,5 @@ const Separator = styled('div', { margin: '16px 0', height: 1, background: 'rgba(0, 0, 0, 0.1)', + flexShrink: 0, }) diff --git a/packages/status-react/src/components/member-sidebar/disconnect-dialog.tsx b/packages/status-react/src/components/member-sidebar/disconnect-dialog.tsx new file mode 100644 index 00000000..b142279a --- /dev/null +++ b/packages/status-react/src/components/member-sidebar/disconnect-dialog.tsx @@ -0,0 +1,28 @@ +import React from 'react' + +import { useProfile } from '~/src/protocol/use-profile' +import { Avatar, Dialog, EmojiHash, Flex, Heading, Text } from '~/src/system' + +export const DisconnectDialog = () => { + const profile = useProfile() + + return ( + + + Do you want to disconnect your profile from this browser? + + + {profile.name} + + Chatkey: 0x63FaC9201494f0bd17B9892B9fae4d52fe3BD377 + + + + + + Stay Connected + Disconnect + + + ) +} diff --git a/packages/status-react/src/components/member-sidebar/user-item.tsx b/packages/status-react/src/components/member-sidebar/user-item.tsx index f506aad5..fe03e22f 100644 --- a/packages/status-react/src/components/member-sidebar/user-item.tsx +++ b/packages/status-react/src/components/member-sidebar/user-item.tsx @@ -1,37 +1,32 @@ import React from 'react' +import { useProfile } from '~/src/protocol/use-profile' import { styled } from '~/src/styles/config' -import { - Avatar, - Dialog, - DialogTrigger, - EthAddress, - Flex, - Text, -} from '~/src/system' +import { Avatar, DialogTrigger, EthAddress, Flex, Text } from '~/src/system' + +import { DisconnectDialog } from './disconnect-dialog' export const UserItem = () => { + const profile = useProfile() + return ( - +
- - Pavel + + {profile.name} - 71C7656EC7ab88b098defB751B7401B5f6d8976F + {profile.publicKey}
- + { fill="#4360DF" /> - - - Do you want to disconnect your profile from this browser? - - + +
) } -const LogoutButton = styled('button', { +const DisconnectButton = styled('button', { background: 'rgba(67, 96, 223, 0.1)', borderRadius: '50%', height: 32, diff --git a/packages/status-react/src/components/user-profile-dialog/index.tsx b/packages/status-react/src/components/user-profile-dialog/index.tsx new file mode 100644 index 00000000..4e8b99eb --- /dev/null +++ b/packages/status-react/src/components/user-profile-dialog/index.tsx @@ -0,0 +1,28 @@ +import React from 'react' + +import { Avatar, Dialog, EmojiHash, Heading, Text } from '~/src/system' + +interface Props { + contact: string +} + +// TODO: Add all states of contact, wait for desktop release +export const UserProfileDialog = (props: Props) => { + const { contact, ...dialogProps } = props + + return ( + + + + {contact} + Chatkey:0x63FaC9201494f0bd17B9892B9fae4d52fe3BD377 + + + + Remove Contact + Mark Untrustworthy + Send Contact Request + + + ) +} diff --git a/packages/status-react/src/components/welcome-dialog/index.tsx b/packages/status-react/src/components/welcome-dialog/index.tsx new file mode 100644 index 00000000..6a2c959a --- /dev/null +++ b/packages/status-react/src/components/welcome-dialog/index.tsx @@ -0,0 +1,46 @@ +import React, { useState } from 'react' + +import { useCommunity } from '~/src/protocol/use-community' +import { Avatar, Checkbox, Dialog, Flex, Text } from '~/src/system' + +export const WelcomeDialog = () => { + const { name, imageUrl, requestNeeded } = useCommunity() + + const [agreed, setAgreed] = useState(false) + + return ( + + + + + + + CryptoKitties sed ut perspiciatis unde omnis iste natus error sit + voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque + ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae + dicta sunt explicabo. +
+
+ Ut enim ad minim veniam Excepteur sint occaecat cupidatat non proident + Duis aute irure Dolore eu fugiat nulla pariatur πŸš— consectetur + adipiscing elit. +
+
+ Nemo enim πŸ˜‹ ipsam voluptatem quia voluptas sit aspernatur aut odit + aut fugit, sed quia consequuntur magni dolores eos qui ratione + voluptatem sequi nesciunt. +
+ + + I agree with the above + + +
+ + + {requestNeeded ? 'Request to Join' : `Join ${name}`} + + +
+ ) +} diff --git a/packages/status-react/src/contexts/dialog-context.tsx b/packages/status-react/src/contexts/dialog-context.tsx new file mode 100644 index 00000000..f5247bec --- /dev/null +++ b/packages/status-react/src/contexts/dialog-context.tsx @@ -0,0 +1,36 @@ +import React, { cloneElement, createContext, useContext, useState } from 'react' + +const DialogContext = createContext< + ((dialog: React.ReactElement) => void) | null +>(null) + +interface Props { + children: React.ReactNode +} + +export const DialogProvider = (props: Props) => { + const { children } = props + + const [dialog, setDialog] = useState(null) + + return ( + + {children} + {dialog && + cloneElement(dialog, { + defaultOpen: true, + onOpenChange: () => setDialog(null), + })} + + ) +} + +export const useDialogContext = () => { + const context = useContext(DialogContext) + + if (!context) { + throw new Error('useDialogContext must be used within a DialogProvider') + } + + return context +} diff --git a/packages/status-react/src/modules/community/index.tsx b/packages/status-react/src/modules/community/index.tsx index c88ea800..7aa4ddc3 100644 --- a/packages/status-react/src/modules/community/index.tsx +++ b/packages/status-react/src/modules/community/index.tsx @@ -5,6 +5,7 @@ import styled, { ThemeProvider } from 'styled-components' import { AppProvider } from '~/src/contexts/app-context' import { ChatStateProvider } from '~/src/contexts/chatStateProvider' +import { DialogProvider } from '~/src/contexts/dialog-context' import { IdentityProvider } from '~/src/contexts/identityProvider' import { MessengerProvider } from '~/src/contexts/messengerProvider' import { ModalProvider } from '~/src/contexts/modalProvider' @@ -32,30 +33,32 @@ export const Community = (props: Props) => { return ( - - - - - - - - - - - -