Add narrow mode (channels) (#35)

This commit is contained in:
Maria Rushkova 2021-10-05 09:55:43 +02:00 committed by GitHub
parent 2f98d4e240
commit 8346801e51
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 288 additions and 17 deletions

View File

@ -1,6 +1,7 @@
import React, { useEffect } from "react";
import React, { useEffect, useMemo } from "react";
import styled from "styled-components";
import { useNarrow } from "../contexts/narrowProvider";
import { ChannelData, channels } from "../helpers/channelsMock";
import { CommunityData } from "../helpers/communityMock";
import { Theme } from "../styles/themes";
@ -81,6 +82,12 @@ export function Channel({
onClick,
notification,
}: ChannelProps) {
const narrow = useNarrow();
const className = useMemo(
() => (narrow && !activeView ? "narrow" : activeView ? "active" : ""),
[narrow]
);
return (
<ChannelWrapper
className={isActive && !activeView ? "active" : ""}
@ -92,7 +99,7 @@ export function Channel({
style={{
backgroundImage: channel.icon ? `url(${channel.icon}` : "",
}}
className={activeView ? "active" : ""}
className={className}
theme={theme}
>
{!channel.icon && channel.name.slice(0, 1).toUpperCase()}
@ -134,6 +141,7 @@ interface ThemeProps {
const ChannelsWrapper = styled.div<ThemeProps>`
width: 21%;
height: 100%;
min-width: 196px;
background-color: ${({ theme }) => theme.sectionBackgroundColor};
padding: 10px 0.6%;
display: flex;
@ -141,7 +149,7 @@ const ChannelsWrapper = styled.div<ThemeProps>`
`;
const StyledCommunity = styled(Community)`
padding-left: 0 0 0 10px;
padding: 0 0 0 10px;
margin: 0 0 16px;
`;
@ -152,10 +160,14 @@ const MembersAmount = styled.p<ThemeProps>`
color: ${({ theme }) => theme.secondary};
`;
const ChannelList = styled.div`
export const ChannelList = styled.div`
display: flex;
flex-direction: column;
margin-top: 16px;
overflow-y: scroll;
&::-webkit-scrollbar {
width: 0;
}
`;
const ChannelWrapper = styled.div<ThemeProps>`
@ -203,6 +215,13 @@ const ChannelLogo = styled.div<ThemeProps>`
font-size: 20px;
line-height: 20px;
}
&.narrow {
width: 40px;
height: 40px;
font-size: 20px;
line-height: 20px;
}
`;
const ChannelName = styled.p<ThemeProps>`

View File

@ -52,6 +52,9 @@ export function Chat({ theme, community }: ChatProps) {
channel={activeChannel}
messages={messages}
sendMessage={sendMessage}
notifications={notifications}
setActiveChannel={setActiveChannel}
activeChannelId={activeChannel.id}
onClick={() => setShowMembers(!showMembers)}
showMembers={showMembers}
community={community}

View File

@ -1,6 +1,7 @@
import React from "react";
import React, { useState } from "react";
import styled from "styled-components";
import { useNarrow } from "../../contexts/narrowProvider";
import { ChannelData } from "../../helpers/channelsMock";
import { CommunityData } from "../../helpers/communityMock";
import { ChatMessage } from "../../models/ChatMessage";
@ -8,6 +9,8 @@ import { Theme } from "../../styles/themes";
import { Channel } from "../Channels";
import { Community } from "../Community";
import { MembersIcon } from "../Icons/MembersIcon";
import { NarrowChannels } from "../NarrowMode/NarrowChannels";
import { NarrowTopbar } from "../NarrowMode/NarrowTopbar";
import { ChatInput } from "./ChatInput";
import { ChatMessages } from "./ChatMessages";
@ -21,6 +24,9 @@ interface ChatBodyProps {
onClick: () => void;
showMembers: boolean;
showCommunity: boolean;
notifications: { [id: string]: number };
setActiveChannel: (val: ChannelData) => void;
activeChannelId: number;
}
export function ChatBody({
@ -32,12 +38,19 @@ export function ChatBody({
onClick,
showMembers,
showCommunity,
notifications,
setActiveChannel,
activeChannelId,
}: ChatBodyProps) {
const narrow = useNarrow();
const [showChannelsList, setShowChannels] = useState(false);
const [showMembersList, setShowMembersList] = useState(false);
return (
<ChatBodyWrapper theme={theme}>
<ChatTopbar>
<ChannelWrapper>
{showCommunity && (
{(showCommunity || narrow) && (
<CommunityWrap theme={theme}>
<Community community={community} theme={theme} />
</CommunityWrap>
@ -48,18 +61,41 @@ export function ChatBody({
isActive={true}
activeView={true}
isMuted={false}
onClick={() => (narrow ? setShowChannels(true) : undefined)}
/>
</ChannelWrapper>
<MemberBtn
onClick={onClick}
onClick={narrow ? () => setShowMembersList(true) : onClick}
className={showMembers ? "active" : ""}
theme={theme}
>
<MembersIcon theme={theme} />
</MemberBtn>
</ChatTopbar>
<ChatMessages messages={messages} theme={theme} />
<ChatInput theme={theme} addMessage={sendMessage} />
{!showChannelsList && !showMembersList && (
<>
<ChatMessages messages={messages} theme={theme} />
<ChatInput theme={theme} addMessage={sendMessage} />
</>
)}
{showChannelsList && narrow && (
<NarrowChannels
theme={theme}
community={community.name}
notifications={notifications}
setActiveChannel={setActiveChannel}
setShowChannels={setShowChannels}
activeChannelId={activeChannelId}
/>
)}
{showMembersList && narrow && (
<NarrowTopbar
theme={theme}
list="Community members"
community={community.name}
onClick={() => setShowMembersList(false)}
/>
)}
</ChatBodyWrapper>
);
}
@ -100,7 +136,7 @@ const CommunityWrap = styled.div<ThemeProps>`
height: 24px;
transform: translateY(-50%);
border-radius: 1px;
background: ${({ theme }) => theme.textPrimaryColor};
background: ${({ theme }) => theme.primary};
opacity: 0.1;
}
`;

View File

@ -125,6 +125,10 @@ const Input = styled.textarea<ThemeProps>`
outline: none;
caret-color: ${({ theme }) => theme.notificationColor};
}
&::-webkit-scrollbar {
width: 0;
}
`;
const AddPictureBtn = styled.label`

View File

@ -69,6 +69,10 @@ const MessagesWrapper = styled.div`
height: calc(100% - 44px);
overflow: auto;
padding: 8px 16px 0;
&::-webkit-scrollbar {
width: 0;
}
`;
const MessageWrapper = styled.div`
@ -142,6 +146,6 @@ const TimeWrapper = styled.div<ThemeProps>`
const MessageText = styled.div<ThemeProps>`
overflow-wrap: anywhere;
width: 100%;
white-space: pre;
white-space: pre-wrap;
color: ${({ theme }) => theme.primary};
`;

View File

@ -43,6 +43,7 @@ const Logo = styled.img`
`;
const Name = styled.p`
font-family: "Inter", sans-serif;
font-weight: 500;
color: ${({ theme }) => theme.primary};

View File

@ -0,0 +1,31 @@
import React from "react";
import styled from "styled-components";
import { Theme } from "../../styles/themes";
interface ThemeProps {
theme: Theme;
}
export const BackIcon = ({ theme }: ThemeProps) => {
return (
<Icon
width="18"
height="14"
viewBox="0 0 18 14"
xmlns="http://www.w3.org/2000/svg"
theme={theme}
>
<path
d="M7.53033 1.53033C7.82322 1.23744 7.82322 0.762563 7.53033 0.46967C7.23744 0.176777 6.76256 0.176777 6.46967 0.46967L0.46967 6.46967C0.176777 6.76256 0.176777 7.23744 0.46967 7.53033L6.46967 13.5303C6.76256 13.8232 7.23744 13.8232 7.53033 13.5303C7.82322 13.2374 7.82322 12.7626 7.53033 12.4697L3.66421 8.60355C3.34923 8.28857 3.57232 7.75 4.01777 7.75H17C17.4142 7.75 17.75 7.41421 17.75 7C17.75 6.58579 17.4142 6.25 17 6.25H4.01777C3.57231 6.25 3.34923 5.71143 3.66421 5.39645L7.53033 1.53033Z"
fill="black"
/>
</Icon>
);
};
const Icon = styled.svg<ThemeProps>`
& > path {
fill: ${({ theme }) => theme.primary};
}
`;

View File

@ -0,0 +1,37 @@
import React from "react";
import styled from "styled-components";
import { Theme } from "../../styles/themes";
interface ThemeProps {
theme: Theme;
}
export const TagIcon = ({ theme }: ThemeProps) => {
return (
<Icon
width="20"
height="20"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
theme={theme}
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M10 18.5C14.6944 18.5 18.5 14.6944 18.5 10C18.5 5.30558 14.6944 1.5 10 1.5C5.30558 1.5 1.5 5.30558 1.5 10C1.5 14.6944 5.30558 18.5 10 18.5ZM10 20C15.5228 20 20 15.5228 20 10C20 4.47715 15.5228 0 10 0C4.47715 0 0 4.47715 0 10C0 15.5228 4.47715 20 10 20Z"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M9.48979 5.62329C9.55789 5.21472 9.28187 4.8283 8.87329 4.7602C8.46472 4.6921 8.0783 4.96812 8.0102 5.3767L7.76762 6.8322C7.72743 7.07329 7.51884 7.25 7.27442 7.25H6C5.58579 7.25 5.25 7.58579 5.25 8C5.25 8.41421 5.58579 8.75 6 8.75H6.85775C7.16672 8.75 7.40174 9.02743 7.35095 9.3322L7.10095 10.8322C7.06077 11.0733 6.85217 11.25 6.60775 11.25H5.5C5.08579 11.25 4.75 11.5858 4.75 12C4.75 12.4142 5.08579 12.75 5.5 12.75H6.19109C6.50006 12.75 6.73508 13.0274 6.68428 13.3322L6.5102 14.3767C6.4421 14.7853 6.71812 15.1717 7.1267 15.2398C7.53527 15.3079 7.92169 15.0319 7.98979 14.6233L8.23237 13.1678C8.27256 12.9267 8.48115 12.75 8.72557 12.75H10.1911C10.5001 12.75 10.7351 13.0274 10.6843 13.3322L10.5102 14.3767C10.4421 14.7853 10.7181 15.1717 11.1267 15.2398C11.5353 15.3079 11.9217 15.0319 11.9898 14.6233L12.2324 13.1678C12.2726 12.9267 12.4811 12.75 12.7256 12.75H14C14.4142 12.75 14.75 12.4142 14.75 12C14.75 11.5858 14.4142 11.25 14 11.25H13.1422C12.8333 11.25 12.5982 10.9726 12.649 10.6678L12.899 9.1678C12.9392 8.92671 13.1478 8.75 13.3922 8.75H14.5C14.9142 8.75 15.25 8.41421 15.25 8C15.25 7.58579 14.9142 7.25 14.5 7.25H13.8089C13.4999 7.25 13.2649 6.97257 13.3157 6.6678L13.4898 5.62329C13.5579 5.21472 13.2819 4.8283 12.8733 4.7602C12.4647 4.6921 12.0783 4.96812 12.0102 5.3767L11.7676 6.8322C11.7274 7.07329 11.5188 7.25 11.2744 7.25H9.8089C9.49993 7.25 9.26491 6.97257 9.31571 6.6678L9.48979 5.62329ZM10.6078 11.25C10.8522 11.25 11.0608 11.0733 11.1009 10.8322L11.3509 9.3322C11.4017 9.02743 11.1667 8.75 10.8578 8.75H9.39224C9.14782 8.75 8.93922 8.92671 8.89904 9.1678L8.64904 10.6678C8.59824 10.9726 8.83327 11.25 9.14224 11.25H10.6078Z"
/>
</Icon>
);
};
const Icon = styled.svg<ThemeProps>`
& > path {
fill: ${({ theme }) => theme.secondary};
}
`;

View File

@ -96,6 +96,7 @@ interface ThemeProps {
const MembersWrapper = styled.div<ThemeProps>`
width: 18%;
height: 100%;
min-width: 164px;
display: flex;
flex-direction: column;
background-color: ${({ theme }) => theme.sectionBackgroundColor};
@ -106,7 +107,7 @@ const MemberHeading = styled.h2<ThemeProps>`
font-weight: 500;
font-size: 15px;
line-height: 22px;
color: ${({ theme }) => theme.textPrimaryColor};
color: ${({ theme }) => theme.primary};
margin-bottom: 16px;
`;
@ -114,7 +115,7 @@ const MemberCategoryName = styled.h3<ThemeProps>`
font-weight: normal;
font-size: 13px;
line-height: 18px;
color: ${({ theme }) => theme.textSecondaryColor};
color: ${({ theme }) => theme.secondary};
margin-bottom: 8px;
`;
@ -122,7 +123,7 @@ const MemberName = styled.p<ThemeProps>`
font-weight: 500;
font-size: 15px;
line-height: 22px;
color: ${({ theme }) => theme.textPrimaryColor};
color: ${({ theme }) => theme.primary};
opacity: 0.7;
margin-left: 8px;
text-overflow: ellipsis;
@ -133,6 +134,11 @@ const MemberName = styled.p<ThemeProps>`
const MembersList = styled.div`
display: flex;
flex-direction: column;
overflow-y: scroll;
&::-webkit-scrollbar {
width: 0;
}
`;
const MemberCategory = styled.div`

View File

@ -0,0 +1,61 @@
import React from "react";
import styled from "styled-components";
import { ChannelData, channels } from "../../helpers/channelsMock";
import { Theme } from "../../styles/themes";
import { Channel, ChannelList } from "../Channels";
import { NarrowTopbar } from "./NarrowTopbar";
interface NarrowChannelsProps {
theme: Theme;
community: string;
notifications: { [id: string]: number };
setActiveChannel: (val: ChannelData) => void;
activeChannelId: number;
setShowChannels: (val: boolean) => void;
}
export function NarrowChannels({
theme,
community,
notifications,
setActiveChannel,
activeChannelId,
setShowChannels,
}: NarrowChannelsProps) {
return (
<ListWrapper>
<NarrowTopbar
theme={theme}
list="Channels"
community={community}
onClick={() => setShowChannels(false)}
/>
<ChannelList>
{channels.map((channel) => (
<Channel
key={channel.id}
channel={channel}
theme={theme}
isActive={channel.id === activeChannelId}
isMuted={channel.isMuted || false}
notification={
notifications[channel.name] > 0 && !channel.isMuted
? notifications[channel.name]
: undefined
}
onClick={() => {
setActiveChannel(channel);
setShowChannels(false);
}}
/>
))}
</ChannelList>
</ListWrapper>
);
}
const ListWrapper = styled.div`
padding: 18px;
`;

View File

@ -0,0 +1,70 @@
import React from "react";
import styled from "styled-components";
import { Theme } from "../../styles/themes";
import { BackIcon } from "../Icons/BackIcon";
interface NarrowTopbarProps {
theme: Theme;
list: string;
community: string;
onClick: () => void;
}
export function NarrowTopbar({
theme,
list,
community,
onClick,
}: NarrowTopbarProps) {
return (
<TopbarWrapper theme={theme}>
<GoBackBtn onClick={onClick}>
<BackIcon theme={theme} />
</GoBackBtn>
<HeadingWrapper>
<Heading theme={theme}>{list}</Heading>
<SubHeading theme={theme}>{community}</SubHeading>
</HeadingWrapper>
</TopbarWrapper>
);
}
interface ThemeProps {
theme: Theme;
}
const TopbarWrapper = styled.div<ThemeProps>`
display: flex;
justify-content: center;
background-color: ${({ theme }) => theme.bodyBackgroundColor};
padding: 14px 0;
position: relative;
`;
const HeadingWrapper = styled.div`
display: flex;
flex-direction: column;
text-align: center;
`;
const Heading = styled.p<ThemeProps>`
font-weight: 500;
color: ${({ theme }) => theme.primary};
`;
const SubHeading = styled.p<ThemeProps>`
font-weight: 500;
color: ${({ theme }) => theme.secondary};
`;
const GoBackBtn = styled.button`
width: 24px;
height: 24px;
margin-right: 18px;
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
`;

View File

@ -14,6 +14,6 @@ interface NarrowProviderProps {
}
export function NarrowProvider({ children, myRef }: NarrowProviderProps) {
const narrow = useRefWidthBreak(myRef, 735);
const narrow = useRefWidthBreak(myRef, 736);
return <NarrowContext.Provider value={narrow} children={children} />;
}

View File

@ -13,7 +13,6 @@ export const GlobalStyle = createGlobalStyle`
font-weight: normal;
font-size: 15px;
line-height: 22px;
overflow-x: hidden;
}
html,