Channel muting (#203)
This commit is contained in:
parent
a024960b98
commit
2519181953
|
@ -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}>
|
||||||
|
<ChannelNameWrapper>
|
||||||
<ChannelName
|
<ChannelName
|
||||||
channel={channel}
|
channel={channel}
|
||||||
active={isActive || activeView || narrow}
|
active={isActive || activeView || narrow}
|
||||||
|
activeView={activeView}
|
||||||
muted={channel?.isMuted}
|
muted={channel?.isMuted}
|
||||||
notified={notified}
|
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;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
|
@ -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`
|
||||||
|
|
|
@ -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`
|
||||||
|
|
|
@ -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};
|
||||||
|
|
|
@ -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" />
|
||||||
|
|
|
@ -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;
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -30,4 +30,9 @@ const Icon = styled.svg`
|
||||||
&.read {
|
&.read {
|
||||||
left: 62%;
|
left: 62%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.muted {
|
||||||
|
top: -8px;
|
||||||
|
transform: rotate(180deg) translateX(50%);
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue