From 6b85ef79ae3c2a07df64251b798f4a437df83f12 Mon Sep 17 00:00:00 2001 From: Pavel <14926950+prichodko@users.noreply.github.com> Date: Mon, 18 Apr 2022 16:20:31 +0200 Subject: [PATCH] Delete legacy code (#249) * refactor(react): delete unused contexts * refactor(react): delete unused hooks * refactor(react): delete unused components * refactor(react): delete unused types * refactor(react): delete unused utils * feat(react): simplify Community component structure --- .../src/components/Buttons/BackButton.tsx | 32 - .../src/components/Buttons/DownloadButton.tsx | 85 --- .../src/components/Buttons/buttonStyle.ts | 59 -- .../src/components/Channels/Channel.tsx | 212 ------- .../src/components/Channels/ChannelIcon.tsx | 56 -- .../src/components/Channels/Channels.tsx | 164 ----- .../src/components/Channels/EmptyChannel.tsx | 130 ---- .../src/components/Chat-legacy/ChatBody.tsx | 183 ------ .../components/Chat-legacy/ChatCreation.tsx | 387 ------------ .../src/components/Chat-legacy/ChatInput.tsx | 558 ------------------ .../Chat-legacy/ChatMessageContent.tsx | 233 -------- .../src/components/Chat-legacy/ChatTopbar.tsx | 209 ------- .../Chat-legacy/CommunitySidebar.tsx | 38 -- .../components/Chat-legacy/EmojiPicker.tsx | 45 -- .../src/components/CommunityIdentity.tsx | 84 --- .../src/components/Form/ChannelMenu.tsx | 194 ------ .../src/components/Form/ContactMenu.tsx | 163 ----- .../src/components/Form/CopyInput.tsx | 29 - .../src/components/Form/DropdownMenu.tsx | 97 --- .../src/components/Form/ImageMenu.tsx | 42 -- .../src/components/Form/LoginInstructions.tsx | 94 --- .../src/components/Form/MessageMenu.tsx | 120 ---- .../src/components/Form/MuteMenu.tsx | 65 -- .../src/components/Form/NameError.tsx | 45 -- .../src/components/Form/PasteInput.tsx | 40 -- .../src/components/Form/TokenRequirement.tsx | 132 ----- .../src/components/Form/Tooltip.tsx | 52 -- .../src/components/Form/inputStyles.ts | 93 --- .../src/components/Icons/ColorChatIcon.tsx | 149 ----- .../src/components/Members/Member.tsx | 126 ---- .../src/components/Members/Members.tsx | 43 -- .../src/components/Members/MembersList.tsx | 130 ---- .../src/components/Members/UserLogo.tsx | 133 ----- .../src/components/Messages/MessageQuote.tsx | 69 --- .../components/Messages/MessageReactions.tsx | 86 --- .../src/components/Messages/MessagesList.tsx | 106 ---- .../src/components/Messages/Styles.tsx | 156 ----- .../src/components/Messages/UiMessage.tsx | 164 ----- .../components/NarrowMode/NarrowChannels.tsx | 17 - .../components/NarrowMode/NarrowMembers.tsx | 27 - .../components/NarrowMode/NarrowTopbar.tsx | 60 -- .../components/Reactions/ReactionButton.tsx | 91 --- .../components/Reactions/ReactionPicker.tsx | 111 ---- .../src/components/Reactions/Reactions.tsx | 90 --- .../src/components/SearchBlock.tsx | 78 --- .../components/Skeleton/CommunitySkeleton.tsx | 29 - .../src/components/Skeleton/Loading.tsx | 38 -- .../components/Skeleton/LoadingSkeleton.tsx | 56 -- .../components/Skeleton/MessageSkeleton.tsx | 61 -- .../src/components/Skeleton/Skeleton.tsx | 46 -- packages/status-react/src/components/Text.tsx | 11 - .../components/ToastMessages/ToastMessage.tsx | 130 ---- .../ToastMessages/ToastMessageList.tsx | 29 - .../components/UserCreation/UserCreation.tsx | 45 -- .../UserCreation/UserCreationButtons.tsx | 86 --- .../src/components/main-sidebar/index.tsx | 7 + .../src/contexts/chatStateProvider.tsx | 29 - .../src/contexts/identityProvider.tsx | 83 --- .../src/contexts/messengerProvider.tsx | 57 -- .../src/contexts/modalProvider.tsx | 65 -- .../src/contexts/narrowProvider.tsx | 33 -- .../src/contexts/scrollProvider.tsx | 73 --- .../src/contexts/toastProvider.tsx | 28 - .../src/hooks/messenger/useChannelsReducer.ts | 79 --- .../src/hooks/messenger/useContacts.ts | 123 ---- .../src/hooks/messenger/useGroupChats.ts | 131 ---- .../src/hooks/messenger/useLoadPrevDay.ts | 71 --- .../src/hooks/messenger/useMessages.ts | 94 --- .../src/hooks/messenger/useMessenger.ts | 361 ----------- .../src/hooks/messenger/useNotifications.ts | 24 - .../status-react/src/hooks/useClickOutside.ts | 25 - .../src/hooks/useClickPosition.ts | 32 - .../status-react/src/hooks/useContextMenu.ts | 27 - packages/status-react/src/hooks/useReply.ts | 6 - packages/status-react/src/index.tsx | 4 +- packages/status-react/src/models/Activity.ts | 49 -- .../status-react/src/models/ChannelData.ts | 15 - .../status-react/src/models/ChatMessage.ts | 58 -- .../status-react/src/models/CommunityData.ts | 8 - packages/status-react/src/models/Contact.ts | 13 - packages/status-react/src/models/Metadata.ts | 5 - packages/status-react/src/models/Toast.ts | 6 - .../src/modules/community/index.tsx | 73 --- .../src/modules/community/messenger.tsx | 32 - packages/status-react/src/routes/index.tsx | 31 +- .../src/routes/new-chat/index.tsx | 1 + packages/status-react/src/utils/copy.ts | 5 - .../status-react/src/utils/createCommunity.ts | 24 - .../status-react/src/utils/createMessenger.ts | 43 -- .../status-react/src/utils/downloadImg.ts | 10 - packages/status-react/src/utils/index.ts | 2 - 91 files changed, 23 insertions(+), 7512 deletions(-) delete mode 100644 packages/status-react/src/components/Buttons/BackButton.tsx delete mode 100644 packages/status-react/src/components/Buttons/DownloadButton.tsx delete mode 100644 packages/status-react/src/components/Buttons/buttonStyle.ts delete mode 100644 packages/status-react/src/components/Channels/Channel.tsx delete mode 100644 packages/status-react/src/components/Channels/ChannelIcon.tsx delete mode 100644 packages/status-react/src/components/Channels/Channels.tsx delete mode 100644 packages/status-react/src/components/Channels/EmptyChannel.tsx delete mode 100644 packages/status-react/src/components/Chat-legacy/ChatBody.tsx delete mode 100644 packages/status-react/src/components/Chat-legacy/ChatCreation.tsx delete mode 100644 packages/status-react/src/components/Chat-legacy/ChatInput.tsx delete mode 100644 packages/status-react/src/components/Chat-legacy/ChatMessageContent.tsx delete mode 100644 packages/status-react/src/components/Chat-legacy/ChatTopbar.tsx delete mode 100644 packages/status-react/src/components/Chat-legacy/CommunitySidebar.tsx delete mode 100644 packages/status-react/src/components/Chat-legacy/EmojiPicker.tsx delete mode 100644 packages/status-react/src/components/CommunityIdentity.tsx delete mode 100644 packages/status-react/src/components/Form/ChannelMenu.tsx delete mode 100644 packages/status-react/src/components/Form/ContactMenu.tsx delete mode 100644 packages/status-react/src/components/Form/CopyInput.tsx delete mode 100644 packages/status-react/src/components/Form/DropdownMenu.tsx delete mode 100644 packages/status-react/src/components/Form/ImageMenu.tsx delete mode 100644 packages/status-react/src/components/Form/LoginInstructions.tsx delete mode 100644 packages/status-react/src/components/Form/MessageMenu.tsx delete mode 100644 packages/status-react/src/components/Form/MuteMenu.tsx delete mode 100644 packages/status-react/src/components/Form/NameError.tsx delete mode 100644 packages/status-react/src/components/Form/PasteInput.tsx delete mode 100644 packages/status-react/src/components/Form/TokenRequirement.tsx delete mode 100644 packages/status-react/src/components/Form/Tooltip.tsx delete mode 100644 packages/status-react/src/components/Form/inputStyles.ts delete mode 100644 packages/status-react/src/components/Icons/ColorChatIcon.tsx delete mode 100644 packages/status-react/src/components/Members/Member.tsx delete mode 100644 packages/status-react/src/components/Members/Members.tsx delete mode 100644 packages/status-react/src/components/Members/MembersList.tsx delete mode 100644 packages/status-react/src/components/Members/UserLogo.tsx delete mode 100644 packages/status-react/src/components/Messages/MessageQuote.tsx delete mode 100644 packages/status-react/src/components/Messages/MessageReactions.tsx delete mode 100644 packages/status-react/src/components/Messages/MessagesList.tsx delete mode 100644 packages/status-react/src/components/Messages/Styles.tsx delete mode 100644 packages/status-react/src/components/Messages/UiMessage.tsx delete mode 100644 packages/status-react/src/components/NarrowMode/NarrowChannels.tsx delete mode 100644 packages/status-react/src/components/NarrowMode/NarrowMembers.tsx delete mode 100644 packages/status-react/src/components/NarrowMode/NarrowTopbar.tsx delete mode 100644 packages/status-react/src/components/Reactions/ReactionButton.tsx delete mode 100644 packages/status-react/src/components/Reactions/ReactionPicker.tsx delete mode 100644 packages/status-react/src/components/Reactions/Reactions.tsx delete mode 100644 packages/status-react/src/components/SearchBlock.tsx delete mode 100644 packages/status-react/src/components/Skeleton/CommunitySkeleton.tsx delete mode 100644 packages/status-react/src/components/Skeleton/Loading.tsx delete mode 100644 packages/status-react/src/components/Skeleton/LoadingSkeleton.tsx delete mode 100644 packages/status-react/src/components/Skeleton/MessageSkeleton.tsx delete mode 100644 packages/status-react/src/components/Skeleton/Skeleton.tsx delete mode 100644 packages/status-react/src/components/Text.tsx delete mode 100644 packages/status-react/src/components/ToastMessages/ToastMessage.tsx delete mode 100644 packages/status-react/src/components/ToastMessages/ToastMessageList.tsx delete mode 100644 packages/status-react/src/components/UserCreation/UserCreation.tsx delete mode 100644 packages/status-react/src/components/UserCreation/UserCreationButtons.tsx delete mode 100644 packages/status-react/src/contexts/chatStateProvider.tsx delete mode 100644 packages/status-react/src/contexts/identityProvider.tsx delete mode 100644 packages/status-react/src/contexts/messengerProvider.tsx delete mode 100644 packages/status-react/src/contexts/modalProvider.tsx delete mode 100644 packages/status-react/src/contexts/narrowProvider.tsx delete mode 100644 packages/status-react/src/contexts/scrollProvider.tsx delete mode 100644 packages/status-react/src/contexts/toastProvider.tsx delete mode 100644 packages/status-react/src/hooks/messenger/useChannelsReducer.ts delete mode 100644 packages/status-react/src/hooks/messenger/useContacts.ts delete mode 100644 packages/status-react/src/hooks/messenger/useGroupChats.ts delete mode 100644 packages/status-react/src/hooks/messenger/useLoadPrevDay.ts delete mode 100644 packages/status-react/src/hooks/messenger/useMessages.ts delete mode 100644 packages/status-react/src/hooks/messenger/useMessenger.ts delete mode 100644 packages/status-react/src/hooks/messenger/useNotifications.ts delete mode 100644 packages/status-react/src/hooks/useClickOutside.ts delete mode 100644 packages/status-react/src/hooks/useClickPosition.ts delete mode 100644 packages/status-react/src/hooks/useContextMenu.ts delete mode 100644 packages/status-react/src/hooks/useReply.ts delete mode 100644 packages/status-react/src/models/Activity.ts delete mode 100644 packages/status-react/src/models/ChannelData.ts delete mode 100644 packages/status-react/src/models/ChatMessage.ts delete mode 100644 packages/status-react/src/models/CommunityData.ts delete mode 100644 packages/status-react/src/models/Contact.ts delete mode 100644 packages/status-react/src/models/Metadata.ts delete mode 100644 packages/status-react/src/models/Toast.ts delete mode 100644 packages/status-react/src/modules/community/index.tsx delete mode 100644 packages/status-react/src/modules/community/messenger.tsx delete mode 100644 packages/status-react/src/utils/copy.ts delete mode 100644 packages/status-react/src/utils/createCommunity.ts delete mode 100644 packages/status-react/src/utils/createMessenger.ts delete mode 100644 packages/status-react/src/utils/downloadImg.ts diff --git a/packages/status-react/src/components/Buttons/BackButton.tsx b/packages/status-react/src/components/Buttons/BackButton.tsx deleted file mode 100644 index ce623574..00000000 --- a/packages/status-react/src/components/Buttons/BackButton.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import React from 'react' - -import styled from 'styled-components' - -import { LeftIcon } from '../Icons/LeftIcon' - -interface BackButtonProps { - onBtnClick: () => void - className?: string -} - -export function BackButton({ onBtnClick, className }: BackButtonProps) { - return ( - - - - ) -} - -const BackBtn = styled.button` - position: absolute; - left: 0; - top: 8px; - width: 32px; - height: 44px; - padding: 0; - - &.narrow { - position: static; - margin-right: 13px; - } -` diff --git a/packages/status-react/src/components/Buttons/DownloadButton.tsx b/packages/status-react/src/components/Buttons/DownloadButton.tsx deleted file mode 100644 index afa4ad22..00000000 --- a/packages/status-react/src/components/Buttons/DownloadButton.tsx +++ /dev/null @@ -1,85 +0,0 @@ -import React, { useEffect, useState } from 'react' - -import styled from 'styled-components' - -import { buttonStyles } from './buttonStyle' - -const userAgent = window.navigator.userAgent -const platform = window.navigator.platform -const macosPlatforms = ['Macintosh', 'MacIntel', 'MacPPC', 'Mac68K'] -const windowsPlatforms = ['Win32', 'Win64', 'Windows', 'WinCE'] -const iosPlatforms = ['iPhone', 'iPad', 'iPod'] - -interface DownloadButtonProps { - className?: string -} - -export const DownloadButton = ({ className }: DownloadButtonProps) => { - const [link, setlink] = useState('https://status.im/get/') - const [os, setOs] = useState(null) - - useEffect(() => { - if (macosPlatforms.includes(platform)) { - setlink( - 'https://status-im-files.ams3.cdn.digitaloceanspaces.com/StatusIm-Desktop-v0.3.0-beta-a8c37d.dmg' - ) - setOs('Mac') - } else if (iosPlatforms.includes(platform)) { - setlink( - 'https://apps.apple.com/us/app/status-private-communication/id1178893006' - ) - setOs('iOS') - } else if (windowsPlatforms.includes(platform)) { - setlink( - 'https://status-im-files.ams3.cdn.digitaloceanspaces.com/StatusIm-Desktop-v0.3.0-beta-a8c37d.exe' - ) - setOs('Windows') - } else if (/Android/.test(userAgent)) { - setlink( - 'https://play.google.com/store/apps/details?id=im.status.ethereum' - ) - setOs('Android') - } else if (/Linux/.test(platform)) { - setlink( - 'https://status-im-files.ams3.cdn.digitaloceanspaces.com/StatusIm-Desktop-v0.3.0-beta-a8c37d.tar.gz' - ) - setOs('Linux') - } - }, []) - - return ( - - {os - ? `${className === 'activity' ? 'd' : 'D'}ownload Status for ${os}` - : `${className === 'activity' ? 'd' : 'D'}ownload Status`} - - ) -} - -const Link = styled.a` - margin-top: 24px; - padding: 11px 32px; - - ${buttonStyles} - - &.activity { - margin: 0; - padding: 0; - color: ${({ theme }) => theme.secondary}; - font-style: italic; - border-radius: 0; - font-weight: 400; - text-decoration: underline; - background: inherit; - - &:hover { - background: inherit; - color: ${({ theme }) => theme.tertiary}; - } - } -` diff --git a/packages/status-react/src/components/Buttons/buttonStyle.ts b/packages/status-react/src/components/Buttons/buttonStyle.ts deleted file mode 100644 index 55c2f8c0..00000000 --- a/packages/status-react/src/components/Buttons/buttonStyle.ts +++ /dev/null @@ -1,59 +0,0 @@ -import styled, { css } from 'styled-components' - -export const buttonStyles = css` - font-family: 'Inter'; - font-weight: 500; - font-size: 15px; - line-height: 22px; - text-align: center; - border-radius: 8px; - color: ${({ theme }) => theme.tertiary}; - background: ${({ theme }) => theme.buttonBg}; - - &:hover { - background: ${({ theme }) => theme.buttonBgHover}; - } - - &:focus { - background: ${({ theme }) => theme.buttonBg}; - } -` - -export const buttonTransparentStyles = css` - font-family: 'Inter'; - font-weight: 500; - font-size: 13px; - line-height: 18px; - text-align: center; - color: ${({ theme }) => theme.tertiary}; - background: inherit; - padding: 10px 12px; - border-radius: 8px; - - &:hover { - background: ${({ theme }) => theme.buttonBgHover}; - } - - &:focus { - background: ${({ theme }) => theme.buttonBg}; - } -` - -export const ButtonNo = styled.button` - padding: 11px 24px; - margin-right: 16px; - - ${buttonStyles} - background: ${({ theme }) => theme.buttonNoBg}; - color: ${({ theme }) => theme.redColor}; - - &:hover { - background: ${({ theme }) => theme.buttonNoBgHover}; - } -` - -export const ButtonYes = styled.button` - padding: 11px 24px; - - ${buttonStyles} -` diff --git a/packages/status-react/src/components/Channels/Channel.tsx b/packages/status-react/src/components/Channels/Channel.tsx deleted file mode 100644 index 9de028f2..00000000 --- a/packages/status-react/src/components/Channels/Channel.tsx +++ /dev/null @@ -1,212 +0,0 @@ -import React from 'react' - -import styled from 'styled-components' - -import { useMessengerContext } from '../../contexts/messengerProvider' -import { useNarrow } from '../../contexts/narrowProvider' -import { ChannelMenu } from '../Form/ChannelMenu' -import { Tooltip } from '../Form/Tooltip' -import { GroupIcon } from '../Icons/GroupIcon' -import { MutedIcon } from '../Icons/MutedIcon' -import { textMediumStyles } from '../Text' -import { ChannelIcon } from './ChannelIcon' - -import type { ChannelData } from '../../models/ChannelData' - -function RenderChannelName({ - channel, - activeView, - className, -}: { - channel: ChannelData - activeView?: boolean - className?: string -}) { - const { activeChannel } = useMessengerContext() - switch (channel.type) { - case 'group': - return ( -
- {!activeView && ( - - )} - {` ${channel.name}`} -
- ) - case 'channel': - return
{`# ${channel.name}`}
- case 'dm': - return
{channel.name.slice(0, 20)}
- } -} - -interface ChannelProps { - channel: ChannelData - notified?: boolean - mention?: number - isActive: boolean - activeView?: boolean - onClick?: () => void - setEditGroup?: React.Dispatch> -} - -export function Channel({ - channel, - isActive, - activeView, - onClick, - notified, - mention, - setEditGroup, -}: ChannelProps) { - const narrow = useNarrow() - const { channelsDispatch } = useMessengerContext() - - return ( - - - - - - - {channel?.isMuted && activeView && !narrow && ( - - channelsDispatch({ type: 'ToggleMuted', payload: channel.id }) - } - > - - - - )} - - {activeView && ( - {channel.description} - )} - - - {!activeView && !!mention && !channel?.isMuted && ( - {mention} - )} - {channel?.isMuted && !activeView && } - {!activeView && ( - - )} - - ) -} - -const ChannelWrapper = styled.div<{ isNarrow?: boolean }>` - width: ${({ isNarrow }) => (isNarrow ? 'calc(100% - 162px)' : '100%')}; - display: flex; - justify-content: space-between; - align-items: center; - padding: 8px; - border-radius: 8px; - position: relative; - cursor: pointer; - - &.active, - &:active { - background-color: ${({ theme }) => theme.activeChannelBackground}; - } - - &:hover { - background-color: ${({ theme, isNarrow }) => isNarrow && theme.border}; - } -` - -export const ChannelInfo = styled.div<{ activeView?: boolean }>` - display: flex; - align-items: ${({ activeView }) => (activeView ? 'flex-start' : 'center')}; - overflow-x: hidden; -` - -const ChannelTextInfo = styled.div<{ activeView?: boolean }>` - display: flex; - flex-direction: column; - text-overflow: ellipsis; - overflow-x: hidden; - white-space: nowrap; - padding: ${({ activeView }) => activeView && '0 24px 24px 0'}; -` - -const ChannelNameWrapper = styled.div` - display: flex; - align-items: center; -` - -export const ChannelName = styled(RenderChannelName)<{ - muted?: boolean - notified?: boolean - active?: boolean - activeView?: boolean -}>` - font-weight: ${({ notified, muted, active }) => - notified && !muted && !active ? '600' : '500'}; - opacity: ${({ notified, muted, active }) => - muted ? '0.4' : notified || active ? '1.0' : '0.7'}; - color: ${({ theme }) => theme.primary}; - margin-right: ${({ muted, activeView }) => - muted && activeView ? '8px' : ''}; - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; - - ${textMediumStyles} -` - -const ChannelDescription = styled.p` - font-size: 12px; - line-height: 16px; - letter-spacing: 0.1px; - color: ${({ theme }) => theme.secondary}; - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; -` - -const NotificationBagde = styled.div` - width: 24px; - height: 24px; - border-radius: 50%; - font-size: 12px; - line-height: 16px; - font-weight: 500; - background-color: ${({ theme }) => theme.notificationColor}; - color: ${({ theme }) => theme.bodyBackgroundColor}; - display: flex; - align-items: center; - justify-content: center; - flex-shrink: 0; -` - -const MutedBtn = styled.button` - padding: 0; - border: none; - outline: none; - position: relative; - - &:hover > svg { - fill-opacity: 1; - } - - &:hover > div { - visibility: visible; - } -` diff --git a/packages/status-react/src/components/Channels/ChannelIcon.tsx b/packages/status-react/src/components/Channels/ChannelIcon.tsx deleted file mode 100644 index f9422012..00000000 --- a/packages/status-react/src/components/Channels/ChannelIcon.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import React from 'react' - -import styled from 'styled-components' - -import { useNarrow } from '../../contexts/narrowProvider' - -import type { ChannelData } from '../../models/ChannelData' - -interface ChannelIconProps { - channel: ChannelData - activeView?: boolean -} - -export function ChannelIcon({ channel, activeView }: ChannelIconProps) { - const narrow = useNarrow() - - return ( - - {!channel.icon && channel.name.slice(0, 1).toUpperCase()} - - ) -} - -export const ChannelLogo = styled.div<{ icon?: string }>` - width: 24px; - height: 24px; - display: flex; - align-items: center; - justify-content: center; - flex-shrink: 0; - margin-right: 10px; - border-radius: 50%; - font-weight: bold; - font-size: 15px; - line-height: 20px; - background-color: ${({ theme }) => theme.iconColor}; - background-size: cover; - background-repeat: no-repeat; - background-image: ${({ icon }) => icon && `url(${icon}`}; - color: ${({ theme }) => theme.iconTextColor}; - - &.active { - width: 36px; - height: 36px; - font-size: 20px; - } - - &.narrow { - width: 40px; - height: 40px; - font-size: 20px; - } -` diff --git a/packages/status-react/src/components/Channels/Channels.tsx b/packages/status-react/src/components/Channels/Channels.tsx deleted file mode 100644 index d5e6b979..00000000 --- a/packages/status-react/src/components/Channels/Channels.tsx +++ /dev/null @@ -1,164 +0,0 @@ -import React, { useMemo } from 'react' - -import styled from 'styled-components' - -import { ChatState, useChatState } from '../../contexts/chatStateProvider' -import { useIdentity } from '../../contexts/identityProvider' -import { useMessengerContext } from '../../contexts/messengerProvider' -import { CreateIcon } from '../Icons/CreateIcon' -import { UserCreation } from '../UserCreation/UserCreation' -import { Channel } from './Channel' - -interface ChannelsProps { - onCommunityClick?: () => void - setEditGroup?: React.Dispatch> -} - -type GenerateChannelsProps = ChannelsProps & { - type: string -} - -function GenerateChannels({ - type, - onCommunityClick, - setEditGroup, -}: GenerateChannelsProps) { - const { mentions, notifications, activeChannel, channelsDispatch, channels } = - useMessengerContext() - - const channelList = useMemo(() => Object.values(channels), [channels]) - - const setChatState = useChatState()[1] - return ( - <> - {channelList - .filter(channel => channel.type === type) - .map(channel => ( - 0} - mention={mentions?.[channel.id]} - onClick={() => { - channelsDispatch({ type: 'ChangeActive', payload: channel.id }) - if (onCommunityClick) { - onCommunityClick() - } - setChatState(ChatState.ChatBody) - }} - setEditGroup={setEditGroup} - /> - ))} - - ) -} - -type ChatsListProps = { - onCommunityClick?: () => void - setEditGroup?: React.Dispatch> -} - -function ChatsSideBar({ onCommunityClick, setEditGroup }: ChatsListProps) { - const setChatState = useChatState()[1] - return ( - <> - - Messages - setChatState(ChatState.ChatCreation)}> - - - - - - - - - ) -} - -export function Channels({ onCommunityClick, setEditGroup }: ChannelsProps) { - const identity = useIdentity() - return ( - - - - {identity ? ( - - ) : ( - - )} - - - ) -} - -export const ChannelList = styled.div` - display: flex; - flex-direction: column; - - &::-webkit-scrollbar { - width: 0; - } -` - -const Chats = styled.div` - display: flex; - flex-direction: column; - padding-top: 16px; - margin-top: 16px; - position: relative; - - &::before { - content: ''; - position: absolute; - top: 0; - left: 50%; - transform: translateX(-50%); - width: calc(100% - 24px); - height: 1px; - background-color: ${({ theme }) => theme.primary}; - opacity: 0.1; - } -` - -const ChatsBar = styled.div` - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 8px; -` - -const ChatsList = styled.div` - display: flex; - flex-direction: column; -` - -const Heading = styled.p` - font-weight: bold; - font-size: 17px; - line-height: 24px; - color: ${({ theme }) => theme.primary}; -` - -const EditBtn = styled.button` - width: 32px; - height: 32px; - border-radius: 8px; - padding: 0; - - &:hover { - background: ${({ theme }) => theme.inputColor}; - } - - &:active { - background: ${({ theme }) => theme.sectionBackgroundColor}; - } -` diff --git a/packages/status-react/src/components/Channels/EmptyChannel.tsx b/packages/status-react/src/components/Channels/EmptyChannel.tsx deleted file mode 100644 index d2e84eb0..00000000 --- a/packages/status-react/src/components/Channels/EmptyChannel.tsx +++ /dev/null @@ -1,130 +0,0 @@ -import React, { useMemo } from 'react' - -import styled from 'styled-components' - -import { useUserPublicKey } from '../../contexts/identityProvider' -import { useMessengerContext } from '../../contexts/messengerProvider' -import { useNarrow } from '../../contexts/narrowProvider' -import { textMediumStyles } from '../Text' -import { ChannelInfo, ChannelName } from './Channel' -import { ChannelLogo } from './ChannelIcon' - -import type { ChannelData } from '../../models/ChannelData' - -type ChannelBeggingTextProps = { - channel: ChannelData -} - -function ChannelBeggingText({ channel }: ChannelBeggingTextProps) { - const userPK = useUserPublicKey() - const { contacts } = useMessengerContext() - const members = useMemo(() => { - if (channel?.members && userPK) { - return channel.members - .filter(contact => contact.id !== userPK) - .map(member => contacts?.[member.id] ?? member) - } - return [] - }, [channel, contacts, userPK]) - - switch (channel.type) { - case 'dm': - return ( - - Any messages you send here are encrypted and can only be read by you - and
- {channel.name.slice(0, 10)}. -
- ) - case 'group': - return ( - - {userPK && {userPK}} created a group with{' '} - {members.map((contact, idx) => ( - - {contact?.customName ?? contact.trueName.slice(0, 10)} - {idx < members.length - 1 && <> and } - - ))} - - ) - case 'channel': - return ( - - Welcome to the beginning of the #{channel.name} channel! - - ) - } - return null -} - -type EmptyChannelProps = { - channel: ChannelData -} - -export function EmptyChannel({ channel }: EmptyChannelProps) { - const narrow = useNarrow() - - return ( - - - - {' '} - {!channel.icon && channel.name.slice(0, 1).toUpperCase()} - - - - - - ) -} - -const Wrapper = styled.div` - display: flex; - flex-direction: column; - align-items: center; - margin-bottom: 32px; - - &.wide { - margin-top: 24px; - } -` - -const ChannelInfoEmpty = styled(ChannelInfo)` - flex-direction: column; -` - -const ChannelLogoEmpty = styled(ChannelLogo)` - width: 120px; - height: 120px; - font-weight: bold; - font-size: 51px; - line-height: 62px; - margin-bottom: 16px; -` - -const ChannelNameEmpty = styled(ChannelName)` - font-weight: bold; - font-size: 22px; - line-height: 30px; - margin-bottom: 16px; -` - -const EmptyText = styled.p` - display: inline-block; - color: ${({ theme }) => theme.secondary}; - max-width: 310px; - text-align: center; - - & > span { - color: ${({ theme }) => theme.primary}; - } - - ${textMediumStyles} -` - -const EmptyTextGroup = styled(EmptyText)` - & > span { - word-break: break-all; - } -` diff --git a/packages/status-react/src/components/Chat-legacy/ChatBody.tsx b/packages/status-react/src/components/Chat-legacy/ChatBody.tsx deleted file mode 100644 index a7a7e70e..00000000 --- a/packages/status-react/src/components/Chat-legacy/ChatBody.tsx +++ /dev/null @@ -1,183 +0,0 @@ -import React, { useCallback, useEffect, useMemo, useState } from 'react' - -import styled from 'styled-components' - -import { useMessengerContext } from '../../contexts/messengerProvider' -import { useNarrow } from '../../contexts/narrowProvider' -import { TokenRequirement } from '../Form/TokenRequirement' -import { MessagesList } from '../Messages/MessagesList' -import { NarrowChannels } from '../NarrowMode/NarrowChannels' -import { NarrowMembers } from '../NarrowMode/NarrowMembers' -import { LoadingSkeleton } from '../Skeleton/LoadingSkeleton' -import { ChatCreation } from './ChatCreation' -import { ChatInput } from './ChatInput' -import { ChatTopbar, ChatTopbarLoading } from './ChatTopbar' - -import type { Reply } from '../../hooks/useReply' -import type { ChannelData } from '../../models/ChannelData' - -export enum ChatBodyState { - Chat, - Channels, - Members, -} - -function ChatBodyLoading() { - const narrow = useNarrow() - return ( - - - - - undefined} /> - - - ) -} - -type ChatBodyContentProps = { - showState: ChatBodyState - switchShowState: (state: ChatBodyState) => void - channel: ChannelData -} - -function ChatBodyContent({ - showState, - switchShowState, - channel, -}: ChatBodyContentProps) { - const [reply, setReply] = useState(undefined) - - switch (showState) { - case ChatBodyState.Chat: - return ( - <> - - - - ) - case ChatBodyState.Channels: - return ( - switchShowState(ChatBodyState.Channels)} - /> - ) - case ChatBodyState.Members: - return ( - switchShowState(ChatBodyState.Members)} - /> - ) - } -} - -interface ChatBodyProps { - onClick: () => void - showMembers: boolean - permission: boolean - editGroup: boolean - setEditGroup: React.Dispatch> -} - -export function ChatBody({ - onClick, - showMembers, - permission, - editGroup, - setEditGroup, -}: ChatBodyProps) { - const { activeChannel, loadingMessenger } = useMessengerContext() - - const narrow = useNarrow() - const className = useMemo(() => (narrow ? 'narrow' : ''), [narrow]) - - const [showState, setShowState] = useState(ChatBodyState.Chat) - const switchShowState = useCallback( - (state: ChatBodyState) => { - if (narrow) { - setShowState(prev => (prev === state ? ChatBodyState.Chat : state)) - } - }, - [narrow] - ) - - useEffect(() => { - if (!narrow) { - setShowState(ChatBodyState.Chat) - } - }, [narrow]) - - if (!loadingMessenger && activeChannel) { - return ( - - - {editGroup ? ( - - ) : ( - <> - - - - )} - - {!permission && ( - - - - )} - - ) - } - - return -} - -export const Wrapper = styled.div` - width: 61%; - display: flex; - flex-direction: column; - flex: 1; - height: 100%; - background: ${({ theme }) => theme.bodyBackgroundColor}; - position: relative; - - &.narrow { - width: 100%; - } -` - -const ChatBodyWrapper = styled.div` - width: 100%; - height: 100%; - display: flex; - flex-direction: column; - flex: 1; - background: ${({ theme }) => theme.bodyBackgroundColor}; -` - -const BluredWrapper = styled.div` - width: 100%; - height: 100%; - display: flex; - align-items: flex-end; - justify-content: center; - position: absolute; - bottom: 0; - left: 0; - background: ${({ theme }) => theme.bodyBackgroundGradient}; - backdrop-filter: blur(4px); - z-index: 2; -` diff --git a/packages/status-react/src/components/Chat-legacy/ChatCreation.tsx b/packages/status-react/src/components/Chat-legacy/ChatCreation.tsx deleted file mode 100644 index 470fef73..00000000 --- a/packages/status-react/src/components/Chat-legacy/ChatCreation.tsx +++ /dev/null @@ -1,387 +0,0 @@ -import React, { useCallback, useMemo, useState } from 'react' - -import styled from 'styled-components' - -import { ChatState, useChatState } from '../../contexts/chatStateProvider' -import { useUserPublicKey } from '../../contexts/identityProvider' -import { useMessengerContext } from '../../contexts/messengerProvider' -import { useNarrow } from '../../contexts/narrowProvider' -import { ActivityButton } from '../ActivityCenter/ActivityButton' -import { BackButton } from '../Buttons/BackButton' -import { buttonStyles } from '../Buttons/buttonStyle' -import { CrossIcon } from '../Icons/CrossIcon' -import { Member } from '../Members/Member' -import { SearchBlock } from '../SearchBlock' -import { textMediumStyles } from '../Text' -import { ChatInput } from './ChatInput' - -import type { ChannelData } from '../../models/ChannelData' - -interface ChatCreationProps { - setEditGroup?: (val: boolean) => void - activeChannel?: ChannelData -} - -export function ChatCreation({ - setEditGroup, - activeChannel, -}: ChatCreationProps) { - const narrow = useNarrow() - const userPK = useUserPublicKey() - const [query, setQuery] = useState('') - const [groupChatMembersIds, setGroupChatMembersIds] = useState( - activeChannel?.members?.map(member => member.id) ?? [] - ) - const { contacts, createGroupChat, addMembers } = useMessengerContext() - - const groupChatMembers = useMemo( - () => groupChatMembersIds.map(id => contacts[id]).filter(e => !!e), - [groupChatMembersIds, contacts] - ) - - const contactsList = useMemo(() => { - return Object.values(contacts) - .filter( - member => - member.id.includes(query) || - member?.customName?.includes(query) || - member.trueName.includes(query) - ) - .filter(member => !groupChatMembersIds.includes(member.id)) - }, [query, groupChatMembersIds, contacts]) - - const setChatState = useChatState()[1] - - const addMember = useCallback( - (member: string) => { - setGroupChatMembersIds((prevMembers: string[]) => { - if ( - prevMembers.find(mem => mem === member) || - prevMembers.length >= 5 - ) { - return prevMembers - } else { - return [...prevMembers, member] - } - }) - setQuery('') - }, - [setGroupChatMembersIds] - ) - const removeMember = useCallback( - (member: string) => { - setGroupChatMembersIds(prev => prev.filter(e => e != member)) - }, - [setGroupChatMembersIds] - ) - - const createChat = useCallback( - (group: string[]) => { - if (userPK) { - const newGroup = group.slice() - newGroup.push(userPK) - createGroupChat(newGroup) - setChatState(ChatState.ChatBody) - } - }, - [userPK, createGroupChat, setChatState] - ) - - const handleCreationClick = useCallback(() => { - if (!activeChannel) { - createChat(groupChatMembers.map(member => member.id)) - } else { - addMembers( - groupChatMembers.map(member => member.id), - activeChannel.id - ) - } - setEditGroup?.(false) - }, [activeChannel, groupChatMembers, createChat, addMembers, setEditGroup]) - - return ( - - - {narrow && ( - - setEditGroup - ? setEditGroup?.(false) - : setChatState(ChatState.ChatBody) - } - className="narrow" - /> - )} - - - To: - - {groupChatMembers.map(member => ( - - - {member?.customName?.slice(0, 10) ?? - member.trueName.slice(0, 10)} - - removeMember(member.id)}> - - - - ))} - - {groupChatMembers.length < 5 && ( - - setQuery(e.currentTarget.value)} - /> - - )} - {!narrow && groupChatMembers.length === 5 && ( - 5 user Limit reached - )} - - {narrow && groupChatMembers.length === 5 && ( - 5 user Limit reached - )} - - - Confirm - - {!narrow && } - {!narrow && ( - - )} - - {((!setEditGroup && groupChatMembers.length === 0) || narrow) && - Object.keys(contacts).length > 0 && ( - - Contacts - - {userPK && narrow - ? contactsList.map(contact => ( - - addMember(contact.id)} - /> - - )) - : Object.values(contacts) - .filter( - e => e.id != userPK && !groupChatMembersIds.includes(e.id) - ) - .map(contact => ( - - addMember(contact.id)} - /> - - ))} - - - )} - {!setEditGroup && Object.keys(contacts).length === 0 && ( - - - You only can send direct messages to your Contacts.{' '} - - - {' '} - Send a contact request to the person you would like to chat with, - you will be able to chat with them once they have accepted your - contact request. - - - )} - - {!activeChannel && ( - member.id)} - /> - )} - - ) -} - -const CreationWrapper = styled.div` - height: 100%; - display: flex; - flex-direction: column; - justify-content: space-between; - flex: 1; - background-color: ${({ theme }) => theme.bodyBackgroundColor}; - padding: 8px 16px; - - &.narrow { - width: 100%; - max-width: 100%; - } -` - -const CreationBar = styled.div` - display: flex; - align-items: center; - margin-bottom: 24px; - position: relative; - - &.limit { - align-items: flex-start; - } -` - -const Column = styled.div` - display: flex; - flex-direction: column; - align-items: center; - flex: 1; - margin-right: 16px; - overflow-x: hidden; -` - -const InputBar = styled.div` - display: flex; - align-items: center; - width: 100%; - height: 44px; - background-color: ${({ theme }) => theme.inputColor}; - border: 1px solid ${({ theme }) => theme.inputColor}; - border-radius: 8px; - padding: 6px 16px; - - ${textMediumStyles} -` - -const Input = styled.input` - width: 100%; - min-width: 20px; - background-color: ${({ theme }) => theme.inputColor}; - border: 1px solid ${({ theme }) => theme.inputColor}; - outline: none; - resize: none; - - ${textMediumStyles} - - &:focus { - outline: none; - caret-color: ${({ theme }) => theme.notificationColor}; - } -` - -const InputText = styled.div` - color: ${({ theme }) => theme.secondary}; - margin-right: 8px; -` - -const CreationBtn = styled.button` - padding: 11px 24px; - ${buttonStyles} - - &:disabled { - background: ${({ theme }) => theme.inputColor}; - color: ${({ theme }) => theme.secondary}; - } -` - -const StyledList = styled.div` - display: flex; - overflow-x: scroll; - margin-right: 8px; - - &::-webkit-scrollbar { - display: none; - } -` - -const StyledMember = styled.div` - display: flex; - align-items: center; - padding: 4px 4px 4px 8px; - background: ${({ theme }) => theme.tertiary}; - color: ${({ theme }) => theme.bodyBackgroundColor}; - border-radius: 8px; - - & + & { - margin-left: 8px; - } -` - -const StyledName = styled.p` - color: ${({ theme }) => theme.bodyBackgroundColor}; - - ${textMediumStyles} -` - -const CloseButton = styled.button` - width: 20px; - height: 20px; -` - -const Contacts = styled.div` - height: calc(100% - 44px); - display: flex; - flex-direction: column; - flex: 1; - overflow: auto; -` - -const Contact = styled.div` - display: flex; - align-items: center; - padding: 12px 12px 0 16px; - border-radius: 8px; - - &:hover { - background: ${({ theme }) => theme.inputColor}; - } -` - -const ContactsHeading = styled.p` - color: ${({ theme }) => theme.secondary}; - - ${textMediumStyles} -` - -export const ContactsList = styled.div` - display: flex; - flex-direction: column; -` - -const EmptyContacts = styled(Contacts)` - justify-content: center; - align-items: center; -` - -const EmptyContactsHeading = styled(ContactsHeading)` - max-width: 550px; - margin-bottom: 24px; - text-align: center; -` - -const SearchMembers = styled.div` - position: relative; - flex: 1; -` - -const LimitAlert = styled.p` - text-transform: uppercase; - margin-left: auto; - color: ${({ theme }) => theme.redColor}; - white-space: nowrap; - - &.narrow { - margin: 8px 0 0; - } -` diff --git a/packages/status-react/src/components/Chat-legacy/ChatInput.tsx b/packages/status-react/src/components/Chat-legacy/ChatInput.tsx deleted file mode 100644 index 54468dc4..00000000 --- a/packages/status-react/src/components/Chat-legacy/ChatInput.tsx +++ /dev/null @@ -1,558 +0,0 @@ -import 'emoji-mart/css/emoji-mart.css' - -import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' - -import styled from 'styled-components' - -import { ChatState, useChatState } from '../../contexts/chatStateProvider' -import { useIdentity } from '../../contexts/identityProvider' -import { useMessengerContext } from '../../contexts/messengerProvider' -import { useModal } from '../../contexts/modalProvider' -import { useNarrow } from '../../contexts/narrowProvider' -import { useClickOutside } from '../../hooks/useClickOutside' -import { uintToImgUrl } from '../../utils/uintToImgUrl' -import { ClearBtn } from '../Form/inputStyles' -import { ClearSvg } from '../Icons/ClearIcon' -import { ClearSvgFull } from '../Icons/ClearIconFull' -import { EmojiIcon } from '../Icons/EmojiIcon' -import { GifIcon } from '../Icons/GifIcon' -import { PictureIcon } from '../Icons/PictureIcon' -import { ReplySvg } from '../Icons/ReplyIcon' -import { StickerIcon } from '../Icons/StickerIcon' -import { SizeLimitModal, SizeLimitModalName } from '../Modals/SizeLimitModal' -import { UserCreationStartModalName } from '../Modals/UserCreationStartModal' -import { SearchBlock } from '../SearchBlock' -import { textMediumStyles, textSmallStyles } from '../Text' -import { EmojiPicker } from './EmojiPicker' - -import type { Reply } from '../../hooks/useReply' -import type { EmojiData } from 'emoji-mart' - -interface ChatInputProps { - reply?: Reply | undefined - setReply?: (val: Reply | undefined) => void - createChat?: (group: string[]) => void - group?: string[] -} - -export function ChatInput({ - reply, - setReply, - createChat, - group, -}: ChatInputProps) { - const narrow = useNarrow() - const identity = useIdentity() - const setChatState = useChatState()[1] - const disabled = useMemo(() => !identity, [identity]) - const { sendMessage, contacts } = useMessengerContext() - const [content, setContent] = useState('') - const [clearComponent, setClearComponent] = useState('') - const [showEmoji, setShowEmoji] = useState(false) - const [inputHeight, setInputHeight] = useState(40) - const [imageUint, setImageUint] = useState(undefined) - - const { setModal } = useModal(SizeLimitModalName) - const { setModal: setCreationStartModal } = useModal( - UserCreationStartModalName - ) - - const [query, setQuery] = useState('') - - const inputRef = useRef(null) - - const ref = useRef(null) - useClickOutside(ref, () => setShowEmoji(false)) - - const image = useMemo( - () => (imageUint ? uintToImgUrl(imageUint) : ''), - [imageUint] - ) - - const addEmoji = useCallback( - (e: EmojiData) => { - if ('unified' in e) { - const sym = e.unified.split('-') - const codesArray: string[] = [] - sym.forEach((el: string) => codesArray.push('0x' + el)) - const emoji = String.fromCodePoint( - ...(codesArray as unknown as number[]) - ) - if (inputRef.current) { - inputRef.current.appendChild(document.createTextNode(emoji)) - } - setContent(p => p + emoji) - } - }, - [setContent] - ) - - const resizeTextArea = useCallback((target: HTMLDivElement) => { - target.style.height = '40px' - target.style.height = `${Math.min(target.scrollHeight, 438)}px` - setInputHeight(target.scrollHeight) - }, []) - - const rowHeight = inputHeight + (image ? 73 : 0) - - const onInputChange = useCallback( - (e: React.ChangeEvent) => { - const element = document.getSelection() - const inputElement = inputRef.current - if (inputElement && element && element.rangeCount > 0) { - const selection = element?.getRangeAt(0)?.startOffset - const parentElement = element.anchorNode?.parentElement - if (parentElement && parentElement.tagName === 'B') { - parentElement.outerHTML = parentElement.innerText - const range = document.createRange() - const sel = window.getSelection() - if (element.anchorNode.firstChild) { - const childNumber = - element.focusOffset === 0 ? 0 : element.focusOffset - 1 - range.setStart( - element.anchorNode.childNodes[childNumber], - selection - ) - } - range.collapse(true) - - sel?.removeAllRanges() - sel?.addRange(range) - } - } - const target = e.target - resizeTextArea(target) - setContent(target.textContent ?? '') - }, - [resizeTextArea] - ) - - const onInputKeyPress = useCallback( - (e: React.KeyboardEvent) => { - if (e.key == 'Enter' && !e.getModifierState('Shift')) { - e.preventDefault() - ;(e.target as HTMLDivElement).style.height = '40px' - setInputHeight(40) - sendMessage(content, imageUint, reply?.id) - setImageUint(undefined) - setClearComponent('') - if (inputRef.current) { - inputRef.current.innerHTML = '' - } - setContent('') - if (setReply) setReply(undefined) - if (createChat && group) { - createChat(group) - setChatState(ChatState.ChatBody) - } - } - }, - [ - content, - imageUint, - createChat, - group, - sendMessage, - reply?.id, - setChatState, - setReply, - ] - ) - - const [selectedElement, setSelectedElement] = useState<{ - element: Selection | null - start: number - end: number - text: string - node: Node | null - }>({ element: null, start: 0, end: 0, text: '', node: null }) - - const handleCursorChange = useCallback(() => { - const element = document.getSelection() - if (element && element.rangeCount > 0) { - const selection = element?.getRangeAt(0)?.startOffset - const text = element?.anchorNode?.textContent - if (selection && text) { - const end = text.indexOf(' ', selection) - const start = text.lastIndexOf(' ', selection - 1) - setSelectedElement({ - element, - start, - end, - text, - node: element.anchorNode, - }) - const substring = text.substring( - start > -1 ? start + 1 : 0, - end > -1 ? end : undefined - ) - if (substring.startsWith('@')) { - setQuery(substring.slice(1)) - } else { - setQuery('') - } - } - } - }, []) - - useEffect(handleCursorChange, [content, handleCursorChange]) - - const addMention = useCallback( - (contact: string) => { - if (inputRef?.current) { - const { element, start, end, text, node } = selectedElement - if (element && text && node) { - const firstSlice = text.slice(0, start > -1 ? start : 0) - const secondSlice = text.slice(end > -1 ? end : content.length) - const replaceContent = `${firstSlice} @${contact}${secondSlice}` - const spaceElement = document.createTextNode(' ') - const contactElement = document.createElement('span') - contactElement.innerText = `@${contact}` - - if (contactElement && element.rangeCount > 0) { - const range = element.getRangeAt(0) - range.setStart(node, start > -1 ? start : 0) - if (end === -1 || end > text.length) { - range.setEnd(node, text.length) - } else { - range.setEnd(node, end) - } - range.deleteContents() - if (end === -1) { - range.insertNode(spaceElement.cloneNode()) - } - range.insertNode(contactElement) - if (start > -1) { - range.insertNode(spaceElement.cloneNode()) - } - range.collapse() - } - inputRef.current.focus() - setQuery('') - setContent(replaceContent) - resizeTextArea(inputRef.current) - } - } - }, - [inputRef, content, selectedElement, resizeTextArea] - ) - - return ( - - - - - { - const fileReader = new FileReader() - fileReader.onloadend = s => { - const arr = new Uint8Array(s.target?.result as ArrayBuffer) - setImageUint(arr) - } - - if (e?.target?.files?.[0]) { - if (e.target.files[0].size < 1024 * 1024) { - fileReader.readAsArrayBuffer(e.target.files[0]) - } else { - setModal(true) - } - } - }} - /> - - - {reply && ( - - - {' '} - {' '} - {contacts[reply.sender]?.customName ?? - contacts[reply.sender].trueName} - - {reply.content} - {reply.image && } - { - if (setReply) setReply(undefined) - }} - > - {' '} - - - - )} - - - {image && ( - - - setImageUint(undefined)}> - - - - )} - {narrow && !identity ? ( - setCreationStartModal(true)}> - Click here to join discussion - - ) : ( - - )} - {query && ( - - )} - - - - { - if (!disabled) setShowEmoji(!showEmoji) - }} - disabled={disabled} - > - - - - - - - - - - - - - - - ) -} - -const InputWrapper = styled.div` - display: flex; - flex-direction: column; - width: 100%; - position: relative; -` - -const EmojiWrapper = styled.div` - position: relative; -` - -const View = styled.div` - display: flex; - align-items: flex-end; - padding: 6px 8px 6px 10px; - position: relative; - - &.creation { - padding: 0; - } -` - -const InputArea = styled.div` - position: relative; - display: flex; - flex-direction: column; - width: 100%; - max-height: 438px; - padding: 2px; - background: ${({ theme }) => theme.inputColor}; - border-radius: 16px 16px 4px 16px; -` - -const Row = styled.div` - position: relative; - display: flex; - align-items: center; - width: 100%; - max-height: 438px; - padding-right: 6px; - background: ${({ theme }) => theme.inputColor}; - border-radius: 16px 16px 4px 16px; -` - -const InputButtons = styled.div` - display: flex; - align-self: flex-end; - - button + button { - margin-left: 4px; - } -` - -const ImageWrapper = styled.div` - width: 64px; - position: relative; -` -const ImagePreview = styled.img` - width: 64px; - height: 64px; - border-radius: 16px 16px 4px 16px; - margin-left: 8px; - margin-top: 9px; -` - -const ClearImgBtn = styled(ClearBtn)` - width: 24px; - height: 24px; - top: 4px; - right: -20px; - transform: none; - padding: 0; - border: 2px solid ${({ theme }) => theme.inputColor}; - background-color: ${({ theme }) => theme.inputColor}; -` - -const Input = styled.div` - display: block; - width: 100%; - height: 40px; - max-height: 438px; - overflow: auto; - white-space: pre-wrap; - overflow-wrap: anywhere; - padding: 8px 0 8px 12px; - background: ${({ theme }) => theme.inputColor}; - border: 1px solid ${({ theme }) => theme.inputColor}; - color: ${({ theme }) => theme.primary}; - border-radius: 16px 16px 4px 16px; - outline: none; - - ${textMediumStyles}; - - &.disabled { - color: ${({ theme }) => theme.secondary}; - cursor: default; - } - - &:focus { - outline: none; - caret-color: ${({ theme }) => theme.notificationColor}; - } - - &::-webkit-scrollbar { - width: 0; - } - - & > span { - display: inline; - color: ${({ theme }) => theme.mentionColor}; - background: ${({ theme }) => theme.mentionBg}; - border-radius: 4px; - font-weight: 500; - position: relative; - padding: 0 2px; - cursor: pointer; - - &:hover { - background: ${({ theme }) => theme.mentionBgHover}; - cursor: default; - } - } -` - -const AddPictureInputWrapper = styled.div` - position: relative; - display: flex; - justify-content: center; - align-items: center; - width: 32px; - height: 32px; - margin-right: 4px; - - & > input[type='file']::-webkit-file-upload-button { - cursor: pointer; - } - - & > input:disabled::-webkit-file-upload-button { - cursor: default; - } -` - -const AddPictureInput = styled.input` - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - opacity: 0; -` - -const ChatButton = styled.button` - width: 32px; - height: 32px; - - &:disabled { - cursor: default; - } -` - -const CloseButton = styled(ChatButton)` - position: absolute; - top: 0; - right: 0; -` - -const ReplyWrapper = styled.div` - display: flex; - flex-direction: column; - width: 100%; - padding: 6px 12px; - background: rgba(0, 0, 0, 0.1); - color: ${({ theme }) => theme.primary}; - border-radius: 14px 14px 4px 14px; - position: relative; -` - -export const ReplyTo = styled.div` - display: flex; - align-items: center; - font-weight: 500; - ${textSmallStyles}; -` - -export const ReplyOn = styled.div` - width: 100%; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - ${textSmallStyles}; -` - -const JoinBtn = styled.button` - color: ${({ theme }) => theme.secondary}; - background: ${({ theme }) => theme.inputColor}; - border: none; - outline: none; - padding: 0 10px; - text-align: start; - - ${textMediumStyles}; -` diff --git a/packages/status-react/src/components/Chat-legacy/ChatMessageContent.tsx b/packages/status-react/src/components/Chat-legacy/ChatMessageContent.tsx deleted file mode 100644 index f19b4276..00000000 --- a/packages/status-react/src/components/Chat-legacy/ChatMessageContent.tsx +++ /dev/null @@ -1,233 +0,0 @@ -import React, { useEffect, useMemo, useRef, useState } from 'react' - -import { decode } from 'html-entities' -import styled from 'styled-components' - -import { useUserPublicKey } from '../../contexts/identityProvider' -import { useMessengerContext } from '../../contexts/messengerProvider' -import { useClickOutside } from '../../hooks/useClickOutside' -import { ContactMenu } from '../Form/ContactMenu' -import { ImageMenu } from '../Form/ImageMenu' -import { textMediumStyles, textSmallStyles } from '../Text' - -import type { ChatMessage } from '../../models/ChatMessage' -import type { Metadata } from '../../models/Metadata' - -interface MentionProps { - id: string - setMentioned: (val: boolean) => void - className?: string -} - -export function Mention({ id, setMentioned, className }: MentionProps) { - const { contacts } = useMessengerContext() - const contact = useMemo(() => contacts[id.slice(1)], [id, contacts]) - const [showMenu, setShowMenu] = useState(false) - const userPK = useUserPublicKey() - - useEffect(() => { - if (userPK && contact) { - if (contact.id === userPK) setMentioned(true) - } - }, [contact, userPK, setMentioned]) - - const ref = useRef(null) - useClickOutside(ref, () => setShowMenu(false)) - - if (!contact) return <>{id} - return ( - setShowMenu(!showMenu)} - className={className} - ref={ref} - > - {`@${contact?.customName ?? contact.trueName}`} - {showMenu && } - - ) -} - -type ChatMessageContentProps = { - message: ChatMessage - setImage: (image: string) => void - setLinkOpen: (link: string) => void - setMentioned: (val: boolean) => void -} - -export function ChatMessageContent({ - message, - setImage, - setLinkOpen, - setMentioned, -}: ChatMessageContentProps) { - const { content, image } = useMemo(() => message, [message]) - const [elements, setElements] = useState<(string | React.ReactElement)[]>([ - content, - ]) - const [link, setLink] = useState(undefined) - const [openGraph] = useState(undefined) - - useEffect(() => { - let link - const split = content.split(' ') - const newSplit = split.flatMap((element, idx) => { - if (element.startsWith('http://') || element.startsWith('https://')) { - link = element - return [ - setLinkOpen(element)}> - {element} - , - ' ', - ] - } - if (element.startsWith('@')) { - return [ - , - ' ', - ] - } - return [element, ' '] - }) - newSplit.pop() - setLink(link) - setElements(newSplit) - }, [content, setLink, setMentioned, setElements, setLinkOpen]) - - // useEffect(() => { - // const updatePreview = async () => { - // if (link && fetchMetadata) { - // try { - // const metadata = await fetchMetadata(link) - // if (metadata) { - // setOpenGraph(metadata) - // } - // } catch { - // return - // } - // } - // } - // updatePreview() - // }, [link, fetchMetadata]) - - return ( - -
{elements.map(el => el)}
- {image && ( - - { - setImage(image) - }} - /> - - - )} - {openGraph && ( - setLinkOpen(link ?? '')}> - - {openGraph['og:title']} - - {openGraph['og:site_name']} - - - )} -
- ) -} - -const MessageImageWrapper = styled.div` - width: 147px; - height: 196px; - margin-top: 8px; - position: relative; -` - -const MessageImage = styled.img` - width: 100%; - height: 100%; - object-fit: cover; - border-radius: 16px; - cursor: pointer; -` - -const PreviewSiteNameWrapper = styled.div` - font-family: 'Inter'; - font-style: normal; - font-weight: normal; - font-size: 12px; - line-height: 16px; - letter-spacing: 0.1px; - margin-top: 2px; - color: #939ba1; - margin-left: 12px; -` - -const PreviewTitleWrapper = styled.div` - margin-top: 7px; - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; - font-family: Inter; - font-style: normal; - font-weight: 500; - width: 290px; - margin-left: 12px; - - ${textSmallStyles} -` - -const PreviewImage = styled.img` - border-radius: 15px 15px 15px 4px; - width: 305px; - height: 170px; -` - -const PreviewWrapper = styled.div` - margin-top: 9px; - background: #ffffff; - width: 305px; - height: 224px; - border: 1px solid #eef2f5; - box-sizing: border-box; - border-radius: 16px 16px 16px 4px; - display: flex; - flex-direction: column; - padding: 0px; -` - -const ContentWrapper = styled.div` - display: flex; - flex-direction: column; -` - -const MentionBLock = styled.div` - display: inline-flex; - color: ${({ theme }) => theme.mentionColor}; - background: ${({ theme }) => theme.mentionBgHover}; - border-radius: 4px; - font-weight: 500; - position: relative; - padding: 0 2px; - cursor: pointer; - - &:hover { - background: ${({ theme }) => theme.mentionHover}; - } - - &.activity { - max-width: 488px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - - ${textMediumStyles} -` - -const Link = styled.a` - text-decoration: underline; - cursor: pointer; - color: ${({ theme }) => theme.memberNameColor}; -` diff --git a/packages/status-react/src/components/Chat-legacy/ChatTopbar.tsx b/packages/status-react/src/components/Chat-legacy/ChatTopbar.tsx deleted file mode 100644 index 54184307..00000000 --- a/packages/status-react/src/components/Chat-legacy/ChatTopbar.tsx +++ /dev/null @@ -1,209 +0,0 @@ -import React, { useRef, useState } from 'react' - -import styled from 'styled-components' - -import { useMessengerContext } from '../../contexts/messengerProvider' -import { useNarrow } from '../../contexts/narrowProvider' -import { useClickOutside } from '../../hooks/useClickOutside' -import { - ActivityButton, - ActivityWrapper, -} from '../ActivityCenter/ActivityButton' -import { Channel } from '../Channels/Channel' -import { ChannelMenu } from '../Form/ChannelMenu' -import { ActivityIcon } from '../Icons/ActivityIcon' -import { MembersIcon } from '../Icons/MembersIcon' -import { MoreIcon } from '../Icons/MoreIcon' -import { CommunitySkeleton } from '../Skeleton/CommunitySkeleton' -import { Loading } from '../Skeleton/Loading' -import { ChatBodyState } from './ChatBody' -import { CommunitySidebar } from './CommunitySidebar' - -export function ChatTopbarLoading() { - const narrow = useNarrow() - - return ( - - - - - - - - {!narrow && ( - - - - )} - - - - - - - - - - - - ) -} - -type ChatTopbarProps = { - showState: ChatBodyState - onClick: () => void - switchShowState: (state: ChatBodyState) => void - showMembers: boolean - setEditGroup: React.Dispatch> -} - -export function ChatTopbar({ - showState, - onClick, - switchShowState, - showMembers, - setEditGroup, -}: ChatTopbarProps) { - const { activeChannel, loadingMessenger } = useMessengerContext() - - const narrow = useNarrow() - const [showChannelMenu, setShowChannelMenu] = useState(false) - - const ref = useRef(null) - useClickOutside(ref, () => setShowChannelMenu(false)) - - if (!activeChannel) { - return - } - - return ( - - - {!loadingMessenger ? ( - <> - {narrow && ( - - - - )} - - switchShowState(ChatBodyState.Channels)} - /> - - ) : ( - - - - )} - - - - {!narrow && activeChannel.type !== 'dm' && ( - - - - )} -
- setShowChannelMenu(!showChannelMenu)}> - - {showChannelMenu && ( - switchShowState(ChatBodyState.Members)} - setShowChannelMenu={setShowChannelMenu} - setEditGroup={setEditGroup} - className={`${narrow && 'narrow'}`} - /> - )} - -
- {!narrow && } -
- {loadingMessenger && } -
- ) -} - -const Topbar = styled.div` - display: flex; - justify-content: space-between; - align-items: flex-start; - padding: 5px 8px; - background: ${({ theme }) => theme.bodyBackgroundColor}; - position: relative; - - &.narrow { - width: 100%; - } -` - -const ChannelWrapper = styled.div` - display: flex; - align-items: center; - max-width: 80%; - - &.narrow { - width: calc(100% - 46px); - } -` - -const SkeletonWrapper = styled.div` - padding: 8px; -` - -const CommunityWrap = styled.div` - padding-right: 10px; - margin-right: 16px; - position: relative; - - &.narrow { - margin-right: 8px; - } - - &:after { - content: ''; - position: absolute; - right: 0; - top: 50%; - width: 2px; - height: 24px; - transform: translateY(-50%); - border-radius: 1px; - background: ${({ theme }) => theme.primary}; - opacity: 0.1; - } -` - -const MenuWrapper = styled.div` - display: flex; - align-items: center; - padding: 8px 0; -` - -export const TopBtn = styled.button` - width: 32px; - height: 32px; - border-radius: 8px; - padding: 0; - background: ${({ theme }) => theme.bodyBackgroundColor}; - position: relative; - - &:hover { - background: ${({ theme }) => theme.inputColor}; - } - - &:active { - background: ${({ theme }) => theme.sectionBackgroundColor}; - } - - &:disabled { - cursor: default; - } -` diff --git a/packages/status-react/src/components/Chat-legacy/CommunitySidebar.tsx b/packages/status-react/src/components/Chat-legacy/CommunitySidebar.tsx deleted file mode 100644 index 421add1f..00000000 --- a/packages/status-react/src/components/Chat-legacy/CommunitySidebar.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import React from 'react' - -import styled from 'styled-components' - -import { useMessengerContext } from '../../contexts/messengerProvider' -import { useModal } from '../../contexts/modalProvider' -import { CommunityIdentity } from '../CommunityIdentity' -import { CommunityModalName } from '../Modals/CommunityModal' -import { CommunitySkeleton } from '../Skeleton/CommunitySkeleton' - -interface CommunityProps { - className?: string -} - -export function CommunitySidebar({ className }: CommunityProps) { - const { communityData } = useMessengerContext() - const { setModal } = useModal(CommunityModalName) - - if (!communityData) { - return ( - - - - ) - } - - return ( - <> - - - ) -} - -const SkeletonWrapper = styled.div` - margin-bottom: 16px; -` diff --git a/packages/status-react/src/components/Chat-legacy/EmojiPicker.tsx b/packages/status-react/src/components/Chat-legacy/EmojiPicker.tsx deleted file mode 100644 index c8e017c9..00000000 --- a/packages/status-react/src/components/Chat-legacy/EmojiPicker.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import React from 'react' - -import { Picker } from 'emoji-mart' -import { useTheme } from 'styled-components' - -import { useLow } from '../../contexts/narrowProvider' -import { lightTheme } from '../../styles/themes' - -import type { Theme } from '~/src/types/theme' -import type { EmojiData } from 'emoji-mart' - -type EmojiPickerProps = { - showEmoji: boolean - addEmoji: (e: EmojiData) => void - bottom: number -} - -export function EmojiPicker({ showEmoji, addEmoji, bottom }: EmojiPickerProps) { - const theme = useTheme() as Theme - const low = useLow() - - if (showEmoji) { - return ( - - ) - } - return null -} diff --git a/packages/status-react/src/components/CommunityIdentity.tsx b/packages/status-react/src/components/CommunityIdentity.tsx deleted file mode 100644 index 10a448a8..00000000 --- a/packages/status-react/src/components/CommunityIdentity.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import React from 'react' - -import styled from 'styled-components' - -import { useMessengerContext } from '../contexts/messengerProvider' -import { textMediumStyles } from './Text' - -export interface CommunityIdentityProps { - subtitle: string - className?: string -} - -export const CommunityIdentity = ({ - subtitle, - className, -}: CommunityIdentityProps) => { - const { communityData } = useMessengerContext() - - return ( - - - {' '} - {communityData?.icon === undefined && - communityData?.name.slice(0, 1).toUpperCase()} - - - {communityData?.name} - {subtitle} - - - ) -} - -const Row = styled.div` - display: flex; -` - -export const Column = styled.div` - display: flex; - flex-direction: column; - align-items: flex-start; -` - -export const Logo = styled.div` - width: 36px; - height: 36px; - display: flex; - align-items: center; - justify-content: center; - flex-shrink: 0; - border-radius: 50%; - margin-right: 8px; - background-color: ${({ theme }) => theme.tertiary}; - background-size: cover; - background-repeat: no-repeat; - color: ${({ theme }) => theme.iconTextColor}; - font-weight: bold; - font-size: 15px; - line-height: 20px; -` - -const Name = styled.p` - font-family: 'Inter', sans-serif; - font-weight: 500; - text-align: left; - color: ${({ theme }) => theme.primary}; - white-space: nowrap; - - ${textMediumStyles} -` - -const Subtitle = styled.p` - font-family: 'Inter', sans-serif; - font-size: 12px; - line-height: 16px; - letter-spacing: 0.1px; - color: ${({ theme }) => theme.secondary}; -` diff --git a/packages/status-react/src/components/Form/ChannelMenu.tsx b/packages/status-react/src/components/Form/ChannelMenu.tsx deleted file mode 100644 index e6943be8..00000000 --- a/packages/status-react/src/components/Form/ChannelMenu.tsx +++ /dev/null @@ -1,194 +0,0 @@ -import React, { useMemo, useRef, useState } from 'react' - -import styled from 'styled-components' - -import { useMessengerContext } from '../../contexts/messengerProvider' -import { useModal } from '../../contexts/modalProvider' -import { useNarrow } from '../../contexts/narrowProvider' -import { useClickOutside } from '../../hooks/useClickOutside' -import { useContextMenu } from '../../hooks/useContextMenu' -import { AddMemberIcon } from '../Icons/AddMemberIcon' -import { CheckIcon } from '../Icons/CheckIcon' -import { DeleteIcon } from '../Icons/DeleteIcon' -import { DownloadIcon } from '../Icons/DownloadIcon' -import { EditIcon } from '../Icons/EditIcon' -import { LeftIcon } from '../Icons/LeftIcon' -import { MembersSmallIcon } from '../Icons/MembersSmallIcon' -import { MuteIcon } from '../Icons/MuteIcon' -import { NextIcon } from '../Icons/NextIcon' -import { ProfileIcon } from '../Icons/ProfileIcon' -import { EditModalName } from '../Modals/EditModal' -import { LeavingModalName } from '../Modals/LeavingModal' -import { ProfileModalName } from '../Modals/ProfileModal' -import { DropdownMenu, MenuItem, MenuSection, MenuText } from './DropdownMenu' -import { MuteMenu } from './MuteMenu' - -import type { ChannelData } from '../../models/ChannelData' - -interface ChannelMenuProps { - channel: ChannelData - setShowChannelMenu?: (val: boolean) => void - showNarrowMembers?: boolean - switchMemberList?: () => void - setEditGroup?: (val: boolean) => void - className?: string -} - -export const ChannelMenu = ({ - channel, - setShowChannelMenu, - showNarrowMembers, - switchMemberList, - setEditGroup, - className, -}: ChannelMenuProps) => { - const narrow = useNarrow() - const { clearNotifications, channelsDispatch } = useMessengerContext() - const { setModal } = useModal(EditModalName) - const { setModal: setLeavingModal } = useModal(LeavingModalName) - const { setModal: setProfileModal } = useModal(ProfileModalName) - const [showSubmenu, setShowSubmenu] = useState(false) - - const { showMenu, setShowMenu: setShowSideMenu } = useContextMenu( - channel.id + 'contextMenu' - ) - - const setShowMenu = useMemo( - () => (setShowChannelMenu ? setShowChannelMenu : setShowSideMenu), - [setShowChannelMenu, setShowSideMenu] - ) - - const ref = useRef(null) - useClickOutside(ref, () => setShowMenu(false)) - - if (showMenu || setShowChannelMenu) { - return ( - - {narrow && channel.type !== 'dm' && ( - { - if (switchMemberList) switchMemberList() - setShowMenu(false) - }} - > - - {showNarrowMembers ? 'Hide' : 'View'} Members - - )} - {channel.type === 'group' && ( - <> - { - if (setEditGroup) setEditGroup(true) - setShowMenu(false) - }} - > - - Add / remove from group - - setModal(true)}> - - Edit name and image - - - )} - {channel.type === 'dm' && ( - { - setProfileModal({ - id: channel.name, - renamingState: false, - requestState: false, - }) - setShowMenu(false) - }} - > - - View Profile - - )} - - { - if (channel.isMuted) { - channelsDispatch({ - type: 'ToggleMuted', - payload: channel.id, - }) - setShowMenu(false) - } - }} - onMouseEnter={() => { - if (!channel.isMuted) setShowSubmenu(true) - }} - onMouseLeave={() => { - if (!channel.isMuted) setShowSubmenu(false) - }} - > - - {!channel.isMuted && } - - {(channel.isMuted ? 'Unmute' : 'Mute') + - (channel.type === 'group' ? ' Group' : 'Chat')} - - {!channel.isMuted && showSubmenu && ( - - channelsDispatch({ - type: 'ToggleMuted', - payload: channel.id, - }) - } - className={className} - /> - )} - - clearNotifications(channel.id)}> - - Mark as Read - - - - Fetch Messages - - - {(channel.type === 'group' || channel.type === 'dm') && ( - { - setLeavingModal(true) - setShowMenu(false) - }} - > - {channel.type === 'group' && ( - - )} - {channel.type === 'dm' && ( - - )} - - {channel.type === 'group' ? 'Leave Group' : 'Delete Chat'} - - - )} - - ) - } else { - return null - } -} - -const ChannelDropdown = styled(DropdownMenu)` - top: calc(100% + 4px); - right: 0px; - - &.side { - top: 20px; - left: calc(100% - 35px); - right: unset; - } - - &.sideNarrow { - top: 20px; - right: 8px; - } -` diff --git a/packages/status-react/src/components/Form/ContactMenu.tsx b/packages/status-react/src/components/Form/ContactMenu.tsx deleted file mode 100644 index b0fc5697..00000000 --- a/packages/status-react/src/components/Form/ContactMenu.tsx +++ /dev/null @@ -1,163 +0,0 @@ -import React, { useMemo } from 'react' - -import styled from 'styled-components' - -import { useUserPublicKey } from '../../contexts/identityProvider' -import { useMessengerContext } from '../../contexts/messengerProvider' -import { useModal } from '../../contexts/modalProvider' -import { AddContactIcon } from '../Icons/AddContactIcon' -import { BlockSvg } from '../Icons/BlockIcon' -import { ChatSvg } from '../Icons/ChatIcon' -import { EditIcon } from '../Icons/EditIcon' -import { ProfileIcon } from '../Icons/ProfileIcon' -import { UntrustworthIcon } from '../Icons/UntrustworthIcon' -import { UserIcon } from '../Icons/UserIcon' -import { WarningSvg } from '../Icons/WarningIcon' -import { UserAddress } from '../Messages/Styles' -import { ProfileModalName } from '../Modals/ProfileModal' -import { textMediumStyles } from '../Text' -import { DropdownMenu, MenuItem, MenuText } from './DropdownMenu' - -type ContactMenuProps = { - id: string - setShowMenu: (val: boolean) => void -} - -export function ContactMenu({ id, setShowMenu }: ContactMenuProps) { - const userPK = useUserPublicKey() - const { contacts, contactsDispatch } = useMessengerContext() - const contact = useMemo(() => contacts[id], [id, contacts]) - const isUser = useMemo(() => { - if (userPK) { - return id === userPK - } else { - return false - } - }, [id, userPK]) - - const { setModal } = useModal(ProfileModalName) - - if (!contact) return null - return ( - - - - - {contact?.customName ?? contact.trueName} - {contact.isUntrustworthy && } - - {contact?.customName && ( - ({contact.trueName}) - )} - - {id.slice(0, 10)}...{id.slice(-3)} - - - - { - setModal({ id, renamingState: false, requestState: false }) - }} - > - - View Profile - - {!contact.isFriend && ( - { - setModal({ id, requestState: true }) - }} - > - - Send Contact Request - - )} - {contact.isFriend && ( - - - Send Message - - )} - { - setModal({ id, renamingState: true }) - }} - > - - Rename - - - - - contactsDispatch({ type: 'toggleTrustworthy', payload: { id } }) - } - > - - - {contact.isUntrustworthy - ? 'Remove Untrustworthy Mark' - : 'Mark as Untrustworthy'} - - - - {!contact.isFriend && !isUser && ( - { - contactsDispatch({ type: 'toggleBlocked', payload: { id } }) - setShowMenu(false) - }} - > - - - {contact.blocked ? 'Unblock User' : 'Block User'} - - - )} - - - ) -} - -const ContactDropdown = styled(DropdownMenu)` - top: 20px; - left: 0px; - width: 222px; -` - -const ContactInfo = styled.div` - display: flex; - flex-direction: column; - align-items: center; -` - -const MenuSection = styled.div` - margin-top: 5px; - padding-top: 5px; - border-top: 1px solid ${({ theme }) => theme.inputColor}; -` - -const UserNameWrapper = styled.div` - display: flex; - align-items: center; - margin-bottom: 4px; -` - -const UserName = styled.p` - color: ${({ theme }) => theme.primary}; - margin-right: 4px; - - ${textMediumStyles} -` - -const UserTrueName = styled.p` - color: ${({ theme }) => theme.primary}; - font-size: 12px; - line-height: 16px; - letter-spacing: 0.1px; - margin-top: 4px; -` diff --git a/packages/status-react/src/components/Form/CopyInput.tsx b/packages/status-react/src/components/Form/CopyInput.tsx deleted file mode 100644 index e801f21d..00000000 --- a/packages/status-react/src/components/Form/CopyInput.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import React from 'react' - -import { copy } from '../../utils/copy' -import { reduceString } from '../../utils/reduceString' -import { - ButtonWrapper, - InputBtn, - InputWrapper, - Label, - Text, - Wrapper, -} from './inputStyles' - -interface CopyInputProps { - label?: string - value: string -} - -export const CopyInput = ({ label, value }: CopyInputProps) => ( - - {label && } - - {reduceString(value, 15, 15)} - - copy(value)}>Copy - - - -) diff --git a/packages/status-react/src/components/Form/DropdownMenu.tsx b/packages/status-react/src/components/Form/DropdownMenu.tsx deleted file mode 100644 index 9b24531a..00000000 --- a/packages/status-react/src/components/Form/DropdownMenu.tsx +++ /dev/null @@ -1,97 +0,0 @@ -import React from 'react' - -import styled from 'styled-components' - -import { textSmallStyles } from '../Text' - -import type { ReactNode } from 'react' - -type DropdownMenuProps = { - children: ReactNode - className?: string - style?: { top: number; left: number } - menuRef?: React.MutableRefObject - id?: string -} - -export function DropdownMenu({ - children, - className, - style, - menuRef, - id, -}: DropdownMenuProps) { - return ( - - {children} - - ) -} - -const MenuBlock = styled.div` - width: 207px; - background: ${({ theme }) => theme.bodyBackgroundColor}; - box-shadow: 0px 2px 4px rgba(0, 34, 51, 0.16), - 0px 4px 12px rgba(0, 34, 51, 0.08); - border-radius: 8px; - padding: 8px 0; - position: absolute; - z-index: 2; -` - -const MenuList = styled.ul` - list-style: none; -` - -export const MenuItem = styled.li` - width: 100%; - display: flex; - align-items: center; - padding: 8px 8px 8px 14px; - cursor: pointer; - color: ${({ theme }) => theme.primary}; - position: relative; - - &:hover, - &:hover > span { - background: ${({ theme }) => theme.border}; - } - - &.picker:hover { - background: ${({ theme }) => theme.bodyBackgroundColor}; - } - - & > svg.red { - fill: ${({ theme }) => theme.redColor}; - } -` - -export const MenuText = styled.span` - margin-left: 6px; - color: ${({ theme }) => theme.primary}; - - &.red { - color: ${({ theme }) => theme.redColor}; - } - - ${textSmallStyles} -` - -export const MenuSection = styled.div` - padding: 4px 0; - margin: 4px 0; - border-top: 1px solid ${({ theme }) => theme.inputColor}; - border-bottom: 1px solid ${({ theme }) => theme.inputColor}; - - &.channel { - padding: 0; - margin: 0; - border: none; - } - - &.message { - padding: 4px 0 0; - margin: 4px 0 0; - border-bottom: none; - } -` diff --git a/packages/status-react/src/components/Form/ImageMenu.tsx b/packages/status-react/src/components/Form/ImageMenu.tsx deleted file mode 100644 index 19420076..00000000 --- a/packages/status-react/src/components/Form/ImageMenu.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import React, { useRef } from 'react' - -import styled from 'styled-components' - -import { useClickOutside } from '../../hooks/useClickOutside' -import { useContextMenu } from '../../hooks/useContextMenu' -import { copyImg } from '../../utils/copyImg' -import { downloadImg } from '../../utils/downloadImg' -import { CopyIcon } from '../Icons/CopyIcon' -import { DownloadIcon } from '../Icons/DownloadIcon' -import { DropdownMenu, MenuItem, MenuText } from './DropdownMenu' - -interface ImageMenuProps { - imageId: string -} - -export const ImageMenu = ({ imageId }: ImageMenuProps) => { - const { showMenu, setShowMenu } = useContextMenu(imageId) - - const ref = useRef(null) - useClickOutside(ref, () => setShowMenu(false)) - - return showMenu ? ( - - copyImg(imageId)}> - Copy image - - downloadImg(imageId)}> - - Download image - - - ) : ( - <> - ) -} - -const ImageDropdown = styled(DropdownMenu)` - width: 176px; - left: 120px; - top: 46px; -` diff --git a/packages/status-react/src/components/Form/LoginInstructions.tsx b/packages/status-react/src/components/Form/LoginInstructions.tsx deleted file mode 100644 index c0a6c189..00000000 --- a/packages/status-react/src/components/Form/LoginInstructions.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import React from 'react' - -import styled from 'styled-components' - -import { MobileIcon } from '../Icons/MobileIcon' -import { ProfileIcon } from '../Icons/ProfileIcon' -import { ScanIcon } from '../Icons/ScanIcon' -import { textMediumStyles } from '../Text' - -interface LoginInstructionsProps { - mobileFlow: boolean -} - -export function LoginInstructions({ mobileFlow }: LoginInstructionsProps) { - return ( - - - Open Status App on your {mobileFlow ? 'mobile' : 'desktop'} - - - Navigate yourself to{' '} - - {' '} - Profile - {' '} - tab - - - Select{' '} - - - {' '} - Sync Settings - - - Tap{' '} - - {' '} - {' '} - {' '} - {mobileFlow ? 'Scan' : 'Display'} sync code - - - {mobileFlow - ? 'Scan the sync code from this screen' - : 'Paste the sync code above'}{' '} - ↑ - - - ) -} - -const Instructions = styled.ol` - color: ${({ theme }) => theme.secondary}; - margin: auto 0; - list-style-type: decimal; - counter-reset: ollist; - - ${textMediumStyles} -` - -const InstructionStep = styled.li` - display: flex; - align-items: center; - - & + & { - margin-top: 10px; - } - - & > span { - color: ${({ theme }) => theme.tertiary}; - } - - &::before { - counter-increment: ollist; - content: counter(ollist) '.'; - margin-right: 4px; - } -` - -const InstructionIcon = styled.div` - width: 40px; - height: 40px; - display: inline-flex; - flex-direction: column; - align-items: center; - justify-content: center; - border-radius: 50%; - background: ${({ theme }) => theme.buttonBg}; - color: ${({ theme }) => theme.tertiary}; - font-size: 8px; - line-height: 10px; - margin: 0 6px; -` diff --git a/packages/status-react/src/components/Form/MessageMenu.tsx b/packages/status-react/src/components/Form/MessageMenu.tsx deleted file mode 100644 index a4d9c28c..00000000 --- a/packages/status-react/src/components/Form/MessageMenu.tsx +++ /dev/null @@ -1,120 +0,0 @@ -import React, { useMemo, useRef } from 'react' - -import styled from 'styled-components' - -import { useUserPublicKey } from '../../contexts/identityProvider' -import { useMessengerContext } from '../../contexts/messengerProvider' -import { useClickOutside } from '../../hooks/useClickOutside' -import { useClickPosition } from '../../hooks/useClickPosition' -import { useContextMenu } from '../../hooks/useContextMenu' -import { DeleteIcon } from '../Icons/DeleteIcon' -import { EditIcon } from '../Icons/EditIcon' -import { PinIcon } from '../Icons/PinIcon' -import { ReplySvg } from '../Icons/ReplyIcon' -import { ReactionPicker } from '../Reactions/ReactionPicker' -import { DropdownMenu, MenuItem, MenuSection, MenuText } from './DropdownMenu' - -import type { Reply } from '../../hooks/useReply' -import type { ChatMessage } from '../../models/ChatMessage' -import type { BaseEmoji } from 'emoji-mart' - -interface MessageMenuProps { - message: ChatMessage - messageReactions: BaseEmoji[] - setMessageReactions: React.Dispatch> - setReply: (val: Reply | undefined) => void - messageRef: React.MutableRefObject -} - -export const MessageMenu = ({ - message, - messageReactions, - setMessageReactions, - setReply, - messageRef, -}: MessageMenuProps) => { - const userPK = useUserPublicKey() - const { activeChannel } = useMessengerContext() - const { showMenu, setShowMenu } = useContextMenu(message.id) - const { topPosition, leftPosition } = useClickPosition(messageRef) - - const menuStyle = useMemo(() => { - return { - top: topPosition, - left: leftPosition, - } - }, [topPosition, leftPosition]) - - const ref = useRef(null) - useClickOutside(ref, () => setShowMenu(false)) - - const userMessage = useMemo( - () => !!userPK && message.sender === userPK, - [userPK, message] - ) - - return userPK && showMenu ? ( - - - - - - { - setReply({ - sender: message.sender, - content: message.content, - image: message.image, - id: message.id, - }) - setShowMenu(false) - }} - > - - Reply - - - {userMessage && ( - { - setShowMenu(false) - }} - > - - Edit - - )} - {activeChannel?.type !== 'channel' && ( - { - setShowMenu(false) - }} - > - - Pin - - )} - - {userMessage && ( - { - setShowMenu(false) - }} - > - - Delete message - - )} - - ) : ( - <> - ) -} - -const MessageDropdown = styled(DropdownMenu)` - width: 176px; -` diff --git a/packages/status-react/src/components/Form/MuteMenu.tsx b/packages/status-react/src/components/Form/MuteMenu.tsx deleted file mode 100644 index ce4229c1..00000000 --- a/packages/status-react/src/components/Form/MuteMenu.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import React, { useCallback } from 'react' - -import styled from 'styled-components' - -import { DropdownMenu, MenuItem, MenuText } from './DropdownMenu' - -interface SubMenuProps { - setIsMuted: (val: boolean) => void - className?: string -} - -export const MuteMenu = ({ setIsMuted, className }: SubMenuProps) => { - const muteChannel = useCallback( - (timeout: number) => { - setIsMuted(true) - const timer = setTimeout(() => setIsMuted(false), timeout * 6000000) - return () => { - clearTimeout(timer) - } - }, - [setIsMuted] - ) - - return ( - - muteChannel(0.25)}> - For 15 min - - muteChannel(1)}> - For 1 hour - - muteChannel(8)}> - For 8 hours - - muteChannel(24)}> - For 24 hours - - setIsMuted(true)}> - Until I turn it back on - - - ) -} - -const MuteDropdown = styled(DropdownMenu)` - width: 176px; - top: 100%; - right: -60px; - z-index: 3; - - &.side { - width: 176px; - top: -8px; - left: 100%; - right: unset; - } - - &.narrow, - &.sideNarrow { - width: 176px; - top: 100%; - right: -16px; - z-index: 3; - } -` diff --git a/packages/status-react/src/components/Form/NameError.tsx b/packages/status-react/src/components/Form/NameError.tsx deleted file mode 100644 index 5197ef30..00000000 --- a/packages/status-react/src/components/Form/NameError.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import React from 'react' - -import styled from 'styled-components' - -import { NameErrors } from '../../hooks/useNameError' -import { Hint } from '../Modals/ModalStyle' - -type NameErrorProps = { - error: NameErrors -} - -export function NameError({ error }: NameErrorProps) { - switch (error) { - case NameErrors.NoError: - return null - case NameErrors.NameExists: - return ( - - Sorry, the name you have chosen is not allowed, try picking another - username - - ) - case NameErrors.BadCharacters: - return ( - - Only letters, numbers, underscores and hypens allowed - - ) - case NameErrors.EndingWithEth: - return ( - - Usernames ending with {'"_eth"'} or {'"-eth"'} are not allowed - - ) - case NameErrors.TooLong: - return 24 character username limit - } -} - -const ErrorText = styled(Hint)` - color: ${({ theme }) => theme.redColor}; - text-align: center; - width: 328px; - margin: 8px 0; -` diff --git a/packages/status-react/src/components/Form/PasteInput.tsx b/packages/status-react/src/components/Form/PasteInput.tsx deleted file mode 100644 index 821cdfdc..00000000 --- a/packages/status-react/src/components/Form/PasteInput.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import React from 'react' - -import styled from 'styled-components' - -import { paste } from '../../utils/paste' -import { - ButtonWrapper, - InputBtn, - inputStyles, - InputWrapper, - Label, - Wrapper, -} from './inputStyles' - -interface PasteInputProps { - label: string -} - -export const PasteInput = ({ label }: PasteInputProps) => ( - - - - - - paste('pasteInput')}>Paste - - - -) - -const Input = styled.input` - ${inputStyles} - width: 100%; - - &:focus { - outline: none; - } - - border: none; -` diff --git a/packages/status-react/src/components/Form/TokenRequirement.tsx b/packages/status-react/src/components/Form/TokenRequirement.tsx deleted file mode 100644 index 7a087de2..00000000 --- a/packages/status-react/src/components/Form/TokenRequirement.tsx +++ /dev/null @@ -1,132 +0,0 @@ -import React from 'react' - -import styled from 'styled-components' - -import { useMessengerContext } from '../../contexts/messengerProvider' -import { textMediumStyles } from '../Text' - -const communityRequirements = { - requirements: [ - { - name: 'STN', - amount: 10, - logo: 'https://status.im/img/logo.svg', - }, - ], - alternativeRequirements: [ - { - name: 'ETH', - amount: 1, - logo: 'https://ethereum.org/static/a110735dade3f354a46fc2446cd52476/db4de/eth-home-icon.webp', - }, - { - name: 'MKR', - amount: 10, - logo: 'https://cryptologos.cc/logos/maker-mkr-logo.svg?v=017', - }, - ], -} - -export function TokenRequirement() { - const { communityData } = useMessengerContext() - return ( - - - To join {communityData?.name} community chat you need to - hold: - - - {communityRequirements.requirements.map(req => ( - - - - {req.amount} {req.name}{' '} - - - ))} - - {communityRequirements.alternativeRequirements && or} - - {communityRequirements.alternativeRequirements.map(req => ( - - - - {req.amount} {req.name}{' '} - - - ))} - - - ) -} - -const Wrapper = styled.div` - display: flex; - flex-direction: column; - align-items: center; - height: 50%; -` - -const Text = styled.p` - color: ${({ theme }) => theme.primary}; - text-align: center; - margin-bottom: 16px; - - & > span { - font-weight: 700; - } - - ${textMediumStyles} -` - -const Requirement = styled.div` - display: flex; - align-items: center; - border-radius: 16px; - padding: 2px 12px 2px 2px; - background: ${({ theme }) => theme.buttonBg}; - color: ${({ theme }) => theme.tertiary}; - - &.denial { - background: ${({ theme }) => theme.buttonNoBgHover}; - color: ${({ theme }) => theme.redColor}; - } - - & + & { - margin-left: 18px; - } -` - -const Amount = styled.p` - font-weight: 500; - text-transform: uppercase; - margin-left: 6px; - - ${textMediumStyles} -` - -const Row = styled.div` - display: flex; - align-items: center; - margin-bottom: 16px; -` - -const Logo = styled.div` - width: 28px; - height: 28px; - display: flex; - align-items: center; - justify-content: center; - flex-shrink: 0; - border-radius: 50%; - background-size: cover; - background-repeat: no-repeat; -` diff --git a/packages/status-react/src/components/Form/Tooltip.tsx b/packages/status-react/src/components/Form/Tooltip.tsx deleted file mode 100644 index 9b95feb6..00000000 --- a/packages/status-react/src/components/Form/Tooltip.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import React from 'react' - -import styled from 'styled-components' - -import { TipIcon } from '../Icons/TipIcon' -import { textSmallStyles } from '../Text' - -type TooltipProps = { - tip: string - className?: string -} - -export function Tooltip({ tip, className }: TooltipProps) { - return ( - - - {tip} - - - - ) -} - -const TooltipWrapper = styled.div` - width: max-content; - position: absolute; - top: calc(-100% - 12px); - left: 50%; - transform: translateX(-50%); - visibility: hidden; - - &.read { - left: 18%; - } - - &.muted { - top: calc(100% + 8px); - z-index: 10; - } -` -const TooltipBlock = styled.div` - background: ${({ theme }) => theme.primary}; - border-radius: 8px; - position: relative; - padding: 8px; -` - -const TooltipText = styled.p` - font-weight: 500; - color: ${({ theme }) => theme.bodyBackgroundColor}; - ${textSmallStyles}; -` diff --git a/packages/status-react/src/components/Form/inputStyles.ts b/packages/status-react/src/components/Form/inputStyles.ts deleted file mode 100644 index 2e47973b..00000000 --- a/packages/status-react/src/components/Form/inputStyles.ts +++ /dev/null @@ -1,93 +0,0 @@ -import styled, { css } from 'styled-components' - -import { textMediumStyles, textSmallStyles } from '../Text' - -export const inputStyles = css` - background: ${({ theme }) => theme.inputColor}; - border-radius: 8px; - border: 1px solid ${({ theme }) => theme.inputColor}; - color: ${({ theme }) => theme.primary}; - outline: none; - - ${textMediumStyles} - - &:focus { - outline: 1px solid ${({ theme }) => theme.tertiary}; - caret-color: ${({ theme }) => theme.notificationColor}; - } -` - -export const Label = styled.p` - margin-bottom: 7px; - font-weight: 500; - display: flex; - align-items: center; - color: ${({ theme }) => theme.primary}; - - ${textSmallStyles} -` - -export const InputWrapper = styled.div` - width: 100%; -` - -export const Wrapper = styled.div` - position: relative; - padding: 14px 70px 14px 8px; - background: ${({ theme }) => theme.inputColor}; - border-radius: 8px; -` - -export const Text = styled.p` - color: ${({ theme }) => theme.primary}; - - ${textMediumStyles} -` - -export const ButtonWrapper = styled.div` - position: absolute; - top: 50%; - right: 0; - display: flex; - align-items: center; - justify-content: center; - height: 100%; - width: 70px; - transform: translateY(-50%); - background: ${({ theme }) => theme.inputColor}; - border-radius: 8px; -` - -export const InputBtn = styled.button` - padding: 6px 12px; - font-size: 12px; - line-height: 16px; - letter-spacing: 0.1px; - color: ${({ theme }) => theme.tertiary}; - background: ${({ theme }) => theme.buttonBg}; - border: 1px solid ${({ theme }) => theme.tertiary}; - border-radius: 6px; -` - -export const NameInputWrapper = styled.div` - position: relative; -` - -export const NameInput = styled.input` - width: 328px; - padding: 11px 16px; - - ${inputStyles} -` - -export const ClearBtn = styled.button` - position: absolute; - right: 16px; - top: 50%; - transform: translateY(-50%); - border-radius: 50%; - - & > svg { - fill: ${({ theme }) => theme.secondary}; - } -` diff --git a/packages/status-react/src/components/Icons/ColorChatIcon.tsx b/packages/status-react/src/components/Icons/ColorChatIcon.tsx deleted file mode 100644 index d5d659be..00000000 --- a/packages/status-react/src/components/Icons/ColorChatIcon.tsx +++ /dev/null @@ -1,149 +0,0 @@ -import React from 'react' - -import styled from 'styled-components' - -type ColorChatSvgProps = { - width: number - height: number -} - -export function ColorChatSvg({ width, height }: ColorChatSvgProps) { - return ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ) -} - -export const ColorChatIcon = () => { - return -} - -const Icon = styled(ColorChatSvg)` - & > path { - fill: ${({ theme }) => theme.tertiary}; - } - - &:hover > path { - fill: ${({ theme }) => theme.bodyBackgroundColor}; - } -` diff --git a/packages/status-react/src/components/Members/Member.tsx b/packages/status-react/src/components/Members/Member.tsx deleted file mode 100644 index 6102eb96..00000000 --- a/packages/status-react/src/components/Members/Member.tsx +++ /dev/null @@ -1,126 +0,0 @@ -import React, { useRef, useState } from 'react' - -import styled from 'styled-components' - -import { useIdentity } from '../../contexts/identityProvider' -import { useClickOutside } from '../../hooks/useClickOutside' -import { ContactMenu } from '../Form/ContactMenu' -import { IconBtn, UserAddress } from '../Messages/Styles' -import { UserLogo } from './UserLogo' - -import type { Contact } from '../../models/Contact' - -interface MemberProps { - contact: Contact - isOnline?: boolean - isYou?: boolean - onClick?: () => void -} - -export function Member({ contact, isOnline, isYou, onClick }: MemberProps) { - const identity = useIdentity() - - const [showMenu, setShowMenu] = useState(false) - - const ref = useRef(null) - useClickOutside(ref, () => setShowMenu(false)) - - return ( - - { - if (identity) setShowMenu(e => !e) - }} - ref={ref} - > - {showMenu && } - - - - {contact?.customName ?? contact.trueName} - - {contact.id.slice(0, 5)}...{contact.id.slice(-3)} - - - - ) -} - -export const MemberData = styled.div` - display: flex; - align-items: center; - margin-bottom: 16px; - cursor: pointer; - - &.you { - margin-bottom: 0; - cursor: default; - } -` - -export const MemberName = styled.p` - font-weight: 500; - font-size: 15px; - line-height: 22px; - color: ${({ theme }) => theme.primary}; - opacity: 0.7; - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; -` - -export const MemberIcon = styled(IconBtn)` - width: 29px; - height: 29px; - - &.offline { - &::after { - content: ''; - position: absolute; - right: -1px; - bottom: -2px; - width: 7px; - height: 7px; - border-radius: 50%; - background-color: ${({ theme }) => theme.secondary}; - border: 2px solid ${({ theme }) => theme.bodyBackgroundColor}; - } - } - - &.online { - &::after { - content: ''; - position: absolute; - right: -1px; - bottom: -2px; - width: 7px; - height: 7px; - border-radius: 50%; - background-color: #4ebc60; - border: 2px solid ${({ theme }) => theme.bodyBackgroundColor}; - } - } -` - -const Column = styled.div` - display: flex; - flex-direction: column; - margin-left: 8px; - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; -` diff --git a/packages/status-react/src/components/Members/Members.tsx b/packages/status-react/src/components/Members/Members.tsx deleted file mode 100644 index 4cfa9df8..00000000 --- a/packages/status-react/src/components/Members/Members.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import React, { useMemo } from 'react' - -import styled from 'styled-components' - -import { useMessengerContext } from '../../contexts/messengerProvider' -import { MembersList } from './MembersList' - -export function Members() { - const { activeChannel } = useMessengerContext() - const heading = useMemo( - () => - activeChannel && activeChannel?.type === 'group' - ? 'Group members' - : 'Members', - [activeChannel] - ) - - return ( - - {heading} - - - ) -} - -const MembersWrapper = styled.div` - width: 18%; - height: 100%; - min-width: 164px; - display: flex; - flex-direction: column; - background-color: ${({ theme }) => theme.sectionBackgroundColor}; - padding: 16px; - overflow-y: scroll; -` - -const MemberHeading = styled.h2` - font-weight: 500; - font-size: 15px; - line-height: 22px; - color: ${({ theme }) => theme.primary}; - margin-bottom: 16px; -` diff --git a/packages/status-react/src/components/Members/MembersList.tsx b/packages/status-react/src/components/Members/MembersList.tsx deleted file mode 100644 index 9ee3d2b1..00000000 --- a/packages/status-react/src/components/Members/MembersList.tsx +++ /dev/null @@ -1,130 +0,0 @@ -import React, { useMemo } from 'react' - -import styled from 'styled-components' - -import { useUserPublicKey } from '../../contexts/identityProvider' -import { useMessengerContext } from '../../contexts/messengerProvider' -import { useModal } from '../../contexts/modalProvider' -import { buttonStyles } from '../Buttons/buttonStyle' -import { LogoutIcon } from '../Icons/LogoutIcon' -import { LogoutModalName } from '../Modals/LogoutModal' -import { Member } from './Member' - -import type { Contact } from '../../models/Contact' - -export function MembersList() { - const { contacts, nickname, activeChannel } = useMessengerContext() - const userPK = useUserPublicKey() - const { setModal } = useModal(LogoutModalName) - - const members = useMemo(() => { - const contactsArray = Object.values(contacts) - if (userPK) { - if ( - activeChannel && - activeChannel.type === 'group' && - activeChannel.members - ) { - const returnContacts: Contact[] = [] - activeChannel.members.forEach(member => { - if (contacts[member.id] && member.id != userPK) { - returnContacts.push(contacts[member.id]) - } - }) - return returnContacts - } - return contactsArray.filter(e => e.id !== userPK) - } - return contactsArray - }, [activeChannel, contacts, userPK]) - - const onlineContacts = useMemo(() => members.filter(e => e.online), [members]) - const offlineContacts = useMemo( - () => members.filter(e => !e.online), - [members] - ) - - return ( - - {userPK && ( - - You - - - setModal(true)}> - - - - - )} - {onlineContacts.length > 0 && ( - - Online - {onlineContacts.map(contact => ( - - ))} - - )} - {offlineContacts.length > 0 && ( - - Offline - {offlineContacts.map(contact => ( - - ))} - - )} - - ) -} - -const MembersListWrap = styled.div` - display: flex; - flex-direction: column; - - &::-webkit-scrollbar { - width: 0; - } -` - -const MemberCategory = styled.div` - display: flex; - flex-direction: column; - margin-bottom: 16px; -` - -const MemberCategoryName = styled.h3` - font-weight: normal; - font-size: 13px; - line-height: 18px; - color: ${({ theme }) => theme.secondary}; - margin-bottom: 8px; -` - -const Row = styled.div` - display: flex; - align-items: center; - justify-content: space-between; -` - -const LogoutBtn = styled.button` - ${buttonStyles} - width: 32px; - height: 32px; - border-radius: 50%; - padding: 0; -` diff --git a/packages/status-react/src/components/Members/UserLogo.tsx b/packages/status-react/src/components/Members/UserLogo.tsx deleted file mode 100644 index 770617ce..00000000 --- a/packages/status-react/src/components/Members/UserLogo.tsx +++ /dev/null @@ -1,133 +0,0 @@ -import React, { useMemo } from 'react' - -import styled from 'styled-components' - -import type { Contact } from '../../models/Contact' - -type UserLogoProps = { - radius: number - colorWheel: [string, number][] - contact?: Contact - showOnlineStatus?: boolean - icon?: string -} - -export function UserLogo({ - icon, - contact, - radius, - colorWheel, - showOnlineStatus, -}: 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]) - - const letters = useMemo(() => { - if (contact && contact?.customName) { - return contact.customName.slice(0, 2) - } - if (contact && contact.trueName) { - return contact.trueName.slice(0, 2) - } - }, [contact]) - - const logoClassnName = useMemo(() => { - if (showOnlineStatus) { - if (contact && contact.online) { - return 'online' - } - return 'offline' - } - return '' - }, [contact, showOnlineStatus]) - - return ( - - - {!icon && {letters}} - - - ) -} - -const TextWrapper = styled.div<{ radius: number }>` - font-weight: bold; - font-size: calc(${({ radius }) => radius}px / 2.5); - line-height: calc(${({ radius }) => radius}px / 2.1); - display: flex; - align-items: center; - text-align: center; - letter-spacing: -0.4px; - color: ${({ theme }) => theme.iconTextColor}; -` - -const Logo = styled.div<{ radius: number; icon?: string }>` - width: calc(${({ radius }) => radius}px - 6px); - height: calc(${({ radius }) => radius}px - 6px); - display: flex; - 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}`}; - - &.offline { - &::after { - content: ''; - position: absolute; - right: -1px; - bottom: -2px; - width: 7px; - height: 7px; - border-radius: 50%; - background-color: ${({ theme }) => theme.secondary}; - border: 2px solid ${({ theme }) => theme.bodyBackgroundColor}; - } - } - - &.online { - &::after { - content: ''; - position: absolute; - right: -1px; - bottom: -2px; - width: 7px; - height: 7px; - border-radius: 50%; - background-color: ${({ theme }) => theme.greenColor}; - border: 2px solid ${({ theme }) => theme.bodyBackgroundColor}; - } - } - - &.empty { - background-color: ${({ theme }) => theme.bodyBackgroundColor}; - background-image: none; - } -` - -export const Wrapper = styled.div<{ radius: number; conicGradient: string }>` - width: ${({ radius }) => radius}px; - height: ${({ radius }) => radius}px; - display: flex; - align-items: center; - justify-content: center; - border-radius: 50%; - background: ${({ conicGradient }) => conicGradient}; -` diff --git a/packages/status-react/src/components/Messages/MessageQuote.tsx b/packages/status-react/src/components/Messages/MessageQuote.tsx deleted file mode 100644 index 54aee50e..00000000 --- a/packages/status-react/src/components/Messages/MessageQuote.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import React from 'react' - -import styled from 'styled-components' - -import { useMessengerContext } from '../../contexts/messengerProvider' -import { useScrollToMessage } from '../../contexts/scrollProvider' -import { ReplyOn, ReplyTo } from '../Chat-legacy/ChatInput' -import { QuoteSvg } from '../Icons/QuoteIcon' -import { UserIcon } from '../Icons/UserIcon' - -import type { ChatMessage } from '../../models/ChatMessage' - -function calcHeight(quote: ChatMessage) { - if (quote.image && quote.content) { - return 88 - } else if (quote.image && !quote.content) { - return 68 - } else { - return 25 - } -} - -type MessageQuoteProps = { - quote: ChatMessage | undefined -} - -export function MessageQuote({ quote }: MessageQuoteProps) { - const { contacts } = useMessengerContext() - const scroll = useScrollToMessage() - - if (quote && quote.sender) { - return ( - scroll(quote)}> - - - {' '} - {' '} - {contacts[quote.sender]?.customName ?? - contacts[quote.sender].trueName} - - {quote.content} - {quote.image && } - - ) - } - return null -} - -const QuoteWrapper = styled.div` - display: flex; - flex-direction: column; - padding-left: 48px; - position: relative; -` - -const QuoteSender = styled(ReplyTo)` - color: ${({ theme }) => theme.secondary}; -` - -const Quote = styled(ReplyOn)` - color: ${({ theme }) => theme.secondary}; -` - -const QuoteImage = styled.img` - width: 56px; - height: 56px; - border-radius: 4px; - margin-top: 4px; -` diff --git a/packages/status-react/src/components/Messages/MessageReactions.tsx b/packages/status-react/src/components/Messages/MessageReactions.tsx deleted file mode 100644 index 67c6bdb2..00000000 --- a/packages/status-react/src/components/Messages/MessageReactions.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import React, { useCallback } from 'react' - -import { Emoji } from 'emoji-mart' -import styled from 'styled-components' - -import { ReactionButton } from '../Reactions/ReactionButton' - -import type { BaseEmoji } from 'emoji-mart' - -interface MessageReactionsProps { - messageReactions: BaseEmoji[] - setMessageReactions: React.Dispatch> -} - -export function MessageReactions({ - messageReactions, - setMessageReactions, -}: MessageReactionsProps) { - const isMyReactionIncluded = (emoji: BaseEmoji) => - messageReactions.includes(emoji) // temporary function while message reactions are not added to waku - - const handleReaction = useCallback( - (emoji: BaseEmoji) => { - messageReactions.find(e => e === emoji) - ? setMessageReactions(prev => prev.filter(e => e != emoji)) - : setMessageReactions(prev => [...prev, emoji]) - }, - [messageReactions, setMessageReactions] - ) - - return ( - - {messageReactions.map(reaction => ( - handleReaction(reaction)} - > - -

1

-
- ))} - -
- ) -} - -export const Reactions = styled.div` - display: flex; - align-items: center; - margin-top: 6px; -` - -const EmojiReaction = styled.button` - display: flex; - align-items: center; - background: rgba(0, 0, 0, 0.05); - border-radius: 2px 10px 10px 10px; - color: ${({ theme }) => theme.primary}; - font-size: 12px; - line-height: 16px; - padding: 2px 8px 2px 2px; - margin-right: 4px; - - &:hover { - background: rgba(0, 0, 0, 0.1); - } - - & > p { - margin-left: 4px; - } - - & > span { - height: 16px; - } - - &.chosen { - background: ${({ theme }) => theme.blueBg}; - border: 1px solid ${({ theme }) => theme.tertiary}; - color: ${({ theme }) => theme.tertiary}; - } -` diff --git a/packages/status-react/src/components/Messages/MessagesList.tsx b/packages/status-react/src/components/Messages/MessagesList.tsx deleted file mode 100644 index ee83cd77..00000000 --- a/packages/status-react/src/components/Messages/MessagesList.tsx +++ /dev/null @@ -1,106 +0,0 @@ -import React, { useEffect, useMemo, useRef, useState } from 'react' - -import styled from 'styled-components' - -import { useMessengerContext } from '../../contexts/messengerProvider' -import { useModal } from '../../contexts/modalProvider' -import { useNarrow } from '../../contexts/narrowProvider' -import { useChatScrollHandle } from '../../hooks/useChatScrollHandle' -import { EmptyChannel } from '../Channels/EmptyChannel' -import { LoadingIcon } from '../Icons/LoadingIcon' -import { LinkModal, LinkModalName } from '../Modals/LinkModal' -import { PictureModal, PictureModalName } from '../Modals/PictureModal' -import { UiMessage } from './UiMessage' - -import type { Reply } from '../../hooks/useReply' -import type { ChannelData } from '../../models/ChannelData' - -interface MessagesListProps { - setReply: (val: Reply | undefined) => void - channel: ChannelData -} - -export function MessagesList({ setReply, channel }: MessagesListProps) { - const narrow = useNarrow() - const { messages, contacts } = useMessengerContext() - const ref = useRef(null) - const loadingMessages = useChatScrollHandle(messages, ref) - - const shownMessages = useMemo( - () => - messages.filter(message => !contacts?.[message.sender]?.blocked ?? true), - [contacts, messages] - ) - - const [image, setImage] = useState('') - const [link, setLink] = useState('') - - const { setModal: setPictureModal, isVisible: showPictureModal } = - useModal(PictureModalName) - const { setModal: setLinkModal, isVisible: showLinkModal } = - useModal(LinkModalName) - - useEffect( - () => (!image ? undefined : setPictureModal(true)), - [image, setPictureModal] - ) - useEffect( - () => (!link ? undefined : setLinkModal(true)), - [link, setLinkModal] - ) - - useEffect( - () => (!showPictureModal ? setImage('') : undefined), - [showPictureModal] - ) - useEffect(() => (!showLinkModal ? setLink('') : undefined), [showLinkModal]) - - return ( - - - - - {loadingMessages && ( - - - - )} - {shownMessages.map((message, idx) => ( - - ))} - - ) -} - -const LoadingWrapper = styled.div` - display: flex; - align-self: center; - align-items: center; - justify-content: center; - background: ${({ theme }) => theme.bodyBackgroundColor}; - position: relative; -` - -const MessagesWrapper = styled.div` - display: flex; - flex-direction: column; - height: calc(100% - 44px); - overflow: auto; - padding: 8px 0; - - &.wide { - margin-top: -24px; - } - - &::-webkit-scrollbar { - width: 0; - } -` diff --git a/packages/status-react/src/components/Messages/Styles.tsx b/packages/status-react/src/components/Messages/Styles.tsx deleted file mode 100644 index 9dd48384..00000000 --- a/packages/status-react/src/components/Messages/Styles.tsx +++ /dev/null @@ -1,156 +0,0 @@ -import styled from 'styled-components' - -import { textMediumStyles, textSmallStyles } from '../Text' - -export const MessageWrapper = styled.div` - width: 100%; - display: flex; - flex-direction: column; - padding: 8px 16px; - border-left: 2px solid ${({ theme }) => theme.bodyBackgroundColor}; - position: relative; - - &:hover { - background: ${({ theme }) => theme.inputColor}; - border-color: ${({ theme }) => theme.inputColor}; - } - - &:hover > div { - visibility: visible; - } - - &.mention { - background: ${({ theme }) => theme.mentionBg}; - border-color: ${({ theme }) => theme.mentionColor}; - } - - &.mention:hover { - background: ${({ theme }) => theme.mentionBgHover}; - border-color: ${({ theme }) => theme.mentionColor}; - } -` - -export const MessageOuterWrapper = styled.div` - width: 100%; - display: flex; - flex-direction: column; - position: relative; -` - -export const DateSeparator = styled.div` - width: 100%; - display: flex; - flex: 1; - height: 100%; - text-align: center; - justify-content: center; - align-items: center; - font-family: 'Inter'; - font-style: normal; - font-weight: 500; - color: #939ba1; - margin-top: 16px; - margin-bottom: 16px; - - ${textSmallStyles} -` - -export const ContentWrapper = styled.div` - display: flex; - flex-direction: column; - margin-left: 8px; -` - -export const MessageHeaderWrapper = styled.div` - display: flex; - align-items: center; -` - -export const UserNameWrapper = styled.div` - display: flex; - align-items: center; -` - -export const UserName = styled.p` - font-weight: 500; - color: ${({ theme }) => theme.tertiary}; - margin-right: 4px; - - ${textMediumStyles} -` - -export const UserNameBtn = styled.button` - padding: 0; - border: none; - outline: none; - position: relative; - color: ${({ theme }) => theme.tertiary}; - - &:hover { - text-decoration: underline; - } - - &:disabled { - cursor: default; - text-decoration: none; - } -` - -export const UserAddress = styled.p` - font-size: 10px; - line-height: 14px; - letter-spacing: 0.2px; - color: ${({ theme }) => theme.secondary}; - position: relative; - padding-right: 8px; - - &.chat:after { - content: ''; - position: absolute; - right: 0; - top: 50%; - transform: translateY(-50%); - width: 4px; - height: 4px; - border-radius: 50%; - background: ${({ theme }) => theme.secondary}; - } -` - -export const TimeWrapper = styled.div` - font-size: 10px; - line-height: 14px; - letter-spacing: 0.2px; - text-transform: uppercase; - color: ${({ theme }) => theme.secondary}; - margin-left: 4px; -` - -export const MessageText = styled.div` - overflow-wrap: anywhere; - width: 100%; - white-space: pre-wrap; - color: ${({ theme }) => theme.primary}; -` - -export const IconBtn = styled.button` - width: 40px; - height: 40px; - display: flex; - justify-content: center; - align-items: end; - flex-shrink: 0; - border: none; - border-radius: 50%; - background-color: #bcbdff; - background-size: contain; - background-position: center; - padding: 0; - outline: none; - position: relative; - cursor: pointer; - - &:disabled { - cursor: default; - } -` diff --git a/packages/status-react/src/components/Messages/UiMessage.tsx b/packages/status-react/src/components/Messages/UiMessage.tsx deleted file mode 100644 index 4895b38c..00000000 --- a/packages/status-react/src/components/Messages/UiMessage.tsx +++ /dev/null @@ -1,164 +0,0 @@ -import React, { useMemo, useRef, useState } from 'react' - -import styled from 'styled-components' - -import { useIdentity } from '../../contexts/identityProvider' -import { useMessengerContext } from '../../contexts/messengerProvider' -import { useClickOutside } from '../../hooks/useClickOutside' -import { equalDate } from '../../utils' -import { ChatMessageContent } from '../Chat-legacy/ChatMessageContent' -import { ContactMenu } from '../Form/ContactMenu' -import { MessageMenu } from '../Form/MessageMenu' -import { UntrustworthIcon } from '../Icons/UntrustworthIcon' -import { UserLogo } from '../Members/UserLogo' -import { Reactions } from '../Reactions/Reactions' -import { MessageQuote } from './MessageQuote' -import { MessageReactions } from './MessageReactions' -import { - ContentWrapper, - DateSeparator, - IconBtn, - MessageHeaderWrapper, - MessageOuterWrapper, - MessageText, - MessageWrapper, - TimeWrapper, - UserAddress, - UserName, - UserNameBtn, - UserNameWrapper, -} from './Styles' - -import type { Reply } from '../../hooks/useReply' -import type { ChatMessage } from '../../models/ChatMessage' -import type { BaseEmoji } from 'emoji-mart' - -type UiMessageProps = { - idx: number - message: ChatMessage - prevMessage: ChatMessage - setImage: (img: string) => void - setLink: (link: string) => void - setReply: (val: Reply | undefined) => void -} - -export function UiMessage({ - message, - idx, - prevMessage, - setImage, - setLink, - setReply, -}: UiMessageProps) { - const today = new Date() - const { contacts } = useMessengerContext() - const identity = useIdentity() - - const contact = useMemo( - () => contacts[message.sender], - [message.sender, contacts] - ) - const [showMenu, setShowMenu] = useState(false) - - const [mentioned, setMentioned] = useState(false) - const [messageReactions, setMessageReactions] = useState([]) - - const ref = useRef(null) - useClickOutside(ref, () => setShowMenu(false)) - - const messageRef = useRef(null) - - return ( - - {(idx === 0 || !equalDate(prevMessage.date, message.date)) && ( - - {equalDate(message.date, today) - ? 'Today' - : message.date.toLocaleDateString()} - - )} - - - - { - if (identity) setShowMenu(e => !e) - }} - disabled={!identity} - ref={ref} - > - {showMenu && ( - - )} - - - - - - { - if (identity) setShowMenu(e => !e) - }} - disabled={!identity} - > - - {' '} - {contact?.customName ?? contact.trueName} - - - - {message.sender.slice(0, 5)}...{message.sender.slice(-3)} - - {contact.isUntrustworthy && } - - {message.date.toLocaleString()} - - - - - {messageReactions.length > 0 && ( - - )} - - - - {identity && ( - - )} - - - ) -} - -const UserMessageWrapper = styled.div` - width: 100%; - display: flex; - position: relative; -` diff --git a/packages/status-react/src/components/NarrowMode/NarrowChannels.tsx b/packages/status-react/src/components/NarrowMode/NarrowChannels.tsx deleted file mode 100644 index a632aae3..00000000 --- a/packages/status-react/src/components/NarrowMode/NarrowChannels.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import React from 'react' - -import { Channels } from '../Channels/Channels' -import { ListWrapper, NarrowTopbar } from './NarrowTopbar' - -interface NarrowChannelsProps { - setShowChannels: (val: boolean) => void -} - -export function NarrowChannels({ setShowChannels }: NarrowChannelsProps) { - return ( - - setShowChannels(false)} /> - setShowChannels(false)} /> - - ) -} diff --git a/packages/status-react/src/components/NarrowMode/NarrowMembers.tsx b/packages/status-react/src/components/NarrowMode/NarrowMembers.tsx deleted file mode 100644 index c7ce3585..00000000 --- a/packages/status-react/src/components/NarrowMode/NarrowMembers.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import React, { useMemo } from 'react' - -import { useMessengerContext } from '../../contexts/messengerProvider' -import { MembersList } from '../Members/MembersList' -import { ListWrapper, NarrowTopbar } from './NarrowTopbar' - -interface NarrowMembersProps { - switchShowMembersList: () => void -} - -export function NarrowMembers({ switchShowMembersList }: NarrowMembersProps) { - const { activeChannel } = useMessengerContext() - const listName = useMemo( - () => - activeChannel && activeChannel?.type === 'group' - ? 'Group members' - : 'Community members', - [activeChannel] - ) - - return ( - - - - - ) -} diff --git a/packages/status-react/src/components/NarrowMode/NarrowTopbar.tsx b/packages/status-react/src/components/NarrowMode/NarrowTopbar.tsx deleted file mode 100644 index d2afd916..00000000 --- a/packages/status-react/src/components/NarrowMode/NarrowTopbar.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import React from 'react' - -import styled from 'styled-components' - -import { useMessengerContext } from '../../contexts/messengerProvider' -import { BackButton } from '../Buttons/BackButton' - -interface NarrowTopbarProps { - list: string - onBtnClick: () => void -} - -export function NarrowTopbar({ list, onBtnClick }: NarrowTopbarProps) { - const { communityData, activeChannel } = useMessengerContext() - return ( - - - - {list} - - {activeChannel?.type === 'group' - ? activeChannel.name - : communityData?.name} - - - - ) -} - -const TopbarWrapper = styled.div` - display: flex; - justify-content: center; - background-color: ${({ theme }) => theme.bodyBackgroundColor}; - margin-bottom: 16px; - position: relative; -` - -const HeadingWrapper = styled.div` - display: flex; - justify-content: center; - flex-direction: column; - text-align: center; -` - -const Heading = styled.p` - font-weight: 500; - color: ${({ theme }) => theme.primary}; -` - -const SubHeading = styled.p` - font-weight: 500; - color: ${({ theme }) => theme.secondary}; -` - -export const ListWrapper = styled.div` - padding: 16px; - background: ${({ theme }) => theme.bodyBackgroundColor}; - overflow: auto; - flex: 1; -` diff --git a/packages/status-react/src/components/Reactions/ReactionButton.tsx b/packages/status-react/src/components/Reactions/ReactionButton.tsx deleted file mode 100644 index 32f5853e..00000000 --- a/packages/status-react/src/components/Reactions/ReactionButton.tsx +++ /dev/null @@ -1,91 +0,0 @@ -import React, { useRef, useState } from 'react' - -import styled from 'styled-components' - -import { useClickOutside } from '../../hooks/useClickOutside' -import { Tooltip } from '../Form/Tooltip' -import { ReactionSvg } from '../Icons/ReactionIcon' -import { ReactionPicker } from './ReactionPicker' - -import type { BaseEmoji } from 'emoji-mart' - -interface ReactionButtonProps { - className?: string - messageReactions: BaseEmoji[] - setMessageReactions: React.Dispatch> -} - -export function ReactionButton({ - className, - messageReactions, - setMessageReactions, -}: ReactionButtonProps) { - const ref = useRef(null) - useClickOutside(ref, () => setShowReactions(false)) - - const [showReactions, setShowReactions] = useState(false) - - return ( - - {showReactions && ( - - )} - setShowReactions(!showReactions)} - className={className} - > - - {!className && !showReactions && } - - - ) -} - -const Wrapper = styled.div` - position: relative; -` - -export const ReactionBtn = styled.button` - width: 32px; - height: 32px; - display: flex; - justify-content: center; - align-items: center; - border-radius: 8px; - align-self: center; - position: relative; - - &:hover { - background: ${({ theme }) => theme.buttonBgHover}; - } - - &.red:hover { - background: ${({ theme }) => theme.buttonNoBgHover}; - } - - &:hover > svg { - fill: ${({ theme }) => theme.tertiary}; - } - - &.red:hover > svg { - fill: ${({ theme }) => theme.redColor}; - } - - &:hover > div { - visibility: visible; - } - - &.small { - width: 18px; - height: 18px; - padding: 0; - - &:hover { - background: inherit; - } - } -` diff --git a/packages/status-react/src/components/Reactions/ReactionPicker.tsx b/packages/status-react/src/components/Reactions/ReactionPicker.tsx deleted file mode 100644 index 481e978d..00000000 --- a/packages/status-react/src/components/Reactions/ReactionPicker.tsx +++ /dev/null @@ -1,111 +0,0 @@ -import React, { useCallback } from 'react' - -import { Emoji, getEmojiDataFromNative } from 'emoji-mart' -import data from 'emoji-mart/data/all.json' -import styled from 'styled-components' - -import type { BaseEmoji } from 'emoji-mart' - -const emojiHeart = getEmojiDataFromNative('❤️', 'twitter', data) -const emojiLike = getEmojiDataFromNative('👍', 'twitter', data) -const emojiDislike = getEmojiDataFromNative('👎', 'twitter', data) -const emojiLaughing = getEmojiDataFromNative('😆', 'twitter', data) -const emojiDisappointed = getEmojiDataFromNative('😥', 'twitter', data) -const emojiRage = getEmojiDataFromNative('😡', 'twitter', data) - -export const emojiArr = [ - emojiHeart, - emojiLike, - emojiDislike, - emojiLaughing, - emojiDisappointed, - emojiRage, -] - -interface ReactionPickerProps { - className?: string - messageReactions: BaseEmoji[] - setMessageReactions: React.Dispatch> -} - -export function ReactionPicker({ - className, - messageReactions, - setMessageReactions, -}: ReactionPickerProps) { - const handleReaction = useCallback( - (emoji: BaseEmoji) => { - messageReactions.find(e => e === emoji) - ? setMessageReactions(prev => prev.filter(e => e != emoji)) - : setMessageReactions(prev => [...prev, emoji]) - }, - [messageReactions, setMessageReactions] - ) - - return ( - - {emojiArr.map(emoji => ( - handleReaction(emoji)} - className={`${messageReactions.includes(emoji) && 'chosen'}`} - menuMode={className === 'menu'} - > - {' '} - - - ))} - - ) -} - -const Wrapper = styled.div` - width: 266px; - display: flex; - justify-content: space-between; - position: absolute; - right: -34px; - top: -60px; - background: ${({ theme }) => theme.bodyBackgroundColor}; - box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.08); - border-radius: 16px 16px 4px 16px; - padding: 8px; - - &.small { - right: unset; - left: -100px; - transform: none; - border-radius: 16px 16px 16px 4px; - } - - &.menu { - width: 100%; - position: static; - box-shadow: unset; - border: none; - padding: 0; - } -` - -export const EmojiBtn = styled.button<{ menuMode: boolean }>` - width: ${({ menuMode }) => (menuMode ? '24px' : '40px')}; - height: ${({ menuMode }) => (menuMode ? '24px' : '40px')}; - display: flex; - justify-content: center; - align-items: center; - border-radius: 8px; - - &:hover { - background: ${({ theme }) => theme.inputColor}; - } - - &.chosen { - background: ${({ theme }) => theme.reactionBg}; - border: 1px solid ${({ theme }) => theme.tertiary}; - } -` diff --git a/packages/status-react/src/components/Reactions/Reactions.tsx b/packages/status-react/src/components/Reactions/Reactions.tsx deleted file mode 100644 index b7cafb26..00000000 --- a/packages/status-react/src/components/Reactions/Reactions.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import React, { useMemo } from 'react' - -import styled from 'styled-components' - -import { useUserPublicKey } from '../../contexts/identityProvider' -import { useMessengerContext } from '../../contexts/messengerProvider' -import { Tooltip } from '../Form/Tooltip' -import { DeleteIcon } from '../Icons/DeleteIcon' -import { EditIcon } from '../Icons/EditIcon' -import { PinIcon } from '../Icons/PinIcon' -import { ReplySvg } from '../Icons/ReplyIcon' -import { ReactionBtn, ReactionButton } from './ReactionButton' - -import type { Reply } from '../../hooks/useReply' -import type { ChatMessage } from '../../models/ChatMessage' -import type { BaseEmoji } from 'emoji-mart' - -interface ReactionsProps { - message: ChatMessage - setReply: (val: Reply | undefined) => void - messageReactions: BaseEmoji[] - setMessageReactions: React.Dispatch> -} - -export function Reactions({ - message, - setReply, - messageReactions, - setMessageReactions, -}: ReactionsProps) { - const userPK = useUserPublicKey() - const { activeChannel } = useMessengerContext() - - const userMessage = useMemo( - () => !!userPK && message.sender === userPK, - [userPK, message] - ) - - return ( - - - - setReply({ - sender: message.sender, - content: message.content, - image: message.image, - id: message.id, - }) - } - > - - - - {userMessage && ( - - - - - )} - {activeChannel?.type !== 'channel' && ( - - - - - )} - {userMessage && ( - - - - - )} - - ) -} - -const Wrapper = styled.div` - display: flex; - position: absolute; - right: 20px; - top: -18px; - box-shadow: 0px 4px 12px rgba(0, 34, 51, 0.08); - border-radius: 8px; - background: ${({ theme }) => theme.bodyBackgroundColor}; - padding: 2px; - visibility: hidden; -` diff --git a/packages/status-react/src/components/SearchBlock.tsx b/packages/status-react/src/components/SearchBlock.tsx deleted file mode 100644 index b032c88e..00000000 --- a/packages/status-react/src/components/SearchBlock.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import React, { useMemo } from 'react' - -import styled from 'styled-components' - -import { useMessengerContext } from '../contexts/messengerProvider' -import { ContactsList } from './Chat-legacy/ChatCreation' -import { Member } from './Members/Member' - -interface SearchBlockProps { - query: string - discludeList: string[] - onClick: (member: string) => void - onBotttom?: boolean -} - -export const SearchBlock = ({ - query, - discludeList, - onClick, - onBotttom, -}: SearchBlockProps) => { - const { contacts } = useMessengerContext() - - const searchList = useMemo(() => { - return Object.values(contacts) - .filter( - member => - member.id.includes(query) || - member?.customName?.includes(query) || - member.trueName.includes(query) - ) - .filter(member => !discludeList.includes(member.id)) - }, [query, discludeList, contacts]) - - if (searchList.length === 0 || !query) { - return null - } - return ( - - - {searchList.map(member => ( - - onClick(member.id)} /> - - ))} - - - ) -} - -const SearchContacts = styled.div` - display: flex; - flex-direction: column; - width: 360px; - padding: 8px; - background-color: ${({ theme }) => theme.bodyBackgroundColor}; - box-shadow: 0px 2px 4px rgba(0, 34, 51, 0.16), - 0px 4px 12px rgba(0, 34, 51, 0.08); - border-radius: 8px; - position: absolute; - left: 0; - max-height: 200px; - overflow: auto; -` - -const SearchContact = styled.div` - width: 340px; - display: flex; - align-items: center; - padding: 12px 12px 0 16px; - border-radius: 8px; - - &:hover { - background: ${({ theme }) => theme.inputColor}; - } -` diff --git a/packages/status-react/src/components/Skeleton/CommunitySkeleton.tsx b/packages/status-react/src/components/Skeleton/CommunitySkeleton.tsx deleted file mode 100644 index f561681d..00000000 --- a/packages/status-react/src/components/Skeleton/CommunitySkeleton.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import React from 'react' - -import styled from 'styled-components' - -import { Column } from '../CommunityIdentity' -import { Skeleton } from './Skeleton' - -export const CommunitySkeleton = () => { - return ( - - - - - - - - ) -} - -const LogoSkeleton = styled(Skeleton)` - border-radius: 50%; - margin-right: 8px; - flex-shrink: 0; -` - -const Loading = styled.div` - display: flex; - padding: 0 0 0 10px; -` diff --git a/packages/status-react/src/components/Skeleton/Loading.tsx b/packages/status-react/src/components/Skeleton/Loading.tsx deleted file mode 100644 index 90d541f6..00000000 --- a/packages/status-react/src/components/Skeleton/Loading.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import React from 'react' - -import styled from 'styled-components' - -import { LoadingIcon } from '../Icons/LoadingIcon' -import { textSmallStyles } from '../Text' - -export const Loading = () => { - return ( - - - Loading messages... - - ) -} - -const LoadingBlock = styled.div` - display: flex; - align-items: center; - position: absolute; - left: 50%; - bottom: -35px; - transform: translateX(-50%); - padding: 4px 5px 4px 7px; - background: ${({ theme }) => theme.bodyBackgroundColor}; - color: ${({ theme }) => theme.primary}; - box-shadow: ${({ theme }) => theme.shadow}; - border-radius: 8px; - z-index: 2; -` - -const LoadingText = styled.p` - color: ${({ theme }) => theme.primary}; - margin-left: 8px; - font-weight: 500; - - ${textSmallStyles} -` diff --git a/packages/status-react/src/components/Skeleton/LoadingSkeleton.tsx b/packages/status-react/src/components/Skeleton/LoadingSkeleton.tsx deleted file mode 100644 index 263121f3..00000000 --- a/packages/status-react/src/components/Skeleton/LoadingSkeleton.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import React from 'react' - -import styled from 'styled-components' - -import { MessageSkeleton } from './MessageSkeleton' -import { Skeleton } from './Skeleton' - -export const LoadingSkeleton = () => { - return ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ) -} - -const Loading = styled.div` - display: flex; - flex-direction: column; - justify-content: flex-end; - height: calc(100% - 44px); - padding: 8px 16px 0; - overflow: auto; -` diff --git a/packages/status-react/src/components/Skeleton/MessageSkeleton.tsx b/packages/status-react/src/components/Skeleton/MessageSkeleton.tsx deleted file mode 100644 index 04405393..00000000 --- a/packages/status-react/src/components/Skeleton/MessageSkeleton.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import React from 'react' - -import styled from 'styled-components' - -import { Skeleton } from './Skeleton' - -import type { ReactNode } from 'react' - -interface MessageSkeletonProps { - children: ReactNode -} - -export const MessageSkeleton = ({ children }: MessageSkeletonProps) => { - return ( - - - - - - - - {children} - - - ) -} - -const MessageWrapper = styled.div` - display: flex; - padding: 8px 0; - margin-bottom: 8px; -` - -const ContentWrapper = styled.div` - display: flex; - flex-direction: column; - flex: 1; -` - -const MessageHeaderWrapper = styled.div` - display: flex; - align-items: center; -` - -const MessageBodyWrapper = styled.div` - display: flex; - flex-direction: column; -` - -const AvatarSkeleton = styled(Skeleton)` - border-radius: 50%; - margin-right: 8px; -` - -const UserNameSkeleton = styled(Skeleton)` - margin-right: 4px; -` - -const TimeSkeleton = styled(Skeleton)` - margin-right: 4px; -` diff --git a/packages/status-react/src/components/Skeleton/Skeleton.tsx b/packages/status-react/src/components/Skeleton/Skeleton.tsx deleted file mode 100644 index dd6cd0f2..00000000 --- a/packages/status-react/src/components/Skeleton/Skeleton.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import styled, { keyframes } from 'styled-components' - -interface SkeletonProps { - width?: string - height?: string - borderRadius?: string -} - -const waveKeyframe = keyframes` - 0% { - transform: translateX(-100%); - } - 50% { - transform: translateX(100%); - } - 100% { - transform: translateX(100%); - } -` - -export const Skeleton = styled.div` - position: relative; - display: inline-block; - width: ${({ width }) => width || '100%'}; - height: ${({ height }) => height || '22px'}; - background: ${({ theme }) => theme.skeletonDark}; - border-radius: ${({ borderRadius }) => borderRadius || '8px'}; - margin-bottom: 5px; - overflow: hidden; - - &::after { - animation: ${waveKeyframe} 1.6s linear 0.5s infinite; - background: linear-gradient( - 90deg, - ${({ theme }) => theme.skeletonLight} 0%, - ${({ theme }) => theme.skeletonDark} 100% - ); - content: ''; - position: absolute; - transform: translateX(-100%); - bottom: 0; - left: 0; - right: 0; - top: 0; - } -` diff --git a/packages/status-react/src/components/Text.tsx b/packages/status-react/src/components/Text.tsx deleted file mode 100644 index 04567cf6..00000000 --- a/packages/status-react/src/components/Text.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import { css } from 'styled-components' - -export const textSmallStyles = css` - font-size: 13px; - line-height: 18px; -` - -export const textMediumStyles = css` - font-size: 15px; - line-height: 22px; -` diff --git a/packages/status-react/src/components/ToastMessages/ToastMessage.tsx b/packages/status-react/src/components/ToastMessages/ToastMessage.tsx deleted file mode 100644 index ef9f863a..00000000 --- a/packages/status-react/src/components/ToastMessages/ToastMessage.tsx +++ /dev/null @@ -1,130 +0,0 @@ -import React from 'react' - -import styled, { keyframes } from 'styled-components' - -import { useToasts } from '../../contexts/toastProvider' -import { Column } from '../CommunityIdentity' -import { CheckIcon } from '../Icons/CheckIcon' -import { CommunityIcon } from '../Icons/CommunityIcon' -import { CrossIcon } from '../Icons/CrossIcon' -import { ProfileIcon } from '../Icons/ProfileIcon' -import { textSmallStyles } from '../Text' - -import type { Toast } from '../../models/Toast' - -export function AnimationToastMessage() { - return keyframes` - 0% { - opacity: 0; - transform: translateY(-100%); } - 100% { - opacity: 1; - transform: translateY(0); } -` -} - -type ToastMessageProps = { - toast: Toast -} - -export function ToastMessage({ toast }: ToastMessageProps) { - const { setToasts } = useToasts() - - const closeToast = () => { - setToasts(prev => prev.filter(e => e != toast)) - } - - return ( - - - {toast.type === 'confirmation' && ( - - - - )} - {toast.type === 'incoming' && ( - - - - )} - {(toast.type === 'approvement' || toast.type === 'rejection') && ( - - - - )} - - {toast.text} - {toast.request && {toast.request}} - - - - - - - ) -} - -const ToastWrapper = styled.div` - width: 343px; - display: flex; - justify-content: space-between; - align-items: center; - padding: 8px 12px; - margin-top: 8px; - background: ${({ theme }) => theme.bodyBackgroundColor}; - box-shadow: ${({ theme }) => theme.shadow}; - border-radius: 8px; - animation: ${AnimationToastMessage} 2s ease; -` - -const ToastBlock = styled.div` - display: flex; - align-items: center; - color: ${({ theme }) => theme.primary}; -` - -const ToastText = styled.p` - font-weight: 500; - ${textSmallStyles}; -` - -const ToastRequest = styled(ToastText)` - width: 243px; - color: ${({ theme }) => theme.secondary}; - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; -` - -const IconWrapper = styled.div` - width: 32px; - height: 32px; - display: flex; - justify-content: center; - align-items: center; - border-radius: 50%; - margin-right: 12px; - - &.green { - background: ${({ theme }) => theme.greenBg}; - } - - &.blue { - background: ${({ theme }) => theme.blueBg}; - } - - &.red { - background: ${({ theme }) => theme.buttonNoBg}; - } -` - -const CloseButton = styled.button` - width: 32px; - height: 32px; -` diff --git a/packages/status-react/src/components/ToastMessages/ToastMessageList.tsx b/packages/status-react/src/components/ToastMessages/ToastMessageList.tsx deleted file mode 100644 index a31c3ea6..00000000 --- a/packages/status-react/src/components/ToastMessages/ToastMessageList.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import React from 'react' - -import styled from 'styled-components' - -import { useToasts } from '../../contexts/toastProvider' -import { ToastMessage } from './ToastMessage' - -export function ToastMessageList() { - const { toasts } = useToasts() - - return ( - - {toasts.map(toast => ( - - ))} - - ) -} - -const ToastsWrapper = styled.div` - position: absolute; - bottom: 56px; - right: 16px; - width: 343px; - display: flex; - flex-direction: column-reverse; - align-items: center; - z-index: 999; -` diff --git a/packages/status-react/src/components/UserCreation/UserCreation.tsx b/packages/status-react/src/components/UserCreation/UserCreation.tsx deleted file mode 100644 index 9942ed36..00000000 --- a/packages/status-react/src/components/UserCreation/UserCreation.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import React from 'react' - -import styled from 'styled-components' - -import { useNarrow } from '../../contexts/narrowProvider' -import { ColorChatIcon } from '../Icons/ColorChatIcon' -import { UserCreationButtons } from './UserCreationButtons' - -interface UserCreationProps { - permission: boolean -} - -export function UserCreation({ permission }: UserCreationProps) { - const narrow = useNarrow() - - if (!narrow) { - return ( - - - Want to jump into the discussion? - - - ) - } else { - return null - } -} - -const Wrapper = styled.div` - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - flex: 1; - background-color: ${({ theme }) => theme.sectionBackgroundColor}; -` - -const TitleWrapper = styled.div` - font-weight: bold; - font-size: 17px; - line-height: 24px; - text-align: center; - margin: 24px 0; - color: ${({ theme }) => theme.primary}; -` diff --git a/packages/status-react/src/components/UserCreation/UserCreationButtons.tsx b/packages/status-react/src/components/UserCreation/UserCreationButtons.tsx deleted file mode 100644 index 557112ee..00000000 --- a/packages/status-react/src/components/UserCreation/UserCreationButtons.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import React, { useMemo } from 'react' - -import styled from 'styled-components' - -import { useModal } from '../../contexts/modalProvider' -import { useNarrow } from '../../contexts/narrowProvider' -import { loadEncryptedIdentity } from '../../utils' -import { buttonStyles, buttonTransparentStyles } from '../Buttons/buttonStyle' -import { ProfileFoundModalName } from '../Modals/ProfileFoundModal' -import { StatusModalName } from '../Modals/StatusModal' -import { UserCreationModalName } from '../Modals/UserCreationModal' -import { UserCreationStartModalName } from '../Modals/UserCreationStartModal' -import { WalletModalName } from '../Modals/WalletModal' -import { textSmallStyles } from '../Text' - -interface UserCreationProps { - permission: boolean -} - -export function UserCreationButtons({ permission }: UserCreationProps) { - const narrow = useNarrow() - const { setModal } = useModal(UserCreationModalName) - const { setModal: setStatusModal } = useModal(StatusModalName) - const { setModal: setWalletModal } = useModal(WalletModalName) - const { setModal: setProfileFoundModal } = useModal(ProfileFoundModalName) - const { setModal: setCreationStartModal } = useModal( - UserCreationStartModalName - ) - - const encryptedIdentity = useMemo(() => loadEncryptedIdentity(), []) - - return ( - - { - setStatusModal(true) - setCreationStartModal(false) - }} - className={`${narrow && 'narrow'}`} - > - Sync with Status profile - - { - setWalletModal(true) - setCreationStartModal(false) - }} - className={`${narrow && 'narrow'}`} - > - Connect Ethereum Wallet - - {permission && ( - { - encryptedIdentity ? setProfileFoundModal(true) : setModal(true) - setCreationStartModal(false) - }} - > - Use a throwaway profile - - )} - - ) -} - -const Wrapper = styled.div` - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; -` - -const LoginBtn = styled.button` - ${buttonStyles} - ${textSmallStyles} - padding: 10px 12px; - margin-bottom: 16px; - - &.narrow { - margin-bottom: 32px; - } -` - -const ThrowAwayButton = styled.button` - ${buttonTransparentStyles} -` diff --git a/packages/status-react/src/components/main-sidebar/index.tsx b/packages/status-react/src/components/main-sidebar/index.tsx index 13619c49..e12df65d 100644 --- a/packages/status-react/src/components/main-sidebar/index.tsx +++ b/packages/status-react/src/components/main-sidebar/index.tsx @@ -1,5 +1,6 @@ import React from 'react' +import { useAppState } from '~/src/contexts/app-context' import { styled } from '~/src/styles/config' import { Separator } from '~/src/system' @@ -9,6 +10,12 @@ import { GetStarted } from './components/get-started' import { Messages } from './components/messages' export const MainSidebar = () => { + const { options } = useAppState() + + if (options.enableSidebar === false) { + return null + } + return ( diff --git a/packages/status-react/src/contexts/chatStateProvider.tsx b/packages/status-react/src/contexts/chatStateProvider.tsx deleted file mode 100644 index 3014c2c0..00000000 --- a/packages/status-react/src/contexts/chatStateProvider.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import React, { createContext, useContext, useState } from 'react' - -export enum ChatState { - ChatCreation, - ChatBody, -} - -type ChatStateContextType = [ - ChatState, - React.Dispatch> -] - -const ChatStateContext = createContext([ - ChatState.ChatBody, - () => undefined, -]) - -export function useChatState() { - return useContext(ChatStateContext) -} - -export function ChatStateProvider({ children }: { children: React.ReactNode }) { - const state = useState(ChatState.ChatBody) - return ( - - {children} - - ) -} diff --git a/packages/status-react/src/contexts/identityProvider.tsx b/packages/status-react/src/contexts/identityProvider.tsx deleted file mode 100644 index 5ac44329..00000000 --- a/packages/status-react/src/contexts/identityProvider.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import React, { createContext, useContext, useMemo, useState } from 'react' - -import { bufToHex } from '@status-im/core' - -import type { Identity } from '@status-im/core' - -const IdentityContext = createContext<{ - identity: Identity | undefined - setIdentity: React.Dispatch> - publicKey: string | undefined - walletIdentity: Identity | undefined - setWalletIdentity: React.Dispatch> - nickname: string | undefined - setNickname: React.Dispatch> -}>({ - identity: undefined, - setIdentity: () => undefined, - publicKey: undefined, - walletIdentity: undefined, - setWalletIdentity: () => undefined, - nickname: undefined, - setNickname: () => undefined, -}) - -export function useIdentity() { - return useContext(IdentityContext).identity -} - -export function useUserPublicKey() { - return useContext(IdentityContext).publicKey -} - -export function useSetIdentity() { - return useContext(IdentityContext).setIdentity -} - -export function useWalletIdentity() { - return useContext(IdentityContext).walletIdentity -} - -export function useSetWalletIdentity() { - return useContext(IdentityContext).setWalletIdentity -} - -export function useNickname() { - return useContext(IdentityContext).nickname -} - -export function useSetNikcname() { - return useContext(IdentityContext).setNickname -} - -interface IdentityProviderProps { - children: React.ReactNode -} - -export function IdentityProvider({ children }: IdentityProviderProps) { - const [identity, setIdentity] = useState(undefined) - const publicKey = useMemo( - () => (identity ? bufToHex(identity.publicKey) : undefined), - [identity] - ) - const [walletIdentity, setWalletIdentity] = useState( - undefined - ) - const [nickname, setNickname] = useState(undefined) - - return ( - - {children} - - ) -} diff --git a/packages/status-react/src/contexts/messengerProvider.tsx b/packages/status-react/src/contexts/messengerProvider.tsx deleted file mode 100644 index 3fc21c17..00000000 --- a/packages/status-react/src/contexts/messengerProvider.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import React, { createContext, useContext } from 'react' - -import { useMessenger } from '../hooks/messenger/useMessenger' -import { useIdentity, useNickname } from './identityProvider' - -import type { MessengerType } from '../hooks/messenger/useMessenger' -import type { Environment } from '~/src/types/config' - -const MessengerContext = createContext({ - messenger: undefined, - messages: [], - sendMessage: async () => undefined, - notifications: {}, - clearNotifications: () => undefined, - mentions: {}, - clearMentions: () => undefined, - loadPrevDay: async () => undefined, - loadingMessages: false, - loadingMessenger: true, - communityData: undefined, - contacts: {}, - contactsDispatch: () => undefined, - addContact: () => undefined, - activeChannel: undefined, - channels: {}, - channelsDispatch: () => undefined, - removeChannel: () => undefined, - createGroupChat: () => undefined, - changeGroupChatName: () => undefined, - addMembers: () => undefined, - nickname: undefined, - subscriptionsDispatch: () => undefined, -}) - -export function useMessengerContext() { - return useContext(MessengerContext) -} - -interface Props { - publicKey: string - environment?: Environment - children: React.ReactNode -} - -export function MessengerProvider(props: Props) { - const { publicKey, environment, children } = props - - const identity = useIdentity() - const nickname = useNickname() - const messenger = useMessenger(publicKey, environment, identity, nickname) - - return ( - - {children} - - ) -} diff --git a/packages/status-react/src/contexts/modalProvider.tsx b/packages/status-react/src/contexts/modalProvider.tsx deleted file mode 100644 index 8feffcaf..00000000 --- a/packages/status-react/src/contexts/modalProvider.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import React, { - createContext, - useCallback, - useContext, - useMemo, - useState, -} from 'react' - -import type { - ProfileModalName, - ProfileModalProps, -} from '../components/Modals/ProfileModal' - -type TypeMap = { - [ProfileModalName]?: ProfileModalProps -} - -type ModalsState = TypeMap & { - [name: string]: boolean | undefined -} - -type ModalContextType = [ - state: ModalsState, - setState: React.Dispatch> -] - -const ModalContext = createContext([{}, () => undefined]) - -export function useModal(name: T) { - const [modals, setModals] = useContext(ModalContext) - - const setModal = useCallback( - (state: T extends keyof TypeMap ? TypeMap[T] | false : boolean) => { - setModals(prev => { - if (!state) { - return { - ...prev, - [name]: undefined, - } - } - return { - ...prev, - [name]: state, - } - }) - }, - [name, setModals] - ) - const isVisible = useMemo(() => !!modals?.[name], [modals, name]) - - const props = useMemo(() => modals?.[name], [modals, name]) - - return { isVisible, setModal, props } -} - -interface IdentityProviderProps { - children: React.ReactNode -} - -export function ModalProvider({ children }: IdentityProviderProps) { - const modalState = useState({}) - return ( - {children} - ) -} diff --git a/packages/status-react/src/contexts/narrowProvider.tsx b/packages/status-react/src/contexts/narrowProvider.tsx deleted file mode 100644 index 20be3a29..00000000 --- a/packages/status-react/src/contexts/narrowProvider.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import React, { createContext, useContext } from 'react' - -import { useRefBreak } from '../hooks/useRefBreak' - -const NarrowContext = createContext<{ narrow: boolean; low: boolean }>({ - narrow: false, - low: false, -}) - -export function useNarrow() { - const { narrow } = useContext(NarrowContext) - return narrow -} - -export function useLow() { - const { low } = useContext(NarrowContext) - return low -} - -interface NarrowProviderProps { - children: React.ReactNode - myRef: React.RefObject -} - -export function NarrowProvider({ children, myRef }: NarrowProviderProps) { - const narrow = useRefBreak(myRef?.current?.offsetWidth ?? 0, 736) - const low = useRefBreak(myRef?.current?.offsetHeight ?? 0, 465) - return ( - - {children} - - ) -} diff --git a/packages/status-react/src/contexts/scrollProvider.tsx b/packages/status-react/src/contexts/scrollProvider.tsx deleted file mode 100644 index 0b23d057..00000000 --- a/packages/status-react/src/contexts/scrollProvider.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import React, { - createContext, - useCallback, - useContext, - useEffect, - useState, -} from 'react' - -import { useMessengerContext } from '../contexts/messengerProvider' - -import type { ChatMessage } from '../models/ChatMessage' - -const ScrollContext = createContext< - (msg: ChatMessage, channelId?: string) => void ->(() => undefined) - -export function useScrollToMessage() { - return useContext(ScrollContext) -} - -interface ScrollProviderProps { - children: React.ReactNode -} - -export function ScrollProvider({ children }: ScrollProviderProps) { - const scrollToDivId = useCallback((id: string) => { - const quoteDiv = document.getElementById(id) - if (quoteDiv) { - quoteDiv.scrollIntoView({ - behavior: 'smooth', - block: 'center', - inline: 'center', - }) - quoteDiv.style.background = 'lightblue' - quoteDiv.style.transition = 'background-color 1000ms linear' - window.setTimeout(() => { - quoteDiv.style.background = '' - window.setTimeout(() => { - quoteDiv.style.transition = '' - }, 1000) - }, 1000) - } - }, []) - - const { activeChannel, channelsDispatch } = useMessengerContext() - const [scrollToMessage, setScrollToMessage] = useState('') - const [messageChannel, setMessageChannel] = useState('') - - useEffect(() => { - if (scrollToMessage && messageChannel) { - if (activeChannel?.id === messageChannel) { - scrollToDivId(scrollToMessage) - setScrollToMessage('') - setMessageChannel('') - } - } - }, [activeChannel, scrollToMessage, messageChannel, scrollToDivId]) - const scroll = useCallback( - (msg: ChatMessage, channelId?: string) => { - if (!channelId || activeChannel?.id === channelId) { - scrollToDivId(msg.id) - } else { - setMessageChannel(channelId) - setScrollToMessage(msg.id) - channelsDispatch({ type: 'ChangeActive', payload: channelId }) - } - }, - [scrollToDivId, channelsDispatch, activeChannel] - ) - return ( - {children} - ) -} diff --git a/packages/status-react/src/contexts/toastProvider.tsx b/packages/status-react/src/contexts/toastProvider.tsx deleted file mode 100644 index fa0f776d..00000000 --- a/packages/status-react/src/contexts/toastProvider.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import React, { createContext, useContext, useState } from 'react' - -import type { Toast } from '../models/Toast' - -const ToastContext = createContext<{ - toasts: Toast[] - setToasts: React.Dispatch> -}>({ - toasts: [], - setToasts: () => undefined, -}) - -export function useToasts() { - return useContext(ToastContext) -} - -interface ToastProviderProps { - children: React.ReactNode -} - -export function ToastProvider({ children }: ToastProviderProps) { - const [toasts, setToasts] = useState([]) - return ( - - {children} - - ) -} diff --git a/packages/status-react/src/hooks/messenger/useChannelsReducer.ts b/packages/status-react/src/hooks/messenger/useChannelsReducer.ts deleted file mode 100644 index 73f1c279..00000000 --- a/packages/status-react/src/hooks/messenger/useChannelsReducer.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { useReducer } from 'react' - -import type { ChannelData, ChannelsData } from '../../models/ChannelData' - -export type ChannelsState = { - channels: ChannelsData - activeChannel: ChannelData -} - -export type ChannelAction = - | { type: 'AddChannel'; payload: ChannelData } - | { type: 'UpdateActive'; payload: ChannelData } - | { type: 'ChangeActive'; payload: string } - | { type: 'ToggleMuted'; payload: string } - | { type: 'RemoveChannel'; payload: string } - -function channelReducer( - state: ChannelsState, - action: ChannelAction -): ChannelsState { - switch (action.type) { - case 'AddChannel': { - const channels = { - ...state.channels, - [action.payload.id]: action.payload, - } - return { channels, activeChannel: action.payload } - } - case 'UpdateActive': { - const activeChannel = state.activeChannel - if (activeChannel) { - return { - channels: { ...state.channels, [activeChannel.id]: action.payload }, - activeChannel: action.payload, - } - } - return state - } - case 'ChangeActive': { - const newActive = state.channels[action.payload] - if (newActive) { - return { ...state, activeChannel: newActive } - } - return state - } - case 'ToggleMuted': { - const channel = state.channels[action.payload] - if (channel) { - const updatedChannel: ChannelData = { - ...channel, - isMuted: !channel.isMuted, - } - return { - channels: { ...state.channels, [channel.id]: updatedChannel }, - activeChannel: updatedChannel, - } - } - return state - } - case 'RemoveChannel': { - const channelsCopy = { ...state.channels } - delete channelsCopy[action.payload] - let newActive = { id: '', name: '', type: 'channel' } as ChannelData - if (Object.values(channelsCopy).length > 0) { - newActive = Object.values(channelsCopy)[0] - } - return { channels: channelsCopy, activeChannel: newActive } - } - default: - throw new Error() - } -} - -export function useChannelsReducer() { - return useReducer(channelReducer, { - channels: {}, - activeChannel: { id: '', name: '', type: 'channel' }, - } as ChannelsState) -} diff --git a/packages/status-react/src/hooks/messenger/useContacts.ts b/packages/status-react/src/hooks/messenger/useContacts.ts deleted file mode 100644 index 3efcd63c..00000000 --- a/packages/status-react/src/hooks/messenger/useContacts.ts +++ /dev/null @@ -1,123 +0,0 @@ -import { useMemo, useReducer, useState } from 'react' - -import { bufToHex, Contacts as ContactsClass } from '@status-im/core' - -import type { Contacts } from '../../models/Contact' -import type { Identity, Messenger } from '@status-im/core' - -export type ContactsAction = - | { type: 'updateOnline'; payload: { id: string; clock: number } } - | { type: 'setTrueName'; payload: { id: string; trueName: string } } - | { - type: 'setCustomName' - payload: { id: string; customName: string | undefined } - } - | { - type: 'setIsUntrustworthy' - payload: { id: string; isUntrustworthy: boolean } - } - | { type: 'setIsFriend'; payload: { id: string; isFriend: boolean } } - | { type: 'setBlocked'; payload: { id: string; blocked: boolean } } - | { type: 'toggleBlocked'; payload: { id: string } } - | { type: 'toggleTrustworthy'; payload: { id: string } } - -function contactsReducer(state: Contacts, action: ContactsAction): Contacts { - const id = action.payload.id - const prev = state[id] - - switch (action.type) { - case 'updateOnline': { - const now = Date.now() - const clock = action.payload.clock - if (prev) { - return { ...state, [id]: { ...prev, online: clock > now - 301000 } } - } - return { ...state, [id]: { id, trueName: id.slice(0, 10) } } - } - case 'setTrueName': { - const trueName = action.payload.trueName - if (prev) { - return { ...state, [id]: { ...prev, trueName } } - } - return { ...state, [id]: { id, trueName } } - } - case 'setCustomName': { - const customName = action.payload.customName - if (prev) { - return { ...state, [id]: { ...prev, customName } } - } - return state - } - case 'setIsUntrustworthy': { - const isUntrustworthy = action.payload.isUntrustworthy - if (prev) { - return { ...state, [id]: { ...prev, isUntrustworthy } } - } - return state - } - case 'setIsFriend': { - const isFriend = action.payload.isFriend - if (prev) { - return { ...state, [id]: { ...prev, isFriend } } - } - return state - } - case 'setBlocked': { - const blocked = action.payload.blocked - if (prev) { - return { ...state, [id]: { ...prev, blocked } } - } - return state - } - case 'toggleBlocked': { - if (prev) { - return { ...state, [id]: { ...prev, blocked: !prev.blocked } } - } - return state - } - case 'toggleTrustworthy': { - if (prev) { - return { - ...state, - [id]: { ...prev, isUntrustworthy: !prev.isUntrustworthy }, - } - } - return state - } - default: - throw new Error() - } -} - -export function useContacts( - messenger: Messenger | undefined, - identity: Identity | undefined, - newNickname: string | undefined -) { - const [nickname, setNickname] = useState(undefined) - const [contacts, contactsDispatch] = useReducer(contactsReducer, {}) - - const contactsClass = useMemo(() => { - if (messenger && messenger.identity === identity) { - const newContacts = new ContactsClass( - identity, - messenger.waku, - (id, clock) => - contactsDispatch({ type: 'updateOnline', payload: { id, clock } }), - (id, nickname) => { - if (identity?.publicKey && id === bufToHex(identity.publicKey)) { - setNickname(nickname) - } - contactsDispatch({ - type: 'setTrueName', - payload: { id, trueName: nickname }, - }) - }, - newNickname - ) - return newContacts - } - }, [messenger, identity, newNickname]) - - return { contacts, contactsDispatch, contactsClass, nickname } -} diff --git a/packages/status-react/src/hooks/messenger/useGroupChats.ts b/packages/status-react/src/hooks/messenger/useGroupChats.ts deleted file mode 100644 index e3b473cd..00000000 --- a/packages/status-react/src/hooks/messenger/useGroupChats.ts +++ /dev/null @@ -1,131 +0,0 @@ -import { useCallback, useMemo } from 'react' - -import { GroupChats } from '@status-im/core' - -import { ChatMessage } from '../../models/ChatMessage' -import { uintToImgUrl } from '../../utils' - -import type { ChannelData } from '../../models/ChannelData' -import type { Contact } from '../../models/Contact' -import type { ChannelAction } from './useChannelsReducer' -import type { - ChatMessage as StatusChatMessage, - Contacts as ContactsClass, - GroupChat, - Identity, - Messenger, -} from '@status-im/core' - -const contactFromId = (member: string): Contact => { - return { - blocked: false, - id: member, - isUntrustworthy: false, - online: false, - trueName: member, - } -} - -export function useGroupChats( - messenger: Messenger | undefined, - identity: Identity | undefined, - dispatch: (action: ChannelAction) => void, - addChatMessage: (newMessage: ChatMessage | undefined, id: string) => void, - contactsClass: ContactsClass | undefined -) { - const groupChat = useMemo(() => { - if (messenger && identity && contactsClass) { - const addChat = (chat: GroupChat) => { - const members = chat.members.map(member => member.id).map(contactFromId) - const channel: ChannelData = - chat.members.length > 2 - ? { - id: chat.chatId, - name: chat.name ?? chat.chatId.slice(0, 10), - type: 'group', - description: `${chat.members.length} members`, - members, - } - : { - id: chat.chatId, - name: chat.members[0].id, - type: 'dm', - description: `Chatkey: ${chat.members[0].id}`, - members, - } - chat.members.forEach(member => contactsClass.addContact(member.id)) - dispatch({ type: 'AddChannel', payload: channel }) - } - const removeChat = (chat: GroupChat) => { - dispatch({ type: 'RemoveChannel', payload: chat.chatId }) - } - const handleMessage = (msg: StatusChatMessage, sender: string) => { - let image: string | undefined = undefined - if (msg.image) { - image = uintToImgUrl(msg.image.payload) - } - addChatMessage( - new ChatMessage( - msg.text ?? '', - new Date(msg.clock ?? 0), - sender, - image, - msg.responseTo - ), - msg.chatId - ) - } - return new GroupChats( - identity, - messenger.waku, - addChat, - removeChat, - handleMessage - ) - } - }, [messenger, identity, contactsClass, addChatMessage, dispatch]) - - const createGroupChat = useCallback( - (members: string[]) => { - if (groupChat) { - groupChat.createGroupChat(members) - } - }, - [groupChat] - ) - - const changeGroupChatName = useCallback( - (name: string, chatId: string) => { - if (groupChat) { - groupChat.changeChatName(chatId, name) - } - }, - [groupChat] - ) - - const removeChannel = useCallback( - (channelId: string) => { - if (groupChat) { - groupChat.quitChat(channelId) - } - }, - [groupChat] - ) - - const addMembers = useCallback( - (members: string[], chatId: string) => { - if (groupChat) { - groupChat.addMembers(chatId, members) - } - }, - [groupChat] - ) - - return { - createGroupChat, - removeChannel, - groupChat, - changeGroupChatName, - addMembers, - } -} diff --git a/packages/status-react/src/hooks/messenger/useLoadPrevDay.ts b/packages/status-react/src/hooks/messenger/useLoadPrevDay.ts deleted file mode 100644 index 983bc9e4..00000000 --- a/packages/status-react/src/hooks/messenger/useLoadPrevDay.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { useCallback, useEffect, useRef, useState } from 'react' - -import type { GroupChats, Messenger } from '@status-im/core' - -const _MS_PER_DAY = 1000 * 60 * 60 * 24 - -export function useLoadPrevDay( - chatId: string, - messenger: Messenger | undefined, - groupChats?: GroupChats -) { - const loadingPreviousMessages = useRef<{ - [chatId: string]: boolean - }>({}) - const lastLoadTime = useRef<{ - [chatId: string]: Date - }>({}) - const [loadingMessages, setLoadingMessages] = useState(false) - - useEffect(() => { - if (chatId) { - setLoadingMessages(loadingPreviousMessages.current[chatId]) - } - }, [chatId]) - - const loadPrevDay = useCallback( - async (id: string, groupChat?: boolean) => { - if (messenger && id) { - const endTime = lastLoadTime.current[id] ?? new Date() - const startTime = new Date(endTime.getTime() - _MS_PER_DAY * 5) - const timeDiff = Math.floor( - (new Date().getTime() - endTime.getTime()) / _MS_PER_DAY - ) - if (timeDiff < 28) { - if (!loadingPreviousMessages.current[id]) { - loadingPreviousMessages.current[id] = true - setLoadingMessages(true) - let amountOfMessages = 0 - let failed = true - try { - if (groupChat && groupChats) { - amountOfMessages = await groupChats.retrievePreviousMessages( - id, - startTime, - endTime - ) - } else { - amountOfMessages = await messenger.retrievePreviousMessages( - id, - startTime, - endTime - ) - } - lastLoadTime.current[id] = startTime - failed = false - } catch { - failed = true - } - loadingPreviousMessages.current[id] = false - setLoadingMessages(false) - if (amountOfMessages === 0 && !failed) { - loadPrevDay(id, groupChat) - } - } - } - } - }, - [messenger, groupChats] - ) - return { loadingMessages, loadPrevDay } -} diff --git a/packages/status-react/src/hooks/messenger/useMessages.ts b/packages/status-react/src/hooks/messenger/useMessages.ts deleted file mode 100644 index 65c41c8d..00000000 --- a/packages/status-react/src/hooks/messenger/useMessages.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { useCallback, useMemo, useState } from 'react' - -import { bufToHex } from '@status-im/core' - -import { ChatMessage } from '../../models/ChatMessage' -import { binarySetInsert } from '../../utils' -import { useNotifications } from './useNotifications' - -import type { - ApplicationMetadataMessage, - Contacts, - Identity, -} from '@status-im/core' - -export function useMessages( - chatId: string, - identity: Identity | undefined, - subscriptions: React.MutableRefObject< - ((msg: ChatMessage, id: string) => void)[] - >, - contacts?: Contacts -) { - const [messages, setMessages] = useState<{ [chatId: string]: ChatMessage[] }>( - {} - ) - const { notifications, incNotification, clearNotifications } = - useNotifications() - - const { - notifications: mentions, - incNotification: incMentions, - clearNotifications: clearMentions, - } = useNotifications() - - const addChatMessage = useCallback( - (newMessage: ChatMessage | undefined, id: string) => { - if (newMessage) { - contacts?.addContact(newMessage.sender) - setMessages(prev => { - if (newMessage.responseTo && prev[id]) { - newMessage.quote = prev[id].find( - msg => msg.id === newMessage.responseTo - ) - } - return { - ...prev, - [id]: binarySetInsert( - prev?.[id] ?? [], - newMessage, - (a, b) => a.date < b.date, - (a, b) => a.date.getTime() === b.date.getTime() - ), - } - }) - subscriptions.current.forEach(subscription => - subscription(newMessage, id) - ) - incNotification(id) - if ( - identity && - newMessage.content.includes(`@${bufToHex(identity.publicKey)}`) - ) { - incMentions(id) - } - } - }, - [contacts, identity, subscriptions, incMentions, incNotification] - ) - - const addMessage = useCallback( - (msg: ApplicationMetadataMessage, id: string, date: Date) => { - const newMessage = ChatMessage.fromMetadataMessage(msg, date) - addChatMessage(newMessage, id) - }, - [addChatMessage] - ) - - const activeMessages = useMemo(() => { - if (messages?.[chatId]) { - return [...messages[chatId]] - } - return [] - }, [messages, chatId]) - - return { - messages: activeMessages, - addMessage, - notifications, - clearNotifications, - mentions, - clearMentions, - addChatMessage, - } -} diff --git a/packages/status-react/src/hooks/messenger/useMessenger.ts b/packages/status-react/src/hooks/messenger/useMessenger.ts deleted file mode 100644 index 996a0c28..00000000 --- a/packages/status-react/src/hooks/messenger/useMessenger.ts +++ /dev/null @@ -1,361 +0,0 @@ -// import { StoreCodec } from "js-waku"; -import { - useCallback, - useEffect, - useMemo, - useReducer, - useRef, - useState, -} from 'react' - -import { createCommunity } from '../../utils/createCommunity' -import { createMessenger } from '../../utils/createMessenger' -import { uintToImgUrl } from '../../utils/uintToImgUrl' -import { useChannelsReducer } from './useChannelsReducer' -import { useContacts } from './useContacts' -import { useGroupChats } from './useGroupChats' -import { useLoadPrevDay } from './useLoadPrevDay' -import { useMessages } from './useMessages' - -import type { ChannelData, ChannelsData } from '../../models/ChannelData' -import type { ChatMessage } from '../../models/ChatMessage' -import type { CommunityData } from '../../models/CommunityData' -import type { Contacts } from '../../models/Contact' -import type { ChannelAction } from './useChannelsReducer' -import type { ContactsAction } from './useContacts' -import type { - ApplicationMetadataMessage, - Community, - Contacts as ContactsClass, - Identity, - Messenger, -} from '@status-im/core' -import type { Environment } from '~/src/types/config' - -export type MessengerType = { - messenger: Messenger | undefined - messages: ChatMessage[] - sendMessage: ( - messageText?: string | undefined, - image?: Uint8Array | undefined, - responseTo?: string - ) => Promise - notifications: { [chatId: string]: number } - clearNotifications: (id: string) => void - mentions: { [chatId: string]: number } - clearMentions: (id: string) => void - loadPrevDay: (id: string, groupChat?: boolean) => Promise - loadingMessages: boolean - loadingMessenger: boolean - communityData: CommunityData | undefined - contacts: Contacts - contactsDispatch: (action: ContactsAction) => void - addContact: (publicKey: string) => void - channels: ChannelsData - channelsDispatch: (action: ChannelAction) => void - removeChannel: (channelId: string) => void - activeChannel: ChannelData | undefined - createGroupChat: (members: string[]) => void - changeGroupChatName: (name: string, chatId: string) => void - addMembers: (members: string[], chatId: string) => void - nickname: string | undefined - subscriptionsDispatch: (action: SubscriptionAction) => void -} - -function useCreateMessenger( - environment: Environment, - identity: Identity | undefined -) { - const [messenger, setMessenger] = useState(undefined) - useEffect(() => { - createMessenger(identity, environment).then(e => { - setMessenger(e) - }) - }, [identity, environment]) - - return messenger -} - -function useCreateCommunity( - messenger: Messenger | undefined, - identity: Identity | undefined, - communityKey: string | undefined, - addMessage: (msg: ApplicationMetadataMessage, id: string, date: Date) => void, - contactsClass: ContactsClass | undefined -) { - const [community, setCommunity] = useState(undefined) - - useEffect(() => { - if ( - messenger && - communityKey && - contactsClass && - addMessage && - messenger.identity === identity - ) { - createCommunity(communityKey, addMessage, messenger).then(comm => { - setCommunity(comm) - }) - } - }, [messenger, communityKey, addMessage, contactsClass, identity]) - - const communityData = useMemo(() => { - if (community?.description) { - const membersList = Object.keys(community.description.proto.members) - - if (contactsClass) { - membersList.forEach(contactsClass.addContact, contactsClass) - } - - return { - id: community.publicKeyStr, - name: community.description.identity?.displayName ?? '', - icon: uintToImgUrl( - community.description?.identity?.images?.thumbnail?.payload ?? - new Uint8Array() - ), - members: membersList.length, - membersList, - description: community.description.identity?.description ?? '', - } - } else { - return undefined - } - }, [community, contactsClass]) - - return { community, communityData } -} - -type Subscriptions = { - [id: string]: (msg: ChatMessage, id: string) => void -} - -type SubscriptionAction = - | { - type: 'addSubscription' - payload: { - name: string - subFunction: (msg: ChatMessage, id: string) => void - } - } - | { type: 'removeSubscription'; payload: { name: string } } - -function subscriptionReducer( - state: Subscriptions, - action: SubscriptionAction -): Subscriptions { - switch (action.type) { - case 'addSubscription': { - if (state[action.payload.name]) { - throw new Error('Subscription already exists') - } - return { ...state, [action.payload.name]: action.payload.subFunction } - } - case 'removeSubscription': { - if (state[action.payload.name]) { - const newState = { ...state } - delete newState[action.payload.name] - return newState - } - return state - } - default: - throw new Error('Wrong subscription action type') - } -} - -export function useMessenger( - communityKey: string, - environment: Environment | undefined = 'production', - identity: Identity | undefined, - newNickname: string | undefined -) { - const [subscriptions, subscriptionsDispatch] = useReducer( - subscriptionReducer, - {} - ) - const subList = useRef<((msg: ChatMessage, id: string) => void)[]>([]) - useEffect(() => { - subList.current = Object.values(subscriptions) - }, [subscriptions]) - - const [channelsState, channelsDispatch] = useChannelsReducer() - const messenger = useCreateMessenger(environment, identity) - const { contacts, contactsDispatch, contactsClass, nickname } = useContacts( - messenger, - identity, - newNickname - ) - - const addContact = useCallback( - (publicKey: string) => { - if (contactsClass) { - contactsClass.addContact(publicKey) - } - }, - [contactsClass] - ) - - const { - addChatMessage, - addMessage, - clearNotifications, - notifications, - messages, - mentions, - clearMentions, - } = useMessages( - channelsState?.activeChannel?.id, - identity, - subList, - contactsClass - ) - - const { community, communityData } = useCreateCommunity( - messenger, - identity, - communityKey, - addMessage, - contactsClass - ) - - useEffect(() => { - if (community?.chats) { - for (const chat of community.chats.values()) { - channelsDispatch({ - type: 'AddChannel', - payload: { - id: chat.id, - name: chat.communityChat?.identity?.displayName ?? '', - description: chat.communityChat?.identity?.description ?? '', - type: 'channel', - }, - }) - } - } - }, [community, channelsDispatch]) - - useEffect(() => { - Object.values(channelsState.channels) - .filter(channel => channel.type === 'dm') - .forEach(channel => { - const contact = contacts?.[channel?.members?.[1]?.id ?? ''] - if ( - contact && - channel.name !== (contact?.customName ?? contact.trueName) - ) { - channelsDispatch({ - type: 'AddChannel', - payload: { - ...channel, - name: contact?.customName ?? contact.trueName, - }, - }) - } - }) - }, [contacts, channelsState.channels, channelsDispatch]) - - const { - groupChat, - removeChannel, - createGroupChat, - changeGroupChatName, - addMembers, - } = useGroupChats( - messenger, - identity, - channelsDispatch, - addChatMessage, - contactsClass - ) - - const { loadPrevDay, loadingMessages } = useLoadPrevDay( - channelsState.activeChannel.id, - messenger, - groupChat - ) - - useEffect(() => { - if (messenger && community?.chats) { - Array.from(community?.chats.values()).forEach(({ id }) => loadPrevDay(id)) - } - }, [messenger, community, loadPrevDay]) - - const sendMessage = useCallback( - async (messageText?: string, image?: Uint8Array, responseTo?: string) => { - let content - if (messageText) { - content = { - text: messageText, - contentType: 0, - } - } - if (image) { - content = { - image, - imageType: 1, - contentType: 2, - } - } - if (content) { - if (channelsState.activeChannel.type !== 'channel') { - await groupChat?.sendMessage( - channelsState.activeChannel.id, - content, - responseTo - ) - } else { - await messenger?.sendMessage( - channelsState.activeChannel.id, - content, - responseTo - ) - } - } - }, - [messenger, groupChat, channelsState.activeChannel] - ) - - useEffect(() => { - if (channelsState.activeChannel) { - if (notifications[channelsState.activeChannel.id] > 0) { - clearNotifications(channelsState.activeChannel.id) - clearMentions(channelsState.activeChannel.id) - } - } - }, [notifications, channelsState, clearNotifications, clearMentions]) - - const loadingMessenger = useMemo(() => { - return Boolean( - (communityKey && !communityData) || - !messenger || - (communityKey && !channelsState.activeChannel.id) - ) - }, [communityData, messenger, channelsState, communityKey]) - - return { - messenger, - messages, - sendMessage, - notifications, - clearNotifications, - loadPrevDay, - loadingMessages, - loadingMessenger, - communityData, - contacts, - contactsDispatch, - addContact, - channels: channelsState.channels, - channelsDispatch, - removeChannel, - activeChannel: channelsState.activeChannel, - mentions, - clearMentions, - createGroupChat, - changeGroupChatName, - addMembers, - nickname, - subscriptionsDispatch, - } -} diff --git a/packages/status-react/src/hooks/messenger/useNotifications.ts b/packages/status-react/src/hooks/messenger/useNotifications.ts deleted file mode 100644 index 185d488f..00000000 --- a/packages/status-react/src/hooks/messenger/useNotifications.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { useCallback, useState } from 'react' - -export function useNotifications() { - const [notifications, setNotifications] = useState<{ - [chatId: string]: number - }>({}) - const incNotification = useCallback((id: string) => { - setNotifications(prevNotifications => { - return { - ...prevNotifications, - [id]: (prevNotifications?.[id] ?? 0) + 1, - } - }) - }, []) - const clearNotifications = useCallback((id: string) => { - setNotifications(prevNotifications => { - return { - ...prevNotifications, - [id]: 0, - } - }) - }, []) - return { notifications, incNotification, clearNotifications } -} diff --git a/packages/status-react/src/hooks/useClickOutside.ts b/packages/status-react/src/hooks/useClickOutside.ts deleted file mode 100644 index 4dc4f44b..00000000 --- a/packages/status-react/src/hooks/useClickOutside.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { useCallback, useEffect } from 'react' - -import type { RefObject } from 'react' - -export const useClickOutside = ( - ref: RefObject, - callback: () => void -) => { - const handleClick = useCallback( - (e: MouseEvent) => { - if (ref.current && !ref.current.contains(e.target as HTMLInputElement)) { - callback() - } - }, - [ref, callback] - ) - - useEffect(() => { - document.addEventListener('mousedown', handleClick) - - return () => { - document.removeEventListener('mousedown', handleClick) - } - }, [handleClick]) -} diff --git a/packages/status-react/src/hooks/useClickPosition.ts b/packages/status-react/src/hooks/useClickPosition.ts deleted file mode 100644 index a1b0b416..00000000 --- a/packages/status-react/src/hooks/useClickPosition.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { useCallback, useEffect, useState } from 'react' - -import type { RefObject } from 'react' - -export const useClickPosition = (ref: RefObject) => { - const [topPosition, setTopPosition] = useState(0) - const [leftPosition, setLeftPosition] = useState(0) - - const getPosition = useCallback( - (e: MouseEvent) => { - if (ref.current) { - const target = e.target as HTMLImageElement - const imgTarget = target.tagName === 'IMG' - const rect = ref.current.getBoundingClientRect() - const x = ref.current.clientWidth - e.clientX < 180 ? 180 : 0 - setLeftPosition(imgTarget ? -200 : e.clientX - rect.left - x) - setTopPosition(imgTarget ? 0 : e.clientY - rect.top) - } - }, - [setTopPosition, setLeftPosition, ref] - ) - - useEffect(() => { - document.addEventListener('contextmenu', getPosition) - - return () => { - document.removeEventListener('contextmenu', getPosition) - } - }) - - return { topPosition, leftPosition } -} diff --git a/packages/status-react/src/hooks/useContextMenu.ts b/packages/status-react/src/hooks/useContextMenu.ts deleted file mode 100644 index 9e52c6b4..00000000 --- a/packages/status-react/src/hooks/useContextMenu.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { useCallback, useEffect, useState } from 'react' - -export const useContextMenu = (elementId: string) => { - const [showMenu, setShowMenu] = useState(false) - - const handleContextMenu = useCallback( - event => { - event.preventDefault() - setShowMenu(true) - }, - [setShowMenu] - ) - - useEffect(() => { - const element = document.getElementById(elementId) || document - - element.addEventListener('contextmenu', handleContextMenu) - document.addEventListener('click', () => setShowMenu(false)) - return () => { - element.removeEventListener('contextmenu', handleContextMenu) - document.removeEventListener('click', () => setShowMenu(false)) - setShowMenu(false) - } - }, [elementId, handleContextMenu]) - - return { showMenu, setShowMenu } -} diff --git a/packages/status-react/src/hooks/useReply.ts b/packages/status-react/src/hooks/useReply.ts deleted file mode 100644 index c483b557..00000000 --- a/packages/status-react/src/hooks/useReply.ts +++ /dev/null @@ -1,6 +0,0 @@ -export type Reply = { - sender: string - content: string - image?: string - id: string -} diff --git a/packages/status-react/src/index.tsx b/packages/status-react/src/index.tsx index 84de0f61..3d466b19 100644 --- a/packages/status-react/src/index.tsx +++ b/packages/status-react/src/index.tsx @@ -1,5 +1,5 @@ -export type { CommunityProps } from './modules/community' -export { Community } from './modules/community' +export type { CommunityProps } from './routes' +export { Community } from './routes' export { darkTheme, theme as lightTheme } from './styles/config' export type { Config } from './types/config' export { HashRouter, MemoryRouter } from 'react-router-dom' diff --git a/packages/status-react/src/models/Activity.ts b/packages/status-react/src/models/Activity.ts deleted file mode 100644 index d78693f5..00000000 --- a/packages/status-react/src/models/Activity.ts +++ /dev/null @@ -1,49 +0,0 @@ -import type { ChannelData } from './ChannelData' -import type { ChatMessage } from './ChatMessage' -import type { CommunityData } from './CommunityData' - -export type ActivityStatus = 'sent' | 'accepted' | 'declined' | 'blocked' - -export type Activity = - | { - id: string - type: 'mention' - date: Date - user: string - message: ChatMessage - channel: ChannelData - isRead?: boolean - } - | { - id: string - type: 'reply' - date: Date - user: string - message: ChatMessage - channel: ChannelData - quote: ChatMessage - isRead?: boolean - } - | { - id: string - type: 'request' - date: Date - user: string - isRead?: boolean - request: string - requestType: 'outcome' | 'income' - status: ActivityStatus - } - | { - id: string - type: 'invitation' - isRead?: boolean - date: Date - user: string - status: ActivityStatus - invitation?: CommunityData - } - -export type Activities = { - [id: string]: Activity -} diff --git a/packages/status-react/src/models/ChannelData.ts b/packages/status-react/src/models/ChannelData.ts deleted file mode 100644 index d0a60b3f..00000000 --- a/packages/status-react/src/models/ChannelData.ts +++ /dev/null @@ -1,15 +0,0 @@ -import type { Contact } from './Contact' - -export type ChannelData = { - id: string - name: string - type: 'channel' | 'dm' | 'group' - description?: string - icon?: string - isMuted?: boolean - members?: Contact[] -} - -export type ChannelsData = { - [id: string]: ChannelData -} diff --git a/packages/status-react/src/models/ChatMessage.ts b/packages/status-react/src/models/ChatMessage.ts deleted file mode 100644 index 6e6b9518..00000000 --- a/packages/status-react/src/models/ChatMessage.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { bufToHex } from '@status-im/core' -import { keccak256 } from 'js-sha3' - -import { uintToImgUrl } from '../utils' - -import type { ApplicationMetadataMessage } from '@status-im/core' - -export class ChatMessage { - content: string - date: Date - sender: string - image?: string - responseTo?: string - quote?: ChatMessage - id: string - - constructor( - content: string, - date: Date, - sender: string, - image?: string, - responseTo?: string - ) { - this.content = content - this.date = date - this.sender = sender - this.image = image - this.responseTo = responseTo - this.id = keccak256(date.getTime().toString() + content) - } - - public static fromMetadataMessage( - msg: ApplicationMetadataMessage, - date: Date - ) { - if ( - msg.signer && - (msg.chatMessage?.text || msg.chatMessage?.image) && - msg.chatMessage.clock - ) { - const content = msg.chatMessage.text ?? '' - let image: string | undefined = undefined - if (msg.chatMessage?.image) { - image = uintToImgUrl(msg.chatMessage?.image.payload) - } - const sender = bufToHex(msg.signer) - return new ChatMessage( - content, - date, - sender, - image, - msg.chatMessage.responseTo - ) - } else { - return undefined - } - } -} diff --git a/packages/status-react/src/models/CommunityData.ts b/packages/status-react/src/models/CommunityData.ts deleted file mode 100644 index 4baaaf4f..00000000 --- a/packages/status-react/src/models/CommunityData.ts +++ /dev/null @@ -1,8 +0,0 @@ -export type CommunityData = { - id: string - name: string - icon: string - members: number - membersList: string[] - description: string -} diff --git a/packages/status-react/src/models/Contact.ts b/packages/status-react/src/models/Contact.ts deleted file mode 100644 index 38f8af9a..00000000 --- a/packages/status-react/src/models/Contact.ts +++ /dev/null @@ -1,13 +0,0 @@ -export type Contact = { - id: string - online?: boolean - trueName: string - customName?: string - isUntrustworthy?: boolean - blocked?: boolean - isFriend?: boolean -} - -export type Contacts = { - [id: string]: Contact -} diff --git a/packages/status-react/src/models/Metadata.ts b/packages/status-react/src/models/Metadata.ts deleted file mode 100644 index 1a6f01e2..00000000 --- a/packages/status-react/src/models/Metadata.ts +++ /dev/null @@ -1,5 +0,0 @@ -export interface Metadata { - 'og:site_name': string - 'og:title': string - 'og:image': string -} diff --git a/packages/status-react/src/models/Toast.ts b/packages/status-react/src/models/Toast.ts deleted file mode 100644 index a691ffa9..00000000 --- a/packages/status-react/src/models/Toast.ts +++ /dev/null @@ -1,6 +0,0 @@ -export type Toast = { - id: string - type: 'confirmation' | 'incoming' | 'approvement' | 'rejection' - text: string - request?: string -} diff --git a/packages/status-react/src/modules/community/index.tsx b/packages/status-react/src/modules/community/index.tsx deleted file mode 100644 index 45711fb5..00000000 --- a/packages/status-react/src/modules/community/index.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import React, { useRef } from 'react' - -import { BrowserRouter } from 'react-router-dom' - -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' -import { NarrowProvider } from '~/src/contexts/narrowProvider' -import { ScrollProvider } from '~/src/contexts/scrollProvider' -import { ThemeProvider } from '~/src/contexts/theme-context' -import { ToastProvider } from '~/src/contexts/toastProvider' -import { styled } from '~/src/styles/config' -import { GlobalStyle } from '~/src/styles/GlobalStyle' - -import { Messenger } from './messenger' - -import type { Config } from '~/src/types/config' - -type Props = Config - -export const Community = (props: Props) => { - const { - theme, - environment, - publicKey, - router: Router = BrowserRouter, - } = props - - const ref = useRef(null) - - return ( - - - - - - - - - - - - - - -
- - - - - - - - - - - - - ) -} - -export type { Props as CommunityProps } - -const Wrapper = styled('div', { - height: '100%', - overflow: 'hidden', -}) diff --git a/packages/status-react/src/modules/community/messenger.tsx b/packages/status-react/src/modules/community/messenger.tsx deleted file mode 100644 index ef373498..00000000 --- a/packages/status-react/src/modules/community/messenger.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import React from 'react' - -import { Route, Routes } from 'react-router-dom' - -import { MainSidebar } from '~/src/components/main-sidebar' -import { useAppState } from '~/src/contexts/app-context' -import { Chat } from '~/src/routes/chat' -import { NewChat } from '~/src/routes/new-chat' -import { styled } from '~/src/styles/config' - -export function Messenger() { - const { options } = useAppState() - - return ( - - {options.enableMembers && } - - } /> - } /> - - - ) -} - -const Wrapper = styled('div', { - position: 'relative', - width: '100%', - height: '100%', - display: 'flex', - alignItems: 'stretch', - background: '$background', -}) diff --git a/packages/status-react/src/routes/index.tsx b/packages/status-react/src/routes/index.tsx index 1c728e18..9fc1cc0d 100644 --- a/packages/status-react/src/routes/index.tsx +++ b/packages/status-react/src/routes/index.tsx @@ -2,43 +2,37 @@ import React from 'react' import { BrowserRouter, Route, Routes } from 'react-router-dom' +import { MainSidebar } from '~/src/components/main-sidebar' import { AppProvider } from '~/src/contexts/app-context' import { DialogProvider } from '~/src/contexts/dialog-context' +import { ThemeProvider } from '~/src/contexts/theme-context' +import { Chat } from '~/src/routes/chat' +import { NewChat } from '~/src/routes/new-chat' import { styled } from '~/src/styles/config' - -import { MainSidebar } from '../components/main-sidebar' -import { Box } from '../system' -import { Chat } from './chat' -import { NewChat } from './new-chat' +import { GlobalStyle } from '~/src/styles/GlobalStyle' import type { Config } from '~/src/types/config' type Props = Config export const Community = (props: Props) => { - const { - // theme, - // environment, - // publicKey, - router: Router = BrowserRouter, - } = props - - const { options } = props + const { theme, router: Router = BrowserRouter } = props return ( - - + + + - {options.enableMembers && } + } /> } /> - - + + ) @@ -47,6 +41,7 @@ export const Community = (props: Props) => { export type { Props as CommunityProps } const Wrapper = styled('div', { + overflow: 'hidden', position: 'relative', width: '100%', height: '100%', diff --git a/packages/status-react/src/routes/new-chat/index.tsx b/packages/status-react/src/routes/new-chat/index.tsx index 1a23fa35..40d1fea6 100644 --- a/packages/status-react/src/routes/new-chat/index.tsx +++ b/packages/status-react/src/routes/new-chat/index.tsx @@ -1,3 +1,4 @@ +// @ts-nocheck import React, { useCallback, useMemo, useState } from 'react' import { BellIcon } from '~/src/icons/bell-icon' diff --git a/packages/status-react/src/utils/copy.ts b/packages/status-react/src/utils/copy.ts deleted file mode 100644 index 7dfecd39..00000000 --- a/packages/status-react/src/utils/copy.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const copy = (text: string) => { - navigator.clipboard.writeText(text).catch(error => { - console.log(error) - }) -} diff --git a/packages/status-react/src/utils/createCommunity.ts b/packages/status-react/src/utils/createCommunity.ts deleted file mode 100644 index 6d86634e..00000000 --- a/packages/status-react/src/utils/createCommunity.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Community } from '@status-im/core' - -import type { ApplicationMetadataMessage, Messenger } from '@status-im/core' - -export async function createCommunity( - communityKey: string, - addMessage: (msg: ApplicationMetadataMessage, id: string, date: Date) => void, - messenger: Messenger -) { - const community = await Community.instantiateCommunity( - communityKey, - messenger.waku - ) - await Promise.all( - Array.from(community.chats.values()).map(async chat => { - await messenger.joinChat(chat) - messenger.addObserver( - (msg, date) => addMessage(msg, chat.id, date), - chat.id - ) - }) - ) - return community -} diff --git a/packages/status-react/src/utils/createMessenger.ts b/packages/status-react/src/utils/createMessenger.ts deleted file mode 100644 index b4e0305c..00000000 --- a/packages/status-react/src/utils/createMessenger.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { Messenger } from '@status-im/core' -import { getPredefinedBootstrapNodes } from 'js-waku' -import { Fleet } from 'js-waku/build/main/lib/discovery/predefined' -import { Protocols } from 'js-waku/build/main/lib/waku' - -import type { Environment } from '../types/config' -import type { Identity } from '@status-im/core' -import type { CreateOptions } from 'js-waku/build/main/lib/waku' - -function createWakuOptions(env: Environment): CreateOptions { - let bootstrap: CreateOptions['bootstrap'] = { - default: true, - } - - if (env === 'test') { - bootstrap = { - peers: getPredefinedBootstrapNodes(Fleet.Test).map(a => a.toString()), - } - } - - return { - bootstrap, - libp2p: { - config: { - pubsub: { - enabled: true, - emitSelf: true, - }, - }, - }, - } -} - -export async function createMessenger( - identity: Identity | undefined, - env: Environment -) { - const wakuOptions = createWakuOptions(env) - const messenger = await Messenger.create(identity, wakuOptions) - await messenger.waku.waitForRemotePeer([Protocols.Store]) - - return messenger -} diff --git a/packages/status-react/src/utils/downloadImg.ts b/packages/status-react/src/utils/downloadImg.ts deleted file mode 100644 index d04302f7..00000000 --- a/packages/status-react/src/utils/downloadImg.ts +++ /dev/null @@ -1,10 +0,0 @@ -export const downloadImg = async (image: string) => { - try { - const a = document.createElement('a') - a.download = `${image.split('/').pop()}.png` - a.href = image - a.click() - } catch { - return - } -} diff --git a/packages/status-react/src/utils/index.ts b/packages/status-react/src/utils/index.ts index c0add802..0c0b93c4 100644 --- a/packages/status-react/src/utils/index.ts +++ b/packages/status-react/src/utils/index.ts @@ -1,7 +1,5 @@ export { binarySetInsert } from './binarySetInsert' -export { copy } from './copy' export { copyImg } from './copyImg' -export { downloadImg } from './downloadImg' export { equalDate } from './equalDate' export { decryptIdentity,