Add narrow mode (channels) (#35)
This commit is contained in:
parent
2f98d4e240
commit
8346801e51
|
@ -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>`
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
`;
|
||||
|
|
|
@ -125,6 +125,10 @@ const Input = styled.textarea<ThemeProps>`
|
|||
outline: none;
|
||||
caret-color: ${({ theme }) => theme.notificationColor};
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
const AddPictureBtn = styled.label`
|
||||
|
|
|
@ -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};
|
||||
`;
|
||||
|
|
|
@ -43,6 +43,7 @@ const Logo = styled.img`
|
|||
`;
|
||||
|
||||
const Name = styled.p`
|
||||
font-family: "Inter", sans-serif;
|
||||
font-weight: 500;
|
||||
color: ${({ theme }) => theme.primary};
|
||||
|
||||
|
|
|
@ -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};
|
||||
}
|
||||
`;
|
|
@ -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};
|
||||
}
|
||||
`;
|
|
@ -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`
|
||||
|
|
|
@ -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;
|
||||
`;
|
|
@ -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%);
|
||||
`;
|
|
@ -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} />;
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@ export const GlobalStyle = createGlobalStyle`
|
|||
font-weight: normal;
|
||||
font-size: 15px;
|
||||
line-height: 22px;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
html,
|
||||
|
|
Loading…
Reference in New Issue