Channel muting (#203)

This commit is contained in:
Maria Rushkova 2022-01-26 10:32:04 +01:00 committed by GitHub
parent a024960b98
commit 2519181953
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 89 additions and 36 deletions

View File

@ -5,6 +5,7 @@ import { useMessengerContext } from "../../contexts/messengerProvider";
import { useNarrow } from "../../contexts/narrowProvider"; import { useNarrow } from "../../contexts/narrowProvider";
import { ChannelData } from "../../models/ChannelData"; import { ChannelData } from "../../models/ChannelData";
import { ChannelMenu } from "../Form/ChannelMenu"; import { ChannelMenu } from "../Form/ChannelMenu";
import { Tooltip } from "../Form/Tooltip";
import { GroupIcon } from "../Icons/GroupIcon"; import { GroupIcon } from "../Icons/GroupIcon";
import { MutedIcon } from "../Icons/MutedIcon"; import { MutedIcon } from "../Icons/MutedIcon";
import { textMediumStyles } from "../Text"; import { textMediumStyles } from "../Text";
@ -13,9 +14,11 @@ import { ChannelIcon } from "./ChannelIcon";
function RenderChannelName({ function RenderChannelName({
channel, channel,
activeView,
className, className,
}: { }: {
channel: ChannelData; channel: ChannelData;
activeView?: boolean;
className?: string; className?: string;
}) { }) {
const { activeChannel } = useMessengerContext(); const { activeChannel } = useMessengerContext();
@ -23,14 +26,16 @@ function RenderChannelName({
case "group": case "group":
return ( return (
<div className={className}> <div className={className}>
<GroupIcon activeView={channel.id === activeChannel?.id} /> {!activeView && (
<GroupIcon active={channel.id === activeChannel?.id} />
)}
{` ${channel.name}`} {` ${channel.name}`}
</div> </div>
); );
case "channel": case "channel":
return <div className={className}>{`# ${channel.name}`}</div>; return <div className={className}>{`# ${channel.name}`}</div>;
case "dm": case "dm":
return <div className={className}>{channel.name}</div>; return <div className={className}>{channel.name.slice(0, 20)}</div>;
} }
} }
@ -62,15 +67,24 @@ export function Channel({
onClick={onClick} onClick={onClick}
id={!activeView ? `${channel.id + "contextMenu"}` : ""} id={!activeView ? `${channel.id + "contextMenu"}` : ""}
> >
<ChannelInfo> <ChannelInfo activeView={activeView}>
<ChannelIcon channel={channel} activeView={activeView} /> <ChannelIcon channel={channel} activeView={activeView} />
<ChannelTextInfo> <ChannelTextInfo activeView={activeView && !narrow}>
<ChannelName <ChannelNameWrapper>
channel={channel} <ChannelName
active={isActive || activeView || narrow} channel={channel}
muted={channel?.isMuted} active={isActive || activeView || narrow}
notified={notified} activeView={activeView}
/> muted={channel?.isMuted}
notified={notified}
/>
{channel?.isMuted && activeView && !narrow && (
<MutedBtn onClick={() => (channel.isMuted = !channel.isMuted)}>
<MutedIcon />
<Tooltip tip="Unmute" className="muted" />
</MutedBtn>
)}
</ChannelNameWrapper>
{activeView && ( {activeView && (
<ChannelDescription>{channel.description}</ChannelDescription> <ChannelDescription>{channel.description}</ChannelDescription>
)} )}
@ -107,34 +121,43 @@ const ChannelWrapper = styled.div<{ isNarrow?: boolean }>`
} }
&:hover { &:hover {
background-color: ${({ theme }) => theme.border}; background-color: ${({ theme, isNarrow }) => isNarrow && theme.border};
} }
`; `;
export const ChannelInfo = styled.div` export const ChannelInfo = styled.div<{ activeView?: boolean }>`
display: flex; display: flex;
align-items: center; align-items: ${({ activeView }) => (activeView ? "flex-start" : "center")};
overflow: hidden; overflow-x: hidden;
`; `;
const ChannelTextInfo = styled.div` const ChannelTextInfo = styled.div<{ activeView?: boolean }>`
display: flex; display: flex;
flex-direction: column; flex-direction: column;
text-overflow: ellipsis; text-overflow: ellipsis;
overflow: hidden; overflow-x: hidden;
white-space: nowrap; white-space: nowrap;
padding: ${({ activeView }) => activeView && "0 24px 24px 0"};
`;
const ChannelNameWrapper = styled.div`
display: flex;
align-items: center;
`; `;
export const ChannelName = styled(RenderChannelName)<{ export const ChannelName = styled(RenderChannelName)<{
muted?: boolean; muted?: boolean;
notified?: boolean; notified?: boolean;
active?: boolean; active?: boolean;
activeView?: boolean;
}>` }>`
font-weight: ${({ notified, muted, active }) => font-weight: ${({ notified, muted, active }) =>
notified && !muted && !active ? "600" : "500"}; notified && !muted && !active ? "600" : "500"};
opacity: ${({ notified, muted, active }) => opacity: ${({ notified, muted, active }) =>
muted ? "0.4" : notified || active ? "1.0" : "0.7"}; muted ? "0.4" : notified || active ? "1.0" : "0.7"};
color: ${({ theme }) => theme.primary}; color: ${({ theme }) => theme.primary};
margin-right: ${({ muted, activeView }) =>
muted && activeView ? "8px" : ""};
text-overflow: ellipsis; text-overflow: ellipsis;
overflow: hidden; overflow: hidden;
white-space: nowrap; white-space: nowrap;
@ -166,3 +189,18 @@ const NotificationBagde = styled.div`
justify-content: center; justify-content: center;
flex-shrink: 0; flex-shrink: 0;
`; `;
const MutedBtn = styled.button`
padding: 0;
border: none;
outline: none;
position: relative;
&:hover > svg {
fill-opacity: 1;
}
&:hover > div {
visibility: visible;
}
`;

View File

@ -4,6 +4,7 @@ import styled from "styled-components";
import { useIdentity } from "../../contexts/identityProvider"; import { useIdentity } from "../../contexts/identityProvider";
import { useMessengerContext } from "../../contexts/messengerProvider"; import { useMessengerContext } from "../../contexts/messengerProvider";
import { useNarrow } from "../../contexts/narrowProvider";
import { ChannelData } from "../../models/ChannelData"; import { ChannelData } from "../../models/ChannelData";
import { textMediumStyles } from "../Text"; import { textMediumStyles } from "../Text";
@ -64,8 +65,10 @@ type EmptyChannelProps = {
}; };
export function EmptyChannel({ channel }: EmptyChannelProps) { export function EmptyChannel({ channel }: EmptyChannelProps) {
const narrow = useNarrow();
return ( return (
<Wrapper> <Wrapper className={`${!narrow && "wide"}`}>
<ChannelInfoEmpty> <ChannelInfoEmpty>
<ChannelLogoEmpty icon={channel.icon}> <ChannelLogoEmpty icon={channel.icon}>
{" "} {" "}
@ -83,6 +86,10 @@ const Wrapper = styled.div`
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
margin-bottom: 32px; margin-bottom: 32px;
&.wide {
margin-top: 24px;
}
`; `;
const ChannelInfoEmpty = styled(ChannelInfo)` const ChannelInfoEmpty = styled(ChannelInfo)`
@ -103,9 +110,6 @@ const ChannelNameEmpty = styled(ChannelName)`
font-size: 22px; font-size: 22px;
line-height: 30px; line-height: 30px;
margin-bottom: 16px; margin-bottom: 16px;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
`; `;
const EmptyText = styled.p` const EmptyText = styled.p`

View File

@ -127,7 +127,7 @@ export function ChatTopbar({
const Topbar = styled.div` const Topbar = styled.div`
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: flex-start;
padding: 5px 8px; padding: 5px 8px;
background: ${({ theme }) => theme.bodyBackgroundColor}; background: ${({ theme }) => theme.bodyBackgroundColor};
position: relative; position: relative;
@ -177,6 +177,7 @@ const CommunityWrap = styled.div`
const MenuWrapper = styled.div` const MenuWrapper = styled.div`
display: flex; display: flex;
align-items: center; align-items: center;
padding: 8px 0;
`; `;
export const TopBtn = styled.button` export const TopBtn = styled.button`

View File

@ -31,6 +31,11 @@ const TooltipWrapper = styled.div`
&.read { &.read {
left: 18%; left: 18%;
} }
&.muted {
top: calc(100% + 8px);
z-index: 10;
}
`; `;
const TooltipBlock = styled.div` const TooltipBlock = styled.div`
background: ${({ theme }) => theme.primary}; background: ${({ theme }) => theme.primary};

View File

@ -2,10 +2,10 @@ import React from "react";
import styled from "styled-components"; import styled from "styled-components";
interface GroupIconProps { interface GroupIconProps {
activeView?: boolean; active?: boolean;
} }
export const GroupIcon = ({ activeView }: GroupIconProps) => { export const GroupIcon = ({ active }: GroupIconProps) => {
return ( return (
<Icon <Icon
width="14" width="14"
@ -13,7 +13,7 @@ export const GroupIcon = ({ activeView }: GroupIconProps) => {
viewBox="0 0 14 10" viewBox="0 0 14 10"
fill="none" fill="none"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
className={`${activeView && "active"}`} className={`${active && "active"}`}
> >
<path d="M7 4.5C8.24265 4.5 9.25 3.49264 9.25 2.25C9.25 1.00736 8.24265 0 7 0C5.75736 0 4.75 1.00736 4.75 2.25C4.75 3.49264 5.75736 4.5 7 4.5Z" /> <path d="M7 4.5C8.24265 4.5 9.25 3.49264 9.25 2.25C9.25 1.00736 8.24265 0 7 0C5.75736 0 4.75 1.00736 4.75 2.25C4.75 3.49264 5.75736 4.5 7 4.5Z" />
<path d="M3.12343 9.01012C3.56395 7.27976 5.13252 6 7 6C8.86749 6 10.4361 7.27976 10.8766 9.01012C11.0128 9.54533 10.5523 10 10 10H4C3.44772 10 2.98718 9.54533 3.12343 9.01012Z" /> <path d="M3.12343 9.01012C3.56395 7.27976 5.13252 6 7 6C8.86749 6 10.4361 7.27976 10.8766 9.01012C11.0128 9.54533 10.5523 10 10 10H4C3.44772 10 2.98718 9.54533 3.12343 9.01012Z" />

View File

@ -9,20 +9,14 @@ export const MutedIcon = () => {
viewBox="0 0 14 14" viewBox="0 0 14 14"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
> >
<path <path d="M8.70186 2.11736C8.91545 1.90377 8.89277 1.54794 8.62625 1.40578C8.13934 1.14607 7.58479 0.999998 7.00002 0.999998C5.27863 0.999998 3.8192 2.26576 3.57576 3.96984L3.14144 7.01005C3.11619 7.18684 3.43245 7.38677 3.55873 7.26049L8.70186 2.11736Z" />
d="M8.70186 2.11736C8.91545 1.90377 8.89277 1.54794 8.62625 1.40578C8.13934 1.14607 7.58479 0.999998 7.00002 0.999998C5.27863 0.999998 3.8192 2.26576 3.57576 3.96984L3.14144 7.01005C3.11619 7.18684 3.43245 7.38677 3.55873 7.26049L8.70186 2.11736Z" <path d="M0.952001 11.9879C0.676901 12.282 0.68279 12.7434 0.969669 13.0303C1.26256 13.3232 1.73744 13.3232 2.03033 13.0303L3.91421 11.1464C4.00798 11.0527 4.13516 11 4.26777 11H4.5C4.77614 11 4.99359 11.2273 5.06171 11.4949C5.28197 12.3601 6.06626 13 7 13C7.93374 13 8.71803 12.3601 8.93829 11.4949C9.00641 11.2273 9.22386 11 9.5 11H11.5858C12.4767 11 12.9229 9.92286 12.2929 9.29289L11.7071 8.7071C11.2481 8.24811 10.9504 7.65264 10.8586 7.01005L10.543 4.80054C10.5207 4.64475 10.5731 4.48756 10.6844 4.37628L13.0303 2.03033C13.3232 1.73744 13.3232 1.26256 13.0303 0.969668C12.7435 0.682807 12.282 0.676903 11.988 0.951949C11.9822 0.958112 11.9763 0.964201 11.9703 0.970214L0.970328 11.9702C0.964295 11.9762 0.958185 11.9822 0.952001 11.9879Z" />
fillOpacity="0.2"
/>
<path
d="M0.952001 11.9879C0.676901 12.282 0.68279 12.7434 0.969669 13.0303C1.26256 13.3232 1.73744 13.3232 2.03033 13.0303L3.91421 11.1464C4.00798 11.0527 4.13516 11 4.26777 11H4.5C4.77614 11 4.99359 11.2273 5.06171 11.4949C5.28197 12.3601 6.06626 13 7 13C7.93374 13 8.71803 12.3601 8.93829 11.4949C9.00641 11.2273 9.22386 11 9.5 11H11.5858C12.4767 11 12.9229 9.92286 12.2929 9.29289L11.7071 8.7071C11.2481 8.24811 10.9504 7.65264 10.8586 7.01005L10.543 4.80054C10.5207 4.64475 10.5731 4.48756 10.6844 4.37628L13.0303 2.03033C13.3232 1.73744 13.3232 1.26256 13.0303 0.969668C12.7435 0.682807 12.282 0.676903 11.988 0.951949C11.9822 0.958112 11.9763 0.964201 11.9703 0.970214L0.970328 11.9702C0.964295 11.9762 0.958185 11.9822 0.952001 11.9879Z"
fillOpacity="0.2"
/>
</Icon> </Icon>
); );
}; };
const Icon = styled.svg` const Icon = styled.svg`
& > path { fill: ${({ theme }) => theme.primary};
fill: ${({ theme }) => theme.primary}; fill-opacity: 0.2;
} flex-shrink: 0;
`; `;

View File

@ -30,4 +30,9 @@ const Icon = styled.svg`
&.read { &.read {
left: 62%; left: 62%;
} }
&.muted {
top: -8px;
transform: rotate(180deg) translateX(50%);
}
`; `;

View File

@ -3,6 +3,7 @@ import styled from "styled-components";
import { useMessengerContext } from "../../contexts/messengerProvider"; import { useMessengerContext } from "../../contexts/messengerProvider";
import { useModal } from "../../contexts/modalProvider"; import { useModal } from "../../contexts/modalProvider";
import { useNarrow } from "../../contexts/narrowProvider";
import { useChatScrollHandle } from "../../hooks/useChatScrollHandle"; import { useChatScrollHandle } from "../../hooks/useChatScrollHandle";
import { Reply } from "../../hooks/useReply"; import { Reply } from "../../hooks/useReply";
import { ChannelData } from "../../models/ChannelData"; import { ChannelData } from "../../models/ChannelData";
@ -19,6 +20,7 @@ interface MessagesListProps {
} }
export function MessagesList({ setReply, channel }: MessagesListProps) { export function MessagesList({ setReply, channel }: MessagesListProps) {
const narrow = useNarrow();
const { messages, contacts } = useMessengerContext(); const { messages, contacts } = useMessengerContext();
const ref = useRef<HTMLHeadingElement>(null); const ref = useRef<HTMLHeadingElement>(null);
const loadingMessages = useChatScrollHandle(messages, ref); const loadingMessages = useChatScrollHandle(messages, ref);
@ -49,7 +51,7 @@ export function MessagesList({ setReply, channel }: MessagesListProps) {
useEffect(() => (!showLinkModal ? setLink("") : undefined), [showLinkModal]); useEffect(() => (!showLinkModal ? setLink("") : undefined), [showLinkModal]);
return ( return (
<MessagesWrapper ref={ref}> <MessagesWrapper ref={ref} className={`${!narrow && "wide"}`}>
<PictureModal image={image} /> <PictureModal image={image} />
<LinkModal link={link} /> <LinkModal link={link} />
<EmptyChannel channel={channel} /> <EmptyChannel channel={channel} />
@ -91,6 +93,10 @@ const MessagesWrapper = styled.div`
overflow: auto; overflow: auto;
padding: 8px 0; padding: 8px 0;
&.wide {
margin-top: -24px;
}
&::-webkit-scrollbar { &::-webkit-scrollbar {
width: 0; width: 0;
} }