diff --git a/packages/react-chat/src/components/Chat.tsx b/packages/react-chat/src/components/Chat.tsx index a79727d7..1a46fe44 100644 --- a/packages/react-chat/src/components/Chat.tsx +++ b/packages/react-chat/src/components/Chat.tsx @@ -49,26 +49,23 @@ export function Chat({ theme, community, fetchMetadata }: ChatProps) { activeChannelId={activeChannel.id} /> )} - {messenger ? ( - setShowMembers(!showMembers)} - showMembers={showMembers} - community={community} - showCommunity={!showChannels} - loadNextDay={() => loadNextDay(activeChannel.name)} - lastMessage={lastMessage} - fetchMetadata={fetchMetadata} - /> - ) : ( - Connecting to waku - )} + setShowMembers(!showMembers)} + showMembers={showMembers} + community={community} + showCommunity={!showChannels} + loadNextDay={() => loadNextDay(activeChannel.name)} + lastMessage={lastMessage} + fetchMetadata={fetchMetadata} + /> {showMembers && !narrow && ( void; onClick: () => void; @@ -38,6 +41,7 @@ export function ChatBody({ theme, channel, community, + messenger, messages, sendMessage, onClick, @@ -99,43 +103,57 @@ export function ChatBody({ > + {!messenger && } - {!showChannelsList && !showMembersList && ( + {messenger ? ( <> - {" "} - {messages.length > 0 ? ( - - ) : ( - + {!showChannelsList && !showMembersList && ( + <> + {" "} + {messages.length > 0 ? ( + messenger ? ( + + ) : ( + + ) + ) : ( + + )} + + )} + + {showChannelsList && narrow && ( + + )} + {showMembersList && narrow && ( + + )} + + ) : ( + <> + )} - - {showChannelsList && narrow && ( - - )} - {showMembersList && narrow && ( - - )} ); } @@ -169,6 +187,7 @@ const ChatTopbar = styled.div` justify-content: space-between; padding: 5px 8px; background: ${({ theme }) => theme.bodyBackgroundColor}; + position: relative; &.narrow { position: fixed; diff --git a/packages/react-chat/src/components/Icons/LoadingIcon.tsx b/packages/react-chat/src/components/Icons/LoadingIcon.tsx new file mode 100644 index 00000000..aab25b00 --- /dev/null +++ b/packages/react-chat/src/components/Icons/LoadingIcon.tsx @@ -0,0 +1,36 @@ +import React from "react"; +import styled, { keyframes } from "styled-components"; + +import { Theme } from "../../styles/themes"; + +const rotation = keyframes` + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +`; + +export const LoadingIcon = ({ theme }: { theme: Theme }) => ( + + + +); + +const Icon = styled.svg` + & > path { + fill: ${({ theme }) => theme.primary}; + } + animation: ${rotation} 2s linear infinite; +`; diff --git a/packages/react-chat/src/components/Icons/TagIcon.tsx b/packages/react-chat/src/components/Icons/TagIcon.tsx deleted file mode 100644 index b567b481..00000000 --- a/packages/react-chat/src/components/Icons/TagIcon.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import React from "react"; -import styled from "styled-components"; - -import { Theme } from "../../styles/themes"; - -interface ThemeProps { - theme: Theme; -} - -export const TagIcon = ({ theme }: ThemeProps) => { - return ( - - - - - ); -}; - -const Icon = styled.svg` - & > path { - fill: ${({ theme }) => theme.secondary}; - } -`; diff --git a/packages/react-chat/src/components/Skeleton/Loading.tsx b/packages/react-chat/src/components/Skeleton/Loading.tsx new file mode 100644 index 00000000..e1fde83a --- /dev/null +++ b/packages/react-chat/src/components/Skeleton/Loading.tsx @@ -0,0 +1,43 @@ +import React from "react"; +import styled from "styled-components"; + +import { Theme } from "../../styles/themes"; +import { LoadingIcon } from "../Icons/LoadingIcon"; +import { textSmallStyles } from "../Text"; + +interface LoadingProps { + theme: Theme; +} + +export const Loading = ({ theme }: LoadingProps) => { + 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: 0px 2px 4px rgba(0, 34, 51, 0.16), + 0px 4px 12px rgba(0, 34, 51, 0.08); + 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/react-chat/src/components/Skeleton/LoadingSkeleton.tsx b/packages/react-chat/src/components/Skeleton/LoadingSkeleton.tsx new file mode 100644 index 00000000..2ebdba84 --- /dev/null +++ b/packages/react-chat/src/components/Skeleton/LoadingSkeleton.tsx @@ -0,0 +1,66 @@ +import React from "react"; +import styled from "styled-components"; + +import { Theme } from "../../styles/themes"; + +import { MessageSkeleton } from "./MessageSkeleton"; +import { Skeleton } from "./Skeleton"; + +interface LoadingSkeletonProps { + theme: Theme; +} + +export const LoadingSkeleton = ({ theme }: LoadingSkeletonProps) => { + 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/react-chat/src/components/Skeleton/MessageSkeleton.tsx b/packages/react-chat/src/components/Skeleton/MessageSkeleton.tsx new file mode 100644 index 00000000..ff67bd84 --- /dev/null +++ b/packages/react-chat/src/components/Skeleton/MessageSkeleton.tsx @@ -0,0 +1,66 @@ +import React, { ReactNode } from "react"; +import styled from "styled-components"; + +import { Theme } from "../../styles/themes"; + +import { Skeleton } from "./Skeleton"; + +interface MessageSkeletonProps { + theme: Theme; + children: ReactNode; +} + +export const MessageSkeleton = ({ theme, 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/react-chat/src/components/Skeleton/Skeleton.tsx b/packages/react-chat/src/components/Skeleton/Skeleton.tsx new file mode 100644 index 00000000..7a36cc07 --- /dev/null +++ b/packages/react-chat/src/components/Skeleton/Skeleton.tsx @@ -0,0 +1,49 @@ +import styled, { keyframes } from "styled-components"; + +import { Theme } from "../../styles/themes"; + +interface SkeletonProps { + theme: Theme; + 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/react-chat/src/styles/themes.ts b/packages/react-chat/src/styles/themes.ts index 4efd44d8..17d7e566 100644 --- a/packages/react-chat/src/styles/themes.ts +++ b/packages/react-chat/src/styles/themes.ts @@ -13,6 +13,8 @@ export type Theme = { inputColor: string; border: string; buttonBg: string; + skeletonLight: string; + skeletonDark: string; }; export const lightTheme: Theme = { @@ -30,6 +32,8 @@ export const lightTheme: Theme = { inputColor: "#EEF2F5", border: "#EEF2F5", buttonBg: "rgba(67, 96, 223, 0.2)", + skeletonLight: "#F6F8FA", + skeletonDark: "#E9EDF1", }; export const darkTheme: Theme = { @@ -47,6 +51,8 @@ export const darkTheme: Theme = { inputColor: "#373737", border: "#373737", buttonBg: "rgba(134, 158, 255, 0.3)", + skeletonLight: "#2E2F31", + skeletonDark: "#141414", }; export default { lightTheme, darkTheme };