Add mute submenu (#204)
* Add mute submenu * Change icon color * Change icons color * Add menu overlapping * Add clickoutside hook to every dropdown
This commit is contained in:
parent
bcb41c6110
commit
9cfba1f50c
|
@ -1,8 +1,9 @@
|
||||||
import React, { useEffect, useMemo, useState } from "react";
|
import React, { useEffect, useMemo, useRef, useState } from "react";
|
||||||
import styled from "styled-components";
|
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 { useClickOutside } from "../../hooks/useClickOutside";
|
||||||
import { Activity } from "../../models/Activity";
|
import { Activity } from "../../models/Activity";
|
||||||
import { equalDate } from "../../utils/equalDate";
|
import { equalDate } from "../../utils/equalDate";
|
||||||
import { DownloadButton } from "../Buttons/DownloadButton";
|
import { DownloadButton } from "../Buttons/DownloadButton";
|
||||||
|
@ -88,6 +89,9 @@ export function ActivityMessage({
|
||||||
}
|
}
|
||||||
}, [activity.message?.content]);
|
}, [activity.message?.content]);
|
||||||
|
|
||||||
|
const ref = useRef(null);
|
||||||
|
useClickOutside(ref, () => setShowMenu(false));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MessageOuterWrapper>
|
<MessageOuterWrapper>
|
||||||
<ActivityDate>
|
<ActivityDate>
|
||||||
|
@ -214,6 +218,7 @@ export function ActivityMessage({
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setShowMenu((e) => !e);
|
setShowMenu((e) => !e);
|
||||||
}}
|
}}
|
||||||
|
ref={ref}
|
||||||
>
|
>
|
||||||
{showMenu && (
|
{showMenu && (
|
||||||
<ContactMenu id={activity.user} setShowMenu={setShowMenu} />
|
<ContactMenu id={activity.user} setShowMenu={setShowMenu} />
|
||||||
|
|
|
@ -98,7 +98,7 @@ export function Channel({
|
||||||
<ChannelMenu
|
<ChannelMenu
|
||||||
channel={channel}
|
channel={channel}
|
||||||
setEditGroup={setEditGroup}
|
setEditGroup={setEditGroup}
|
||||||
className={narrow ? "narrow" : "side"}
|
className={narrow ? "sideNarrow" : "side"}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</ChannelWrapper>
|
</ChannelWrapper>
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
import { utils } from "@waku/status-communities/dist/cjs";
|
import { utils } from "@waku/status-communities/dist/cjs";
|
||||||
import { decode } from "html-entities";
|
import { decode } from "html-entities";
|
||||||
import React, { useEffect, useMemo, useState } from "react";
|
import React, { useEffect, useMemo, useRef, useState } from "react";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
|
|
||||||
import { useFetchMetadata } from "../../contexts/fetchMetadataProvider";
|
import { useFetchMetadata } from "../../contexts/fetchMetadataProvider";
|
||||||
import { useIdentity } from "../../contexts/identityProvider";
|
import { useIdentity } from "../../contexts/identityProvider";
|
||||||
import { useMessengerContext } from "../../contexts/messengerProvider";
|
import { useMessengerContext } from "../../contexts/messengerProvider";
|
||||||
|
import { useClickOutside } from "../../hooks/useClickOutside";
|
||||||
import { ChatMessage } from "../../models/ChatMessage";
|
import { ChatMessage } from "../../models/ChatMessage";
|
||||||
import { Metadata } from "../../models/Metadata";
|
import { Metadata } from "../../models/Metadata";
|
||||||
import { ContactMenu } from "../Form/ContactMenu";
|
import { ContactMenu } from "../Form/ContactMenu";
|
||||||
|
@ -32,8 +33,15 @@ export function Mention({ id, setMentioned, className }: MentionProps) {
|
||||||
}
|
}
|
||||||
}, [contact.id, identity]);
|
}, [contact.id, identity]);
|
||||||
|
|
||||||
|
const ref = useRef(null);
|
||||||
|
useClickOutside(ref, () => setShowMenu(false));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MentionBLock onClick={() => setShowMenu(!showMenu)} className={className}>
|
<MentionBLock
|
||||||
|
onClick={() => setShowMenu(!showMenu)}
|
||||||
|
className={className}
|
||||||
|
ref={ref}
|
||||||
|
>
|
||||||
{`@${contact?.customName ?? contact.trueName}`}
|
{`@${contact?.customName ?? contact.trueName}`}
|
||||||
{showMenu && <ContactMenu id={id.slice(1)} setShowMenu={setShowMenu} />}
|
{showMenu && <ContactMenu id={id.slice(1)} setShowMenu={setShowMenu} />}
|
||||||
</MentionBLock>
|
</MentionBLock>
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import React, { useState } from "react";
|
import React, { useRef, useState } from "react";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
|
|
||||||
import { useMessengerContext } from "../../contexts/messengerProvider";
|
import { useMessengerContext } from "../../contexts/messengerProvider";
|
||||||
import { useNarrow } from "../../contexts/narrowProvider";
|
import { useNarrow } from "../../contexts/narrowProvider";
|
||||||
|
import { useClickOutside } from "../../hooks/useClickOutside";
|
||||||
import {
|
import {
|
||||||
ActivityButton,
|
ActivityButton,
|
||||||
ActivityWrapper,
|
ActivityWrapper,
|
||||||
|
@ -68,6 +69,9 @@ export function ChatTopbar({
|
||||||
const narrow = useNarrow();
|
const narrow = useNarrow();
|
||||||
const [showChannelMenu, setShowChannelMenu] = useState(false);
|
const [showChannelMenu, setShowChannelMenu] = useState(false);
|
||||||
|
|
||||||
|
const ref = useRef(null);
|
||||||
|
useClickOutside(ref, () => setShowChannelMenu(false));
|
||||||
|
|
||||||
if (!activeChannel) {
|
if (!activeChannel) {
|
||||||
return <ChatTopbarLoading />;
|
return <ChatTopbarLoading />;
|
||||||
}
|
}
|
||||||
|
@ -105,12 +109,9 @@ export function ChatTopbar({
|
||||||
<MembersIcon />
|
<MembersIcon />
|
||||||
</TopBtn>
|
</TopBtn>
|
||||||
)}
|
)}
|
||||||
|
<div ref={ref}>
|
||||||
<TopBtn onClick={() => setShowChannelMenu(!showChannelMenu)}>
|
<TopBtn onClick={() => setShowChannelMenu(!showChannelMenu)}>
|
||||||
<MoreIcon />
|
<MoreIcon />
|
||||||
</TopBtn>
|
|
||||||
{!narrow && <ActivityButton />}
|
|
||||||
</MenuWrapper>
|
|
||||||
{loadingMessenger && <Loading />}
|
|
||||||
{showChannelMenu && (
|
{showChannelMenu && (
|
||||||
<ChannelMenu
|
<ChannelMenu
|
||||||
channel={activeChannel}
|
channel={activeChannel}
|
||||||
|
@ -118,8 +119,14 @@ export function ChatTopbar({
|
||||||
switchMemberList={() => switchShowState(ChatBodyState.Members)}
|
switchMemberList={() => switchShowState(ChatBodyState.Members)}
|
||||||
setShowChannelMenu={setShowChannelMenu}
|
setShowChannelMenu={setShowChannelMenu}
|
||||||
setEditGroup={setEditGroup}
|
setEditGroup={setEditGroup}
|
||||||
|
className={`${narrow && "narrow"}`}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
</TopBtn>
|
||||||
|
</div>
|
||||||
|
{!narrow && <ActivityButton />}
|
||||||
|
</MenuWrapper>
|
||||||
|
{loadingMessenger && <Loading />}
|
||||||
</Topbar>
|
</Topbar>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -186,6 +193,7 @@ export const TopBtn = styled.button`
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
background: ${({ theme }) => theme.bodyBackgroundColor};
|
background: ${({ theme }) => theme.bodyBackgroundColor};
|
||||||
|
position: relative;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: ${({ theme }) => theme.inputColor};
|
background: ${({ theme }) => theme.inputColor};
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useMemo } from "react";
|
import React, { useEffect, useMemo, useState } from "react";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
|
|
||||||
import { useMessengerContext } from "../../contexts/messengerProvider";
|
import { useMessengerContext } from "../../contexts/messengerProvider";
|
||||||
|
@ -14,12 +14,14 @@ import { EditIcon } from "../Icons/EditIcon";
|
||||||
import { LeftIcon } from "../Icons/LeftIcon";
|
import { LeftIcon } from "../Icons/LeftIcon";
|
||||||
import { MembersSmallIcon } from "../Icons/MembersSmallIcon";
|
import { MembersSmallIcon } from "../Icons/MembersSmallIcon";
|
||||||
import { MuteIcon } from "../Icons/MuteIcon";
|
import { MuteIcon } from "../Icons/MuteIcon";
|
||||||
|
import { NextIcon } from "../Icons/NextIcon";
|
||||||
import { ProfileIcon } from "../Icons/ProfileIcon";
|
import { ProfileIcon } from "../Icons/ProfileIcon";
|
||||||
import { EditModalName } from "../Modals/EditModal";
|
import { EditModalName } from "../Modals/EditModal";
|
||||||
import { LeavingModalName } from "../Modals/LeavingModal";
|
import { LeavingModalName } from "../Modals/LeavingModal";
|
||||||
import { ProfileModalName } from "../Modals/ProfileModal";
|
import { ProfileModalName } from "../Modals/ProfileModal";
|
||||||
|
|
||||||
import { DropdownMenu, MenuItem, MenuText } from "./DropdownMenu";
|
import { DropdownMenu, MenuItem, MenuSection, MenuText } from "./DropdownMenu";
|
||||||
|
import { MuteMenu } from "./MuteMenu";
|
||||||
|
|
||||||
interface ChannelMenuProps {
|
interface ChannelMenuProps {
|
||||||
channel: ChannelData;
|
channel: ChannelData;
|
||||||
|
@ -43,6 +45,15 @@ export const ChannelMenu = ({
|
||||||
const { setModal } = useModal(EditModalName);
|
const { setModal } = useModal(EditModalName);
|
||||||
const { setModal: setLeavingModal } = useModal(LeavingModalName);
|
const { setModal: setLeavingModal } = useModal(LeavingModalName);
|
||||||
const { setModal: setProfileModal } = useModal(ProfileModalName);
|
const { setModal: setProfileModal } = useModal(ProfileModalName);
|
||||||
|
const [showSubmenu, setShowSubmenu] = useState(false);
|
||||||
|
|
||||||
|
const muting = channel.isMuted;
|
||||||
|
const [isMuted, setIsMuted] = useState(muting);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isMuted) channel.isMuted = true;
|
||||||
|
if (!isMuted) channel.isMuted = false;
|
||||||
|
}, [isMuted]);
|
||||||
|
|
||||||
const { showMenu, setShowMenu: setShowSideMenu } = useContextMenu(
|
const { showMenu, setShowMenu: setShowSideMenu } = useContextMenu(
|
||||||
channel.id + "contextMenu"
|
channel.id + "contextMenu"
|
||||||
|
@ -55,7 +66,7 @@ export const ChannelMenu = ({
|
||||||
|
|
||||||
if (showMenu || setShowChannelMenu) {
|
if (showMenu || setShowChannelMenu) {
|
||||||
return (
|
return (
|
||||||
<DropdownMenu closeMenu={setShowMenu} className={className}>
|
<ChannelDropdown className={className}>
|
||||||
{narrow && !className && (
|
{narrow && !className && (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
@ -102,15 +113,27 @@ export const ChannelMenu = ({
|
||||||
<MenuSection className={`${channel.type === "channel" && "channel"}`}>
|
<MenuSection className={`${channel.type === "channel" && "channel"}`}>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
channel.isMuted = !channel.isMuted;
|
if (isMuted) {
|
||||||
|
setIsMuted(false);
|
||||||
setShowMenu(false);
|
setShowMenu(false);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onMouseEnter={() => {
|
||||||
|
if (!isMuted) setShowSubmenu(true);
|
||||||
|
}}
|
||||||
|
onMouseLeave={() => {
|
||||||
|
if (!isMuted) setShowSubmenu(false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<MuteIcon width={16} height={16} />
|
<MuteIcon width={16} height={16} />
|
||||||
|
{!isMuted && <NextIcon />}
|
||||||
<MenuText>
|
<MenuText>
|
||||||
{(channel.isMuted ? "Unmute" : "Mute") +
|
{(isMuted ? "Unmute" : "Mute") +
|
||||||
(channel.type === "group" ? " Group" : " Chat")}
|
(channel.type === "group" ? " Group" : " Chat")}
|
||||||
</MenuText>
|
</MenuText>
|
||||||
|
{!isMuted && showSubmenu && (
|
||||||
|
<MuteMenu setIsMuted={setIsMuted} className={className} />
|
||||||
|
)}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem onClick={() => clearNotifications(channel.id)}>
|
<MenuItem onClick={() => clearNotifications(channel.id)}>
|
||||||
<CheckIcon width={16} height={16} />
|
<CheckIcon width={16} height={16} />
|
||||||
|
@ -139,22 +162,25 @@ export const ChannelMenu = ({
|
||||||
</MenuText>
|
</MenuText>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
)}
|
)}
|
||||||
</DropdownMenu>
|
</ChannelDropdown>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const MenuSection = styled.div`
|
const ChannelDropdown = styled(DropdownMenu)`
|
||||||
padding: 4px 0;
|
top: calc(100% + 4px);
|
||||||
margin: 4px 0;
|
right: 0px;
|
||||||
border-top: 1px solid ${({ theme }) => theme.inputColor};
|
|
||||||
border-bottom: 1px solid ${({ theme }) => theme.inputColor};
|
|
||||||
|
|
||||||
&.channel {
|
&.side {
|
||||||
padding: 0;
|
top: 20px;
|
||||||
margin: 0;
|
left: calc(100% - 35px);
|
||||||
border: none;
|
right: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.sideNarrow {
|
||||||
|
top: 20px;
|
||||||
|
right: 8px;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -5,7 +5,7 @@ import styled from "styled-components";
|
||||||
import { useIdentity } from "../../contexts/identityProvider";
|
import { useIdentity } from "../../contexts/identityProvider";
|
||||||
import { useModal } from "../../contexts/modalProvider";
|
import { useModal } from "../../contexts/modalProvider";
|
||||||
import { useManageContact } from "../../hooks/useManageContact";
|
import { useManageContact } from "../../hooks/useManageContact";
|
||||||
import { AddContactSvg } from "../Icons/AddContactIcon";
|
import { AddContactIcon } from "../Icons/AddContactIcon";
|
||||||
import { BlockSvg } from "../Icons/BlockIcon";
|
import { BlockSvg } from "../Icons/BlockIcon";
|
||||||
import { ChatSvg } from "../Icons/ChatIcon";
|
import { ChatSvg } from "../Icons/ChatIcon";
|
||||||
import { EditIcon } from "../Icons/EditIcon";
|
import { EditIcon } from "../Icons/EditIcon";
|
||||||
|
@ -39,7 +39,7 @@ export function ContactMenu({ id, setShowMenu }: ContactMenuProps) {
|
||||||
|
|
||||||
if (!contact) return null;
|
if (!contact) return null;
|
||||||
return (
|
return (
|
||||||
<ContactDropdown closeMenu={setShowMenu}>
|
<ContactDropdown>
|
||||||
<ContactInfo>
|
<ContactInfo>
|
||||||
<UserIcon />
|
<UserIcon />
|
||||||
<UserNameWrapper>
|
<UserNameWrapper>
|
||||||
|
@ -68,7 +68,7 @@ export function ContactMenu({ id, setShowMenu }: ContactMenuProps) {
|
||||||
setModal({ id, requestState: true });
|
setModal({ id, requestState: true });
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<AddContactSvg width={16} height={16} />
|
<AddContactIcon width={16} height={16} />
|
||||||
<MenuText>Send Contact Request</MenuText>
|
<MenuText>Send Contact Request</MenuText>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -1,30 +1,38 @@
|
||||||
import React, { ReactNode, useRef } from "react";
|
import React, { ReactNode } from "react";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
|
|
||||||
import { useClickOutside } from "../../hooks/useClickOutside";
|
|
||||||
import { textSmallStyles } from "../Text";
|
import { textSmallStyles } from "../Text";
|
||||||
|
|
||||||
type DropdownMenuProps = {
|
type DropdownMenuProps = {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
className?: string;
|
className?: string;
|
||||||
closeMenu: (val: boolean) => void;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export function DropdownMenu({
|
export function DropdownMenu({ children, className }: DropdownMenuProps) {
|
||||||
children,
|
|
||||||
className,
|
|
||||||
closeMenu,
|
|
||||||
}: DropdownMenuProps) {
|
|
||||||
const ref = useRef(null);
|
|
||||||
useClickOutside(ref, () => closeMenu(false));
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MenuBlock className={className} ref={ref}>
|
<MenuBlock className={className}>
|
||||||
<MenuList>{children}</MenuList>
|
<MenuList>{children}</MenuList>
|
||||||
</MenuBlock>
|
</MenuBlock>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const MenuBlock = styled.div`
|
||||||
|
width: 207px;
|
||||||
|
background: ${({ theme }) => theme.bodyBackgroundColor};
|
||||||
|
box-shadow: 0px 2px 4px rgba(0, 34, 51, 0.16),
|
||||||
|
0px 4px 12px rgba(0, 34, 51, 0.08);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 8px 0;
|
||||||
|
position: absolute;
|
||||||
|
top: calc(100% - 8px);
|
||||||
|
right: 8px;
|
||||||
|
z-index: 2;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const MenuList = styled.ul`
|
||||||
|
list-style: none;
|
||||||
|
`;
|
||||||
|
|
||||||
export const MenuItem = styled.li`
|
export const MenuItem = styled.li`
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -32,16 +40,13 @@ export const MenuItem = styled.li`
|
||||||
padding: 8px 8px 8px 14px;
|
padding: 8px 8px 8px 14px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: ${({ theme }) => theme.primary};
|
color: ${({ theme }) => theme.primary};
|
||||||
|
position: relative;
|
||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
&:hover > span {
|
&:hover > span {
|
||||||
background: ${({ theme }) => theme.border};
|
background: ${({ theme }) => theme.border};
|
||||||
}
|
}
|
||||||
|
|
||||||
& > svg {
|
|
||||||
fill: ${({ theme }) => theme.tertiary};
|
|
||||||
}
|
|
||||||
|
|
||||||
& > svg.red {
|
& > svg.red {
|
||||||
fill: ${({ theme }) => theme.redColor};
|
fill: ${({ theme }) => theme.redColor};
|
||||||
}
|
}
|
||||||
|
@ -58,29 +63,15 @@ export const MenuText = styled.span`
|
||||||
${textSmallStyles}
|
${textSmallStyles}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const MenuBlock = styled.div`
|
export const MenuSection = styled.div`
|
||||||
width: 207px;
|
padding: 4px 0;
|
||||||
background: ${({ theme }) => theme.bodyBackgroundColor};
|
margin: 4px 0;
|
||||||
box-shadow: 0px 2px 4px rgba(0, 34, 51, 0.16),
|
border-top: 1px solid ${({ theme }) => theme.inputColor};
|
||||||
0px 4px 12px rgba(0, 34, 51, 0.08);
|
border-bottom: 1px solid ${({ theme }) => theme.inputColor};
|
||||||
border-radius: 8px;
|
|
||||||
padding: 8px 0;
|
|
||||||
position: absolute;
|
|
||||||
right: 8px;
|
|
||||||
top: calc(100% - 8px);
|
|
||||||
z-index: 2;
|
|
||||||
|
|
||||||
&.side {
|
&.channel {
|
||||||
top: 20px;
|
padding: 0;
|
||||||
right: -90px;
|
margin: 0;
|
||||||
}
|
border: none;
|
||||||
|
|
||||||
&.narrow {
|
|
||||||
top: 20px;
|
|
||||||
right: 0;
|
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const MenuList = styled.ul`
|
|
||||||
list-style: none;
|
|
||||||
`;
|
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import React from "react";
|
import React, { useRef } from "react";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
|
|
||||||
|
import { useClickOutside } from "../../hooks/useClickOutside";
|
||||||
import { useContextMenu } from "../../hooks/useContextMenu";
|
import { useContextMenu } from "../../hooks/useContextMenu";
|
||||||
import { copyImg } from "../../utils/copyImg";
|
import { copyImg } from "../../utils/copyImg";
|
||||||
import { downloadImg } from "../../utils/downloadImg";
|
import { downloadImg } from "../../utils/downloadImg";
|
||||||
import { CopySvg } from "../Icons/CopyIcon";
|
import { CopyIcon } from "../Icons/CopyIcon";
|
||||||
import { DownloadIcon } from "../Icons/DownloadIcon";
|
import { DownloadIcon } from "../Icons/DownloadIcon";
|
||||||
|
|
||||||
import { DropdownMenu, MenuItem, MenuText } from "./DropdownMenu";
|
import { DropdownMenu, MenuItem, MenuText } from "./DropdownMenu";
|
||||||
|
@ -16,16 +17,21 @@ interface ImageMenuProps {
|
||||||
export const ImageMenu = ({ imageId }: ImageMenuProps) => {
|
export const ImageMenu = ({ imageId }: ImageMenuProps) => {
|
||||||
const { showMenu, setShowMenu } = useContextMenu(imageId);
|
const { showMenu, setShowMenu } = useContextMenu(imageId);
|
||||||
|
|
||||||
|
const ref = useRef(null);
|
||||||
|
useClickOutside(ref, () => setShowMenu(false));
|
||||||
|
|
||||||
return showMenu ? (
|
return showMenu ? (
|
||||||
<ImageDropdown closeMenu={setShowMenu}>
|
<div ref={ref}>
|
||||||
|
<ImageDropdown>
|
||||||
<MenuItem onClick={() => copyImg(imageId)}>
|
<MenuItem onClick={() => copyImg(imageId)}>
|
||||||
<CopySvg height={16} width={16} /> <MenuText>Copy image</MenuText>
|
<CopyIcon height={16} width={16} /> <MenuText>Copy image</MenuText>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem onClick={() => downloadImg(imageId)}>
|
<MenuItem onClick={() => downloadImg(imageId)}>
|
||||||
<DownloadIcon height={16} width={16} />
|
<DownloadIcon height={16} width={16} />
|
||||||
<MenuText> Download image</MenuText>
|
<MenuText> Download image</MenuText>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</ImageDropdown>
|
</ImageDropdown>
|
||||||
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<></>
|
<></>
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
import React, { useCallback } from "react";
|
||||||
|
import styled from "styled-components";
|
||||||
|
|
||||||
|
import { DropdownMenu, MenuItem, MenuText } from "./DropdownMenu";
|
||||||
|
|
||||||
|
interface SubMenuProps {
|
||||||
|
setIsMuted: (val: boolean) => void;
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MuteMenu = ({ setIsMuted, className }: SubMenuProps) => {
|
||||||
|
const muteChannel = useCallback(
|
||||||
|
(timeout: number) => {
|
||||||
|
setIsMuted(true);
|
||||||
|
const timer = setTimeout(() => setIsMuted(false), timeout * 6000000);
|
||||||
|
return () => {
|
||||||
|
clearTimeout(timer);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
[setIsMuted]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MuteDropdown className={className}>
|
||||||
|
<MenuItem onClick={() => muteChannel(0.25)}>
|
||||||
|
<MenuText>For 15 min</MenuText>
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem onClick={() => muteChannel(1)}>
|
||||||
|
<MenuText>For 1 hour</MenuText>
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem onClick={() => muteChannel(8)}>
|
||||||
|
<MenuText>For 8 hours</MenuText>
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem onClick={() => muteChannel(24)}>
|
||||||
|
<MenuText>For 24 hours</MenuText>
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem onClick={() => setIsMuted(true)}>
|
||||||
|
<MenuText>Until I turn it back on</MenuText>
|
||||||
|
</MenuItem>
|
||||||
|
</MuteDropdown>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const MuteDropdown = styled(DropdownMenu)`
|
||||||
|
width: 176px;
|
||||||
|
top: 100%;
|
||||||
|
right: -60px;
|
||||||
|
z-index: 3;
|
||||||
|
|
||||||
|
&.side {
|
||||||
|
width: 176px;
|
||||||
|
top: -8px;
|
||||||
|
left: 100%;
|
||||||
|
right: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.narrow,
|
||||||
|
&.sideNarrow {
|
||||||
|
width: 176px;
|
||||||
|
top: 100%;
|
||||||
|
right: -16px;
|
||||||
|
z-index: 3;
|
||||||
|
}
|
||||||
|
`;
|
|
@ -1,19 +1,19 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
|
|
||||||
type AddContactSvgProps = {
|
type AddContactIconProps = {
|
||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
className?: string;
|
className?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function AddContactSvg({
|
export function AddContactIcon({
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
className,
|
className,
|
||||||
}: AddContactSvgProps) {
|
}: AddContactIconProps) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<Icon
|
||||||
width={width}
|
width={width}
|
||||||
height={height}
|
height={height}
|
||||||
viewBox="0 0 16 16"
|
viewBox="0 0 16 16"
|
||||||
|
@ -28,20 +28,10 @@ export function AddContactSvg({
|
||||||
/>
|
/>
|
||||||
<path d="M8.07357 11.6671C8.35289 11.6707 8.60016 11.4677 8.62336 11.1893C8.64601 10.9175 8.44624 10.6758 8.17357 10.6689C8.11597 10.6674 8.05818 10.6667 8.00022 10.6667C5.97321 10.6667 4.15749 11.5713 2.93477 12.9989C2.77487 13.1856 2.79579 13.4622 2.96961 13.636C3.18548 13.8519 3.54282 13.8208 3.74449 13.5916C4.78296 12.4114 6.30462 11.6667 8.00022 11.6667C8.02471 11.6667 8.04916 11.6668 8.07357 11.6671Z" />
|
<path d="M8.07357 11.6671C8.35289 11.6707 8.60016 11.4677 8.62336 11.1893C8.64601 10.9175 8.44624 10.6758 8.17357 10.6689C8.11597 10.6674 8.05818 10.6667 8.00022 10.6667C5.97321 10.6667 4.15749 11.5713 2.93477 12.9989C2.77487 13.1856 2.79579 13.4622 2.96961 13.636C3.18548 13.8519 3.54282 13.8208 3.74449 13.5916C4.78296 12.4114 6.30462 11.6667 8.00022 11.6667C8.02471 11.6667 8.04916 11.6668 8.07357 11.6671Z" />
|
||||||
<path d="M10.167 11.1667C10.167 10.8905 10.3908 10.6667 10.667 10.6667H11.5003C11.6844 10.6667 11.8337 10.5174 11.8337 10.3333V9.5C11.8337 9.22386 12.0575 9 12.3337 9C12.6098 9 12.8337 9.22386 12.8337 9.5V10.3333C12.8337 10.5174 12.9829 10.6667 13.167 10.6667H14.0003C14.2765 10.6667 14.5003 10.8905 14.5003 11.1667C14.5003 11.4428 14.2765 11.6667 14.0003 11.6667H13.167C12.9829 11.6667 12.8337 11.8159 12.8337 12V12.8333C12.8337 13.1095 12.6098 13.3333 12.3337 13.3333C12.0575 13.3333 11.8337 13.1095 11.8337 12.8333V12C11.8337 11.8159 11.6844 11.6667 11.5003 11.6667H10.667C10.3908 11.6667 10.167 11.4428 10.167 11.1667Z" />
|
<path d="M10.167 11.1667C10.167 10.8905 10.3908 10.6667 10.667 10.6667H11.5003C11.6844 10.6667 11.8337 10.5174 11.8337 10.3333V9.5C11.8337 9.22386 12.0575 9 12.3337 9C12.6098 9 12.8337 9.22386 12.8337 9.5V10.3333C12.8337 10.5174 12.9829 10.6667 13.167 10.6667H14.0003C14.2765 10.6667 14.5003 10.8905 14.5003 11.1667C14.5003 11.4428 14.2765 11.6667 14.0003 11.6667H13.167C12.9829 11.6667 12.8337 11.8159 12.8337 12V12.8333C12.8337 13.1095 12.6098 13.3333 12.3337 13.3333C12.0575 13.3333 11.8337 13.1095 11.8337 12.8333V12C11.8337 11.8159 11.6844 11.6667 11.5003 11.6667H10.667C10.3908 11.6667 10.167 11.4428 10.167 11.1667Z" />
|
||||||
</svg>
|
</Icon>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AddContactIcon = () => {
|
const Icon = styled.svg`
|
||||||
return <Icon width={16} height={16} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
const Icon = styled(AddContactSvg)`
|
|
||||||
& > path {
|
|
||||||
fill: ${({ theme }) => theme.tertiary};
|
fill: ${({ theme }) => theme.tertiary};
|
||||||
}
|
|
||||||
|
|
||||||
&:hover > path {
|
|
||||||
fill: ${({ theme }) => theme.bodyBackgroundColor};
|
|
||||||
}
|
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
|
|
||||||
type CopySvgProps = {
|
type CopyIconProps = {
|
||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
className?: string;
|
className?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function CopySvg({ width, height, className }: CopySvgProps) {
|
export function CopyIcon({ width, height, className }: CopyIconProps) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<Icon
|
||||||
width={width}
|
width={width}
|
||||||
height={height}
|
height={height}
|
||||||
viewBox="0 0 16 16"
|
viewBox="0 0 16 16"
|
||||||
|
@ -22,16 +22,10 @@ export function CopySvg({ width, height, className }: CopySvgProps) {
|
||||||
clipRule="evenodd"
|
clipRule="evenodd"
|
||||||
d="M6.00016 4.00065C6.00016 2.52789 7.19407 1.33398 8.66683 1.33398H12.0002C13.4729 1.33398 14.6668 2.52789 14.6668 4.00065V7.33398C14.6668 8.80674 13.4729 10.0007 12.0002 10.0007H8.66683C7.19407 10.0007 6.00016 8.80674 6.00016 7.33398V4.00065ZM8.66683 2.33398H12.0002C12.9206 2.33398 13.6668 3.08018 13.6668 4.00065V7.33398C13.6668 8.25446 12.9206 9.00065 12.0002 9.00065H8.66683C7.74636 9.00065 7.00016 8.25446 7.00016 7.33398V4.00065C7.00016 3.08018 7.74636 2.33398 8.66683 2.33398Z"
|
d="M6.00016 4.00065C6.00016 2.52789 7.19407 1.33398 8.66683 1.33398H12.0002C13.4729 1.33398 14.6668 2.52789 14.6668 4.00065V7.33398C14.6668 8.80674 13.4729 10.0007 12.0002 10.0007H8.66683C7.19407 10.0007 6.00016 8.80674 6.00016 7.33398V4.00065ZM8.66683 2.33398H12.0002C12.9206 2.33398 13.6668 3.08018 13.6668 4.00065V7.33398C13.6668 8.25446 12.9206 9.00065 12.0002 9.00065H8.66683C7.74636 9.00065 7.00016 8.25446 7.00016 7.33398V4.00065C7.00016 3.08018 7.74636 2.33398 8.66683 2.33398Z"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</Icon>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CopyIcon = () => {
|
const Icon = styled.svg`
|
||||||
return <Icon width={16} height={16} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
const Icon = styled(CopySvg)`
|
|
||||||
& > path {
|
|
||||||
fill: ${({ theme }) => theme.tertiary};
|
fill: ${({ theme }) => theme.tertiary};
|
||||||
}
|
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -32,5 +32,5 @@ export function MembersSmallIcon({
|
||||||
}
|
}
|
||||||
|
|
||||||
const Icon = styled.svg`
|
const Icon = styled.svg`
|
||||||
fill: ${({ theme }) => theme.primary};
|
fill: ${({ theme }) => theme.tertiary};
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
import React from "react";
|
||||||
|
import styled from "styled-components";
|
||||||
|
|
||||||
|
export const NextIcon = () => {
|
||||||
|
return (
|
||||||
|
<Icon
|
||||||
|
width="16"
|
||||||
|
height="16"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M9.46967 5.46967C9.76256 5.17678 10.2374 5.17678 10.5303 5.46967L16.1768 11.1161C16.6649 11.6043 16.6649 12.3957 16.1768 12.8839L10.5303 18.5303C10.2374 18.8232 9.76256 18.8232 9.46967 18.5303C9.17678 18.2374 9.17678 17.7626 9.46967 17.4697L14.5858 12.3536C14.781 12.1583 14.781 11.8417 14.5858 11.6464L9.46967 6.53033C9.17678 6.23744 9.17678 5.76256 9.46967 5.46967Z"
|
||||||
|
/>
|
||||||
|
</Icon>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Icon = styled.svg`
|
||||||
|
fill: ${({ theme }) => theme.primary};
|
||||||
|
position: absolute;
|
||||||
|
right: 8px;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
`;
|
|
@ -1,7 +1,8 @@
|
||||||
import React, { useState } from "react";
|
import React, { useRef, useState } from "react";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
|
|
||||||
import { useIdentity } from "../../contexts/identityProvider";
|
import { useIdentity } from "../../contexts/identityProvider";
|
||||||
|
import { useClickOutside } from "../../hooks/useClickOutside";
|
||||||
import { Contact } from "../../models/Contact";
|
import { Contact } from "../../models/Contact";
|
||||||
import { ContactMenu } from "../Form/ContactMenu";
|
import { ContactMenu } from "../Form/ContactMenu";
|
||||||
import { IconBtn, UserAddress } from "../Messages/Styles";
|
import { IconBtn, UserAddress } from "../Messages/Styles";
|
||||||
|
@ -20,6 +21,9 @@ export function Member({ contact, isOnline, isYou, onClick }: MemberProps) {
|
||||||
|
|
||||||
const [showMenu, setShowMenu] = useState(false);
|
const [showMenu, setShowMenu] = useState(false);
|
||||||
|
|
||||||
|
const ref = useRef(null);
|
||||||
|
useClickOutside(ref, () => setShowMenu(false));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MemberData onClick={onClick} className={`${isYou && "you"}`}>
|
<MemberData onClick={onClick} className={`${isYou && "you"}`}>
|
||||||
<MemberIcon
|
<MemberIcon
|
||||||
|
@ -32,6 +36,7 @@ export function Member({ contact, isOnline, isYou, onClick }: MemberProps) {
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (identity) setShowMenu((e) => !e);
|
if (identity) setShowMenu((e) => !e);
|
||||||
}}
|
}}
|
||||||
|
ref={ref}
|
||||||
>
|
>
|
||||||
{showMenu && <ContactMenu id={contact.id} setShowMenu={setShowMenu} />}
|
{showMenu && <ContactMenu id={contact.id} setShowMenu={setShowMenu} />}
|
||||||
<UserLogo
|
<UserLogo
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
import { utils } from "@waku/status-communities/dist/cjs";
|
import { utils } from "@waku/status-communities/dist/cjs";
|
||||||
import { BaseEmoji } from "emoji-mart";
|
import { BaseEmoji } from "emoji-mart";
|
||||||
import React, { useEffect, useMemo, useState } from "react";
|
import React, { useEffect, useMemo, useRef, useState } from "react";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
|
|
||||||
import { useActivities } from "../../contexts/activityProvider";
|
import { useActivities } from "../../contexts/activityProvider";
|
||||||
import { useIdentity } from "../../contexts/identityProvider";
|
import { useIdentity } from "../../contexts/identityProvider";
|
||||||
import { useMessengerContext } from "../../contexts/messengerProvider";
|
import { useMessengerContext } from "../../contexts/messengerProvider";
|
||||||
|
import { useClickOutside } from "../../hooks/useClickOutside";
|
||||||
import { Reply } from "../../hooks/useReply";
|
import { Reply } from "../../hooks/useReply";
|
||||||
import { ChannelData } from "../../models/ChannelData";
|
import { ChannelData } from "../../models/ChannelData";
|
||||||
import { ChatMessage } from "../../models/ChatMessage";
|
import { ChatMessage } from "../../models/ChatMessage";
|
||||||
|
@ -100,6 +101,9 @@ export function UiMessage({
|
||||||
]);
|
]);
|
||||||
}, [mentioned, message, quote]);
|
}, [mentioned, message, quote]);
|
||||||
|
|
||||||
|
const ref = useRef(null);
|
||||||
|
useClickOutside(ref, () => setShowMenu(false));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MessageOuterWrapper>
|
<MessageOuterWrapper>
|
||||||
{(idx === 0 || !equalDate(prevMessage.date, message.date)) && (
|
{(idx === 0 || !equalDate(prevMessage.date, message.date)) && (
|
||||||
|
@ -117,6 +121,7 @@ export function UiMessage({
|
||||||
if (identity) setShowMenu((e) => !e);
|
if (identity) setShowMenu((e) => !e);
|
||||||
}}
|
}}
|
||||||
disabled={!identity}
|
disabled={!identity}
|
||||||
|
ref={ref}
|
||||||
>
|
>
|
||||||
{showMenu && (
|
{showMenu && (
|
||||||
<ContactMenu id={message.sender} setShowMenu={setShowMenu} />
|
<ContactMenu id={message.sender} setShowMenu={setShowMenu} />
|
||||||
|
@ -139,6 +144,7 @@ export function UiMessage({
|
||||||
if (identity) setShowMenu((e) => !e);
|
if (identity) setShowMenu((e) => !e);
|
||||||
}}
|
}}
|
||||||
disabled={!identity}
|
disabled={!identity}
|
||||||
|
ref={ref}
|
||||||
>
|
>
|
||||||
<UserName>
|
<UserName>
|
||||||
{" "}
|
{" "}
|
||||||
|
|
|
@ -16,7 +16,7 @@ import {
|
||||||
NameInputWrapper,
|
NameInputWrapper,
|
||||||
} from "../Form/inputStyles";
|
} from "../Form/inputStyles";
|
||||||
import { ClearSvgFull } from "../Icons/ClearIconFull";
|
import { ClearSvgFull } from "../Icons/ClearIconFull";
|
||||||
import { CopySvg } from "../Icons/CopyIcon";
|
import { CopyIcon } from "../Icons/CopyIcon";
|
||||||
import { EditIcon } from "../Icons/EditIcon";
|
import { EditIcon } from "../Icons/EditIcon";
|
||||||
import { LeftIcon } from "../Icons/LeftIcon";
|
import { LeftIcon } from "../Icons/LeftIcon";
|
||||||
import { UntrustworthIcon } from "../Icons/UntrustworthIcon";
|
import { UntrustworthIcon } from "../Icons/UntrustworthIcon";
|
||||||
|
@ -151,7 +151,7 @@ export const ProfileModal = () => {
|
||||||
</UserAddress>
|
</UserAddress>
|
||||||
|
|
||||||
<CopyButton onClick={() => copy(id)}>
|
<CopyButton onClick={() => copy(id)}>
|
||||||
<CopySvg width={24} height={24} />
|
<CopyIcon width={24} height={24} />
|
||||||
</CopyButton>
|
</CopyButton>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
Loading…
Reference in New Issue