Add group menu (#107)
This commit is contained in:
parent
ac77a0ada2
commit
2714e8e9dd
|
@ -66,7 +66,7 @@ export function Channel({
|
|||
) : (
|
||||
"#"
|
||||
)}{" "}
|
||||
{channel.name}
|
||||
{channel.name.slice(0, 10)}
|
||||
</ChannelName>
|
||||
{activeView && (
|
||||
<ChannelDescription> {channel.description}</ChannelDescription>
|
||||
|
|
|
@ -12,6 +12,7 @@ interface ChannelsProps {
|
|||
activeChannelId: string;
|
||||
channels: ChannelData[];
|
||||
membersList: string[];
|
||||
groupList: string[];
|
||||
setCreateChat: (val: boolean) => void;
|
||||
}
|
||||
|
||||
|
@ -20,6 +21,7 @@ export function Channels({
|
|||
activeChannelId,
|
||||
channels,
|
||||
membersList,
|
||||
groupList,
|
||||
setCreateChat,
|
||||
}: ChannelsProps) {
|
||||
const { clearNotifications, notifications } = useMessengerContext();
|
||||
|
@ -61,6 +63,28 @@ export function Channels({
|
|||
</EditBtn>
|
||||
</ChatsBar>
|
||||
<ChatsList>
|
||||
{groupList.length > 0 &&
|
||||
groupList.map((group) => (
|
||||
<Channel
|
||||
key={group}
|
||||
channel={{
|
||||
id: group,
|
||||
name: group,
|
||||
type: "group",
|
||||
description: "Contact",
|
||||
}}
|
||||
isActive={group === activeChannelId}
|
||||
isMuted={false}
|
||||
onClick={() => {
|
||||
onCommunityClick({
|
||||
id: group,
|
||||
name: group.slice(0, 10),
|
||||
description: "Contact",
|
||||
});
|
||||
setCreateChat(false);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
{membersList.length > 0 &&
|
||||
membersList.map((member) => (
|
||||
<Channel
|
||||
|
@ -68,7 +92,7 @@ export function Channels({
|
|||
channel={{
|
||||
id: member,
|
||||
name: member,
|
||||
type: "group",
|
||||
type: "dm",
|
||||
description: "Contact",
|
||||
}}
|
||||
isActive={member === activeChannelId}
|
||||
|
|
|
@ -22,13 +22,15 @@ export function EmptyChannel({ channel }: EmptyChannelProps) {
|
|||
{" "}
|
||||
{!channel.icon && channel.name.slice(0, 1).toUpperCase()}
|
||||
</ChannelLogoEmpty>
|
||||
<ChannelNameEmpty className={"active"}>{channel.name}</ChannelNameEmpty>
|
||||
<ChannelNameEmpty className={"active"}>
|
||||
{channel.name.slice(0, 10)}
|
||||
</ChannelNameEmpty>
|
||||
</ChannelInfoEmpty>
|
||||
|
||||
{channel.type === "dm" ? (
|
||||
<EmptyText>
|
||||
Any messages you send here are encrypted and can only be read by you
|
||||
and <span>{channel.name}</span>.
|
||||
and <span>{channel.name.slice(0, 10)}</span>.
|
||||
</EmptyText>
|
||||
) : channel.type === "group" ? (
|
||||
<EmptyText>
|
||||
|
@ -70,6 +72,9 @@ const ChannelNameEmpty = styled(ChannelName)`
|
|||
font-size: 22px;
|
||||
line-height: 30px;
|
||||
margin-bottom: 16px;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
`;
|
||||
|
||||
const EmptyText = styled.p`
|
||||
|
@ -77,6 +82,7 @@ const EmptyText = styled.p`
|
|||
color: ${({ theme }) => theme.secondary};
|
||||
max-width: 310px;
|
||||
text-align: center;
|
||||
word-break: break-all;
|
||||
|
||||
& > span {
|
||||
color: ${({ theme }) => theme.primary};
|
||||
|
|
|
@ -13,6 +13,7 @@ import { ChatCreation } from "./Chat/ChatCreation";
|
|||
import { Community } from "./Community";
|
||||
import { Members } from "./Members/Members";
|
||||
import { CommunityModal } from "./Modals/CommunityModal";
|
||||
import { EditModal } from "./Modals/EditModal";
|
||||
import { CommunitySkeleton } from "./Skeleton/CommunitySkeleton";
|
||||
|
||||
interface ChatProps {
|
||||
|
@ -31,6 +32,7 @@ export function Chat({
|
|||
const [showMembers, setShowMembers] = useState(true);
|
||||
const [showChannels, setShowChannels] = useState(true);
|
||||
const [membersList, setMembersList] = useState([]);
|
||||
const [groupList, setGroupList] = useState([]);
|
||||
const [createChat, setCreateChat] = useState(false);
|
||||
|
||||
const narrow = useNarrow();
|
||||
|
@ -38,6 +40,9 @@ export function Chat({
|
|||
const [isModalVisible, setIsModalVisible] = useState(false);
|
||||
const showModal = () => setIsModalVisible(true);
|
||||
|
||||
const [isEditVisible, setIsEditVisible] = useState(false);
|
||||
const showEditModal = () => setIsEditVisible(true);
|
||||
|
||||
const { community } = useMessengerContext();
|
||||
|
||||
const communityData = useMemo(() => {
|
||||
|
@ -90,6 +95,7 @@ export function Chat({
|
|||
activeChannelId={activeChannel?.id ?? ""}
|
||||
channels={channels}
|
||||
membersList={membersList}
|
||||
groupList={groupList}
|
||||
setCreateChat={setCreateChat}
|
||||
/>
|
||||
</ChannelsWrapper>
|
||||
|
@ -106,9 +112,12 @@ export function Chat({
|
|||
community={communityData}
|
||||
showCommunity={!showChannels}
|
||||
onCommunityClick={showModal}
|
||||
onEditClick={showEditModal}
|
||||
channels={channels}
|
||||
membersList={membersList}
|
||||
groupList={groupList}
|
||||
setMembersList={setMembersList}
|
||||
setGroupList={setGroupList}
|
||||
setCreateChat={setCreateChat}
|
||||
/>
|
||||
)}
|
||||
|
@ -123,6 +132,7 @@ export function Chat({
|
|||
<ChatCreation
|
||||
community={communityData}
|
||||
setMembersList={setMembersList}
|
||||
setGroupList={setGroupList}
|
||||
setActiveChannel={setActiveChannel}
|
||||
setCreateChat={setCreateChat}
|
||||
/>
|
||||
|
@ -138,6 +148,11 @@ export function Chat({
|
|||
publicKey={communityKey}
|
||||
/>
|
||||
)}
|
||||
<EditModal
|
||||
isVisible={isEditVisible}
|
||||
onClose={() => setIsEditVisible(false)}
|
||||
channel={activeChannel}
|
||||
/>
|
||||
</ChatWrapper>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ import { CommunitySkeleton } from "../Skeleton/CommunitySkeleton";
|
|||
import { Loading } from "../Skeleton/Loading";
|
||||
import { LoadingSkeleton } from "../Skeleton/LoadingSkeleton";
|
||||
|
||||
import { ChatCreation } from "./ChatCreation";
|
||||
import { ChatInput } from "./ChatInput";
|
||||
import { ChatMessages } from "./ChatMessages";
|
||||
|
||||
|
@ -31,9 +32,12 @@ interface ChatBodyProps {
|
|||
setActiveChannel: (val: ChannelData) => void;
|
||||
activeChannelId: string;
|
||||
onCommunityClick: () => void;
|
||||
onEditClick: () => void;
|
||||
channels: ChannelData[];
|
||||
membersList: string[];
|
||||
groupList: string[];
|
||||
setMembersList: any;
|
||||
setGroupList: any;
|
||||
setCreateChat: (val: boolean) => void;
|
||||
}
|
||||
|
||||
|
@ -47,9 +51,12 @@ export function ChatBody({
|
|||
setActiveChannel,
|
||||
activeChannelId,
|
||||
onCommunityClick,
|
||||
onEditClick,
|
||||
channels,
|
||||
membersList,
|
||||
groupList,
|
||||
setMembersList,
|
||||
setGroupList,
|
||||
setCreateChat,
|
||||
}: ChatBodyProps) {
|
||||
const { messenger, messages } = useMessengerContext();
|
||||
|
@ -57,6 +64,7 @@ export function ChatBody({
|
|||
const [showChannelsList, setShowChannelsList] = useState(false);
|
||||
const [showMembersList, setShowMembersList] = useState(false);
|
||||
const [showChannelMenu, setShowChannelMenu] = useState(false);
|
||||
const [editGroup, setEditGroup] = useState(false);
|
||||
const className = useMemo(() => (narrow ? "narrow" : ""), [narrow]);
|
||||
|
||||
const switchChannelList = useCallback(() => {
|
||||
|
@ -78,55 +86,71 @@ export function ChatBody({
|
|||
|
||||
return (
|
||||
<ChatBodyWrapper className={className}>
|
||||
<ChatTopbar
|
||||
className={
|
||||
narrow && (showChannelsList || showMembersList) ? "narrow" : ""
|
||||
}
|
||||
>
|
||||
<ChannelWrapper className={className}>
|
||||
{messenger && community ? (
|
||||
<>
|
||||
{(showCommunity || narrow) && (
|
||||
<CommunityWrap className={className}>
|
||||
<Community onClick={onCommunityClick} community={community} />
|
||||
</CommunityWrap>
|
||||
)}
|
||||
<Channel
|
||||
channel={channel}
|
||||
isActive={narrow ? showChannelsList : true}
|
||||
activeView={true}
|
||||
isMuted={false}
|
||||
onClick={() => (narrow ? switchChannelList() : undefined)}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<CommunitySkeleton />
|
||||
)}
|
||||
</ChannelWrapper>
|
||||
{editGroup && community ? (
|
||||
<ChatCreation
|
||||
community={community}
|
||||
setMembersList={setMembersList}
|
||||
setGroupList={setGroupList}
|
||||
setActiveChannel={setActiveChannel}
|
||||
setCreateChat={setCreateChat}
|
||||
editGroup={editGroup}
|
||||
/>
|
||||
) : (
|
||||
<ChatTopbar
|
||||
className={
|
||||
narrow && (showChannelsList || showMembersList) ? "narrow" : ""
|
||||
}
|
||||
>
|
||||
<ChannelWrapper className={className}>
|
||||
{messenger && community ? (
|
||||
<>
|
||||
{(showCommunity || narrow) && (
|
||||
<CommunityWrap className={className}>
|
||||
<Community
|
||||
onClick={onCommunityClick}
|
||||
community={community}
|
||||
/>
|
||||
</CommunityWrap>
|
||||
)}
|
||||
<Channel
|
||||
channel={channel}
|
||||
isActive={narrow ? showChannelsList : true}
|
||||
activeView={true}
|
||||
isMuted={false}
|
||||
onClick={() => (narrow ? switchChannelList() : undefined)}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<CommunitySkeleton />
|
||||
)}
|
||||
</ChannelWrapper>
|
||||
|
||||
<MenuWrapper>
|
||||
{!narrow && (
|
||||
<MemberBtn
|
||||
onClick={onClick}
|
||||
className={showMembers && !narrow ? "active" : ""}
|
||||
>
|
||||
<MembersIcon />
|
||||
</MemberBtn>
|
||||
<MenuWrapper>
|
||||
{!narrow && (
|
||||
<MemberBtn
|
||||
onClick={onClick}
|
||||
className={showMembers && !narrow ? "active" : ""}
|
||||
>
|
||||
<MembersIcon />
|
||||
</MemberBtn>
|
||||
)}
|
||||
<MoreBtn onClick={() => setShowChannelMenu(!showChannelMenu)}>
|
||||
<MoreIcon />
|
||||
</MoreBtn>
|
||||
</MenuWrapper>
|
||||
{!community && <Loading />}
|
||||
{showChannelMenu && (
|
||||
<ChannelMenu
|
||||
channel={channel}
|
||||
messages={messages}
|
||||
switchMemberList={switchMemberList}
|
||||
setShowChannelMenu={setShowChannelMenu}
|
||||
setEditGroup={setEditGroup}
|
||||
onEditClick={onEditClick}
|
||||
/>
|
||||
)}
|
||||
<MoreBtn onClick={() => setShowChannelMenu(!showChannelMenu)}>
|
||||
<MoreIcon />
|
||||
</MoreBtn>
|
||||
</MenuWrapper>
|
||||
{!community && <Loading />}
|
||||
{showChannelMenu && (
|
||||
<ChannelMenu
|
||||
channel={channel}
|
||||
messages={messages}
|
||||
switchMemberList={switchMemberList}
|
||||
setShowChannelMenu={setShowChannelMenu}
|
||||
/>
|
||||
)}
|
||||
</ChatTopbar>
|
||||
</ChatTopbar>
|
||||
)}
|
||||
{messenger && community ? (
|
||||
<>
|
||||
{!showChannelsList && !showMembersList && (
|
||||
|
@ -155,6 +179,7 @@ export function ChatBody({
|
|||
setShowChannels={setShowChannelsList}
|
||||
activeChannelId={activeChannelId}
|
||||
membersList={membersList}
|
||||
groupList={groupList}
|
||||
setCreateChat={setCreateChat}
|
||||
/>
|
||||
)}
|
||||
|
|
|
@ -11,15 +11,19 @@ import { textMediumStyles } from "../Text";
|
|||
interface ChatCreationProps {
|
||||
community: CommunityData;
|
||||
setMembersList: any;
|
||||
setGroupList: any;
|
||||
setActiveChannel: (val: ChannelData) => void;
|
||||
setCreateChat: (val: boolean) => void;
|
||||
editGroup?: boolean;
|
||||
}
|
||||
|
||||
export function ChatCreation({
|
||||
community,
|
||||
setMembersList,
|
||||
setGroupList,
|
||||
setActiveChannel,
|
||||
setCreateChat,
|
||||
editGroup,
|
||||
}: ChatCreationProps) {
|
||||
const [query, setQuery] = useState("");
|
||||
const [styledGroup, setStyledGroup] = useState<string[]>([]);
|
||||
|
@ -40,12 +44,27 @@ export function ChatCreation({
|
|||
};
|
||||
|
||||
const createChat = (group: string[]) => {
|
||||
setMembersList(group);
|
||||
setActiveChannel({
|
||||
id: group.join(""),
|
||||
name: group.join(", "),
|
||||
type: "dm",
|
||||
});
|
||||
group.length > 1
|
||||
? (setGroupList((prevGroups: string[]) => {
|
||||
[...prevGroups, group];
|
||||
}),
|
||||
setActiveChannel({
|
||||
id: group.join(""),
|
||||
name: group.join(", "),
|
||||
type: "group",
|
||||
}))
|
||||
: (setMembersList((prevMembers: string[]) => {
|
||||
if (prevMembers.find((chat) => chat === group[0])) {
|
||||
return prevMembers;
|
||||
} else {
|
||||
return [...prevMembers, group[0]];
|
||||
}
|
||||
}),
|
||||
setActiveChannel({
|
||||
id: group[0],
|
||||
name: group[0],
|
||||
type: "dm",
|
||||
}));
|
||||
setCreateChat(false);
|
||||
};
|
||||
|
||||
|
@ -90,12 +109,14 @@ export function ChatCreation({
|
|||
</InputBar>
|
||||
<CreationBtn
|
||||
disabled={styledGroup.length === 0}
|
||||
onClick={() => createChat(styledGroup)}
|
||||
onClick={() =>
|
||||
editGroup ? setMembersList(styledGroup) : createChat(styledGroup)
|
||||
}
|
||||
>
|
||||
Confirm
|
||||
</CreationBtn>
|
||||
</CreationBar>
|
||||
{!query && styledGroup.length === 0 && (
|
||||
{!editGroup && !query && styledGroup.length === 0 && (
|
||||
<Contacts>
|
||||
<ContactsHeading>Contacts</ContactsHeading>
|
||||
<ContactsList>
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
import { useMessengerContext } from "../../contexts/messengerProvider";
|
||||
import { useNarrow } from "../../contexts/narrowProvider";
|
||||
import { ChannelData } from "../../models/ChannelData";
|
||||
import { ChatMessage } from "../../models/ChatMessage";
|
||||
import { AddMemberIconSvg } from "../Icons/AddMemberIcon";
|
||||
import { CheckSvg } from "../Icons/CheckIcon";
|
||||
import { ClearSvg } from "../Icons/ClearIcon";
|
||||
import { EgitGroupSvg } from "../Icons/EditGroupIcon";
|
||||
import { LeftIconSvg } from "../Icons/LeftIcon";
|
||||
import { MembersSmallSvg } from "../Icons/MembersSmallIcon";
|
||||
import { MuteSvg } from "../Icons/MuteIcon";
|
||||
|
||||
|
@ -16,6 +20,8 @@ interface ChannelMenuProps {
|
|||
messages: ChatMessage[];
|
||||
switchMemberList: () => void;
|
||||
setShowChannelMenu: (val: boolean) => void;
|
||||
setEditGroup: (val: boolean) => void;
|
||||
onEditClick: () => void;
|
||||
}
|
||||
|
||||
export const ChannelMenu = ({
|
||||
|
@ -23,6 +29,8 @@ export const ChannelMenu = ({
|
|||
messages,
|
||||
switchMemberList,
|
||||
setShowChannelMenu,
|
||||
setEditGroup,
|
||||
onEditClick,
|
||||
}: ChannelMenuProps) => {
|
||||
const narrow = useNarrow();
|
||||
const { clearNotifications } = useMessengerContext();
|
||||
|
@ -40,23 +48,50 @@ export const ChannelMenu = ({
|
|||
<MenuText>View Members</MenuText>
|
||||
</MenuItem>
|
||||
)}
|
||||
{channel.type === "group" && (
|
||||
<>
|
||||
<MenuItem onClick={() => setEditGroup(true)}>
|
||||
<AddMemberIconSvg width={16} height={16} />
|
||||
<MenuText>Add / remove from group</MenuText>
|
||||
</MenuItem>
|
||||
<MenuItem onClick={onEditClick}>
|
||||
<EgitGroupSvg width={16} height={16} />
|
||||
<MenuText>Edit name and image</MenuText>
|
||||
</MenuItem>
|
||||
</>
|
||||
)}
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
channel.isMuted = true;
|
||||
setShowChannelMenu(false);
|
||||
}}
|
||||
>
|
||||
<MuteSvg height={16} width={16} />
|
||||
<MuteSvg width={16} height={16} />
|
||||
<MenuText>Mute Chat</MenuText>
|
||||
</MenuItem>
|
||||
<MenuItem onClick={() => clearNotifications(channel.id)}>
|
||||
<CheckSvg height={16} width={16} />
|
||||
<CheckSvg width={16} height={16} />
|
||||
<MenuText>Mark as Read</MenuText>
|
||||
</MenuItem>
|
||||
<MenuItem onClick={() => messages.length === 0}>
|
||||
<ClearSvg height={16} width={16} />
|
||||
<ClearSvg width={16} height={16} />
|
||||
<MenuText>Clear History</MenuText>
|
||||
</MenuItem>
|
||||
{channel.type === "group" && (
|
||||
<MenuSection>
|
||||
{" "}
|
||||
<MenuItem onClick={() => channel}>
|
||||
<LeftIconSvg width={16} height={16} />
|
||||
<MenuText>Leave Group</MenuText>
|
||||
</MenuItem>
|
||||
</MenuSection>
|
||||
)}
|
||||
</DropdownMenu>
|
||||
);
|
||||
};
|
||||
|
||||
const MenuSection = styled.div`
|
||||
margin-top: 5px;
|
||||
padding-top: 5px;
|
||||
border-top: 1px solid ${({ theme }) => theme.inputColor};
|
||||
`;
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
export const AddIcon = () => {
|
||||
return (
|
||||
<Icon
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path d="M12 5.25C12.4142 5.25 12.75 5.58579 12.75 6V10.75C12.75 11.0261 12.9739 11.25 13.25 11.25H18C18.4142 11.25 18.75 11.5858 18.75 12C18.75 12.4142 18.4142 12.75 18 12.75H13.25C12.9739 12.75 12.75 12.9739 12.75 13.25V18C12.75 18.4142 12.4142 18.75 12 18.75C11.5858 18.75 11.25 18.4142 11.25 18V13.25C11.25 12.9739 11.0261 12.75 10.75 12.75H6C5.58579 12.75 5.25 12.4142 5.25 12C5.25 11.5858 5.58579 11.25 6 11.25H10.75C11.0261 11.25 11.25 11.0261 11.25 10.75V6C11.25 5.58579 11.5858 5.25 12 5.25Z" />
|
||||
</Icon>
|
||||
);
|
||||
};
|
||||
|
||||
const Icon = styled.svg`
|
||||
& > path {
|
||||
fill: ${({ theme }) => theme.bodyBackgroundColor};
|
||||
}
|
||||
`;
|
|
@ -0,0 +1,49 @@
|
|||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
type AddMemberIconSvgProps = {
|
||||
width: number;
|
||||
height: number;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export function AddMemberIconSvg({
|
||||
width,
|
||||
height,
|
||||
className,
|
||||
}: AddMemberIconSvgProps) {
|
||||
return (
|
||||
<svg
|
||||
width={width}
|
||||
height={height}
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={className}
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M11.3327 5.33333C11.3327 7.17428 9.8403 8.66667 7.99935 8.66667C6.1584 8.66667 4.66602 7.17428 4.66602 5.33333C4.66602 3.49238 6.1584 2 7.99935 2C9.8403 2 11.3327 3.49238 11.3327 5.33333ZM10.3327 5.33333C10.3327 6.622 9.28801 7.66667 7.99935 7.66667C6.71068 7.66667 5.66602 6.622 5.66602 5.33333C5.66602 4.04467 6.71068 3 7.99935 3C9.28801 3 10.3327 4.04467 10.3327 5.33333Z"
|
||||
/>
|
||||
<path d="M4.00065 7.86634C2.96972 7.86634 2.13398 7.03061 2.13398 5.99967C2.13398 4.96874 2.96972 4.13301 4.00065 4.13301C4.31774 4.13301 4.61635 4.21207 4.87787 4.35155L5.19117 3.61285C4.83282 3.43377 4.4285 3.33301 4.00065 3.33301C2.52789 3.33301 1.33398 4.52692 1.33398 5.99967C1.33398 7.47243 2.52789 8.66634 4.00065 8.66634C4.82839 8.66634 5.56804 8.28921 6.05715 7.69741L5.42434 7.20702C5.08194 7.61038 4.57119 7.86634 4.00065 7.86634Z" />
|
||||
<path d="M12.056 7.86634C13.0869 7.86634 13.9227 7.03061 13.9227 5.99967C13.9227 4.96874 13.0869 4.13301 12.056 4.13301C11.7389 4.13301 11.4403 4.21207 11.1788 4.35155L10.8655 3.61285C11.2238 3.43377 11.6281 3.33301 12.056 3.33301C13.5287 3.33301 14.7227 4.52692 14.7227 5.99967C14.7227 7.47243 13.5287 8.66634 12.056 8.66634C11.2283 8.66634 10.4886 8.28921 9.99949 7.69741L10.6323 7.20702C10.9747 7.61038 11.4855 7.86634 12.056 7.86634Z" />
|
||||
<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" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export const AddMemberIcon = () => {
|
||||
return <Icon width={16} height={16} />;
|
||||
};
|
||||
|
||||
const Icon = styled(AddMemberIconSvg)`
|
||||
& > path {
|
||||
fill: ${({ theme }) => theme.tertiary};
|
||||
}
|
||||
|
||||
&:hover > path {
|
||||
fill: ${({ theme }) => theme.bodyBackgroundColor};
|
||||
}
|
||||
`;
|
|
@ -0,0 +1,41 @@
|
|||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
type EgitGroupSvgProps = {
|
||||
width: number;
|
||||
height: number;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export function EgitGroupSvg({ width, height, className }: EgitGroupSvgProps) {
|
||||
return (
|
||||
<svg
|
||||
width={width}
|
||||
height={height}
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={className}
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M10.7914 2.16376C11.6321 1.32302 12.9952 1.32302 13.836 2.16376C14.6767 3.00451 14.6767 4.36763 13.836 5.20838L6.0015 13.0429C5.71671 13.3276 5.36405 13.5352 4.97679 13.6458L2.1717 14.4472C1.99679 14.4972 1.80854 14.4484 1.67992 14.3198C1.55129 14.1912 1.50251 14.0029 1.55249 13.828L2.35394 11.0229C2.46459 10.6357 2.6721 10.283 2.95688 9.99823L10.7914 2.16376ZM13.1276 2.87212C12.6781 2.42258 11.9492 2.42258 11.4997 2.87212L3.66524 10.7066C3.50083 10.871 3.38103 11.0746 3.31716 11.2981C3.07579 12.1429 3.85682 12.9239 4.70159 12.6826C4.92515 12.6187 5.12874 12.4989 5.29315 12.3345L13.1276 4.50003C13.5772 4.05049 13.5772 3.32165 13.1276 2.87212Z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export const EgitGroupIcon = () => {
|
||||
return <Icon width={16} height={16} />;
|
||||
};
|
||||
|
||||
const Icon = styled(EgitGroupSvg)`
|
||||
& > path {
|
||||
fill: ${({ theme }) => theme.tertiary};
|
||||
}
|
||||
|
||||
&:hover > path {
|
||||
fill: ${({ theme }) => theme.bodyBackgroundColor};
|
||||
}
|
||||
`;
|
|
@ -0,0 +1,37 @@
|
|||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
type LeftIconSvgProps = {
|
||||
width: number;
|
||||
height: number;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export function LeftIconSvg({ width, height, className }: LeftIconSvgProps) {
|
||||
return (
|
||||
<svg
|
||||
width={width}
|
||||
height={height}
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={className}
|
||||
>
|
||||
<path d="M7.01957 4.35355C7.21483 4.15829 7.21483 3.84171 7.01957 3.64645C6.82431 3.45118 6.50772 3.45118 6.31246 3.64645L2.31246 7.64645C2.1172 7.84171 2.1172 8.15829 2.31246 8.35355L6.31246 12.3536C6.50772 12.5488 6.82431 12.5488 7.01957 12.3536C7.21483 12.1583 7.21483 11.8417 7.01957 11.6464L4.44216 9.06904C4.23217 8.85905 4.38089 8.5 4.67786 8.5H13.3327C13.6088 8.5 13.8327 8.27614 13.8327 8C13.8327 7.72386 13.6088 7.5 13.3327 7.5H4.67786C4.38089 7.5 4.23217 7.14095 4.44216 6.93096L7.01957 4.35355Z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export const LeftIconIcon = () => {
|
||||
return <Icon width={16} height={16} />;
|
||||
};
|
||||
|
||||
const Icon = styled(LeftIconSvg)`
|
||||
& > path {
|
||||
fill: ${({ theme }) => theme.tertiary};
|
||||
}
|
||||
|
||||
&:hover > path {
|
||||
fill: ${({ theme }) => theme.bodyBackgroundColor};
|
||||
}
|
||||
`;
|
|
@ -9,9 +9,10 @@ import {
|
|||
} from "../CommunityIdentity";
|
||||
import { CopyInput } from "../Form/CopyInput";
|
||||
import { StatusLogo } from "../Icons/StatusLogo";
|
||||
import { textMediumStyles, textSmallStyles } from "../Text";
|
||||
import { textSmallStyles } from "../Text";
|
||||
|
||||
import { BasicModalProps, Modal } from "./Modal";
|
||||
import { Section, Text } from "./ModalStyle";
|
||||
|
||||
interface CommunityModalProps extends BasicModalProps, CommunityIdentityProps {
|
||||
description: string;
|
||||
|
@ -55,27 +56,6 @@ export const CommunityModal = ({
|
|||
);
|
||||
};
|
||||
|
||||
const Section = styled.div`
|
||||
padding: 20px 16px;
|
||||
|
||||
& + & {
|
||||
border-top: 1px solid ${({ theme }) => theme.border};
|
||||
}
|
||||
`;
|
||||
|
||||
const Text = styled.p`
|
||||
color: ${({ theme }) => theme.primary};
|
||||
|
||||
${textMediumStyles}
|
||||
`;
|
||||
|
||||
const Hint = styled.p`
|
||||
margin-top: 16px;
|
||||
color: ${({ theme }) => theme.secondary};
|
||||
|
||||
${textSmallStyles}
|
||||
`;
|
||||
|
||||
const BottomSection = styled(Section)`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
@ -92,3 +72,10 @@ const StyledDownloadButton = styled(DownloadButton)`
|
|||
text-decoration: underline;
|
||||
color: ${({ theme }) => theme.secondary};
|
||||
`;
|
||||
|
||||
const Hint = styled.p`
|
||||
margin-top: 16px;
|
||||
color: ${({ theme }) => theme.secondary};
|
||||
|
||||
${textSmallStyles}
|
||||
`;
|
||||
|
|
|
@ -0,0 +1,185 @@
|
|||
import React, { useState } from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
import { ChannelData } from "../../models/ChannelData";
|
||||
import { uintToImgUrl } from "../../utils/uintToImgUrl";
|
||||
import { buttonStyles } from "../Buttons/buttonStyle";
|
||||
import { ChannelLogo } from "../Channels/Channel";
|
||||
import { AddIcon } from "../Icons/AddIcon";
|
||||
import { textMediumStyles } from "../Text";
|
||||
|
||||
import { BasicModalProps, Modal } from "./Modal";
|
||||
import { Heading, Section } from "./ModalStyle";
|
||||
|
||||
interface EditModalProps extends BasicModalProps {
|
||||
channel: ChannelData;
|
||||
}
|
||||
|
||||
export const EditModal = ({ isVisible, onClose, channel }: EditModalProps) => {
|
||||
const [groupName, setGroupName] = useState("");
|
||||
const [imageUint, setImageUint] = useState<undefined | Uint8Array>(undefined);
|
||||
const [showSizeLimit, setShowSizeLimit] = useState(false);
|
||||
|
||||
return (
|
||||
<Modal isVisible={isVisible} onClose={onClose}>
|
||||
<Section>
|
||||
<Heading>Edit group name and image</Heading>
|
||||
</Section>
|
||||
|
||||
<Section>
|
||||
<NameSection>
|
||||
<LabelGroup>
|
||||
<Label>Name the group</Label>
|
||||
<Hint>{groupName.length}/30</Hint>
|
||||
</LabelGroup>
|
||||
|
||||
<NameInput
|
||||
value={groupName}
|
||||
type="text"
|
||||
placeholder="A catchy name"
|
||||
maxLength={30}
|
||||
onInput={(e) => setGroupName(e.currentTarget.value)}
|
||||
/>
|
||||
</NameSection>
|
||||
<LogoSection>
|
||||
<Label>Group image</Label>
|
||||
<GroupLogo
|
||||
style={{
|
||||
backgroundImage: channel.icon
|
||||
? `url(${channel.icon}`
|
||||
: imageUint
|
||||
? uintToImgUrl(imageUint)
|
||||
: "",
|
||||
}}
|
||||
>
|
||||
{!channel.icon && channel.name.slice(0, 1).toUpperCase()}
|
||||
<AddPictureInputWrapper>
|
||||
<AddIcon />
|
||||
<AddPictureInput
|
||||
type="file"
|
||||
multiple={true}
|
||||
accept="image/png, image/jpeg"
|
||||
onChange={(e) => {
|
||||
const fileReader = new FileReader();
|
||||
fileReader.onloadend = (s) => {
|
||||
const arr = new Uint8Array(s.target?.result as ArrayBuffer);
|
||||
setImageUint(arr);
|
||||
};
|
||||
|
||||
if (e?.target?.files?.[0]) {
|
||||
if (e.target.files[0].size < 1024 * 1024) {
|
||||
fileReader.readAsArrayBuffer(e.target.files[0]);
|
||||
} else {
|
||||
setShowSizeLimit(true);
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</AddPictureInputWrapper>
|
||||
</GroupLogo>
|
||||
{showSizeLimit && <Label>File size must be less than 1MB</Label>}
|
||||
</LogoSection>
|
||||
</Section>
|
||||
<ButtonSection>
|
||||
<SaveBtn>Save changes</SaveBtn>
|
||||
</ButtonSection>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
const NameSection = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: 16px;
|
||||
`;
|
||||
|
||||
const LabelGroup = styled.div`
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
const Label = styled.p`
|
||||
color: ${({ theme }) => theme.primary};
|
||||
padding: 10px 0;
|
||||
|
||||
${textMediumStyles}
|
||||
`;
|
||||
|
||||
const Hint = styled.p`
|
||||
color: ${({ theme }) => theme.secondary};
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
`;
|
||||
|
||||
const NameInput = styled.input`
|
||||
padding: 14px 70px 14px 8px;
|
||||
background: ${({ theme }) => theme.inputColor};
|
||||
border-radius: 8px;
|
||||
border: 1px solid ${({ theme }) => theme.inputColor};
|
||||
color: ${({ theme }) => theme.primary};
|
||||
outline: none;
|
||||
|
||||
${textMediumStyles}
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
caret-color: ${({ theme }) => theme.notificationColor};
|
||||
}
|
||||
`;
|
||||
|
||||
const LogoSection = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
margin-bottom: 8px;
|
||||
`;
|
||||
|
||||
const GroupLogo = styled(ChannelLogo)`
|
||||
width: 128px;
|
||||
height: 128px;
|
||||
font-weight: bold;
|
||||
font-size: 80px;
|
||||
position: relative;
|
||||
align-self: center;
|
||||
margin-right: 0;
|
||||
`;
|
||||
|
||||
const AddPictureInputWrapper = styled.div`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 8px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background: ${({ theme }) => theme.tertiary};
|
||||
border-radius: 50%;
|
||||
`;
|
||||
|
||||
const AddPictureInput = styled.input`
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
opacity: 0;
|
||||
z-index: 2;
|
||||
cursor: pointer;
|
||||
`;
|
||||
|
||||
const ButtonSection = styled(Section)`
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
`;
|
||||
|
||||
const SaveBtn = styled.button`
|
||||
padding: 11px 24px;
|
||||
|
||||
${buttonStyles}
|
||||
|
||||
&:hover {
|
||||
background: ${({ theme }) => theme.buttonBgHover};
|
||||
}
|
||||
`;
|
|
@ -5,6 +5,7 @@ import { buttonStyles } from "../Buttons/buttonStyle";
|
|||
import { textMediumStyles } from "../Text";
|
||||
|
||||
import { BasicModalProps, Modal } from "./Modal";
|
||||
import { Heading, Section } from "./ModalStyle";
|
||||
|
||||
interface LinkModalProps extends BasicModalProps {
|
||||
link: string;
|
||||
|
@ -14,7 +15,7 @@ export const LinkModal = ({ isVisible, onClose, link }: LinkModalProps) => {
|
|||
return (
|
||||
<Modal isVisible={isVisible} onClose={onClose}>
|
||||
<Section>
|
||||
<Question>Are you sure you want to visit this website?</Question>
|
||||
<Heading>Are you sure you want to visit this website?</Heading>
|
||||
</Section>
|
||||
<Section>
|
||||
<Link>{link}</Link>
|
||||
|
@ -34,21 +35,6 @@ export const LinkModal = ({ isVisible, onClose, link }: LinkModalProps) => {
|
|||
);
|
||||
};
|
||||
|
||||
const Section = styled.div`
|
||||
padding: 16px;
|
||||
|
||||
& + & {
|
||||
border-top: 1px solid ${({ theme }) => theme.border};
|
||||
}
|
||||
`;
|
||||
|
||||
const Question = styled.p`
|
||||
color: ${({ theme }) => theme.primary};
|
||||
font-weight: bold;
|
||||
font-size: 17px;
|
||||
line-height: 24px;
|
||||
`;
|
||||
|
||||
const Link = styled.a`
|
||||
text-decoration: none;
|
||||
word-break: break-all;
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
import styled from "styled-components";
|
||||
|
||||
import { textMediumStyles } from "../Text";
|
||||
|
||||
export const Section = styled.div`
|
||||
padding: 16px;
|
||||
|
||||
& + & {
|
||||
border-top: 1px solid ${({ theme }) => theme.border};
|
||||
}
|
||||
`;
|
||||
|
||||
export const Heading = styled.p`
|
||||
color: ${({ theme }) => theme.primary};
|
||||
font-weight: bold;
|
||||
font-size: 17px;
|
||||
line-height: 24px;
|
||||
`;
|
||||
|
||||
export const Text = styled.p`
|
||||
color: ${({ theme }) => theme.primary};
|
||||
|
||||
${textMediumStyles}
|
||||
`;
|
|
@ -13,6 +13,7 @@ interface NarrowChannelsProps {
|
|||
setShowChannels: (val: boolean) => void;
|
||||
channels: ChannelData[];
|
||||
membersList: string[];
|
||||
groupList: string[];
|
||||
setCreateChat: (val: boolean) => void;
|
||||
}
|
||||
|
||||
|
@ -23,6 +24,7 @@ export function NarrowChannels({
|
|||
setShowChannels,
|
||||
channels,
|
||||
membersList,
|
||||
groupList,
|
||||
setCreateChat,
|
||||
}: NarrowChannelsProps) {
|
||||
return (
|
||||
|
@ -36,6 +38,7 @@ export function NarrowChannels({
|
|||
activeChannelId={activeChannelId}
|
||||
channels={channels}
|
||||
membersList={membersList}
|
||||
groupList={groupList}
|
||||
setCreateChat={setCreateChat}
|
||||
/>
|
||||
</ListWrapper>
|
||||
|
|
Loading…
Reference in New Issue