Add modal for images (#60)
This commit is contained in:
parent
a722e8527a
commit
173e400e80
|
@ -12,6 +12,7 @@ import { Channels } from "./Channels";
|
||||||
import { ChatBody } from "./Chat/ChatBody";
|
import { ChatBody } from "./Chat/ChatBody";
|
||||||
import { Members } from "./Members";
|
import { Members } from "./Members";
|
||||||
import { CommunityModal } from "./Modals/CommunityModal";
|
import { CommunityModal } from "./Modals/CommunityModal";
|
||||||
|
import { PictureModal } from "./Modals/PictureModal";
|
||||||
|
|
||||||
interface ChatProps {
|
interface ChatProps {
|
||||||
theme: Theme;
|
theme: Theme;
|
||||||
|
@ -40,6 +41,7 @@ export function Chat({ theme, community, fetchMetadata }: ChatProps) {
|
||||||
|
|
||||||
const [isModalVisible, setIsModalVisible] = useState(false);
|
const [isModalVisible, setIsModalVisible] = useState(false);
|
||||||
const showModal = () => setIsModalVisible(true);
|
const showModal = () => setIsModalVisible(true);
|
||||||
|
const [image, setImage] = useState("");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ChatWrapper>
|
<ChatWrapper>
|
||||||
|
@ -70,6 +72,7 @@ export function Chat({ theme, community, fetchMetadata }: ChatProps) {
|
||||||
onCommunityClick={showModal}
|
onCommunityClick={showModal}
|
||||||
lastMessage={lastMessage}
|
lastMessage={lastMessage}
|
||||||
fetchMetadata={fetchMetadata}
|
fetchMetadata={fetchMetadata}
|
||||||
|
setImage={setImage}
|
||||||
/>
|
/>
|
||||||
{showMembers && !narrow && (
|
{showMembers && !narrow && (
|
||||||
<Members community={community} setShowChannels={setShowChannels} />
|
<Members community={community} setShowChannels={setShowChannels} />
|
||||||
|
@ -83,6 +86,11 @@ export function Chat({ theme, community, fetchMetadata }: ChatProps) {
|
||||||
description={community.description}
|
description={community.description}
|
||||||
publicKey="0xD95DBdaB08A9FED2D71ac9C3028AAc40905d8CF3"
|
publicKey="0xD95DBdaB08A9FED2D71ac9C3028AAc40905d8CF3"
|
||||||
/>
|
/>
|
||||||
|
<PictureModal
|
||||||
|
isVisible={!!image}
|
||||||
|
onClose={() => setImage("")}
|
||||||
|
image={image}
|
||||||
|
/>
|
||||||
</ChatWrapper>
|
</ChatWrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,7 @@ interface ChatBodyProps {
|
||||||
onCommunityClick: () => void;
|
onCommunityClick: () => void;
|
||||||
lastMessage: Date;
|
lastMessage: Date;
|
||||||
fetchMetadata?: (url: string) => Promise<Metadata | undefined>;
|
fetchMetadata?: (url: string) => Promise<Metadata | undefined>;
|
||||||
|
setImage: (image: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ChatBody({
|
export function ChatBody({
|
||||||
|
@ -55,6 +56,7 @@ export function ChatBody({
|
||||||
onCommunityClick,
|
onCommunityClick,
|
||||||
lastMessage,
|
lastMessage,
|
||||||
fetchMetadata,
|
fetchMetadata,
|
||||||
|
setImage,
|
||||||
}: ChatBodyProps) {
|
}: ChatBodyProps) {
|
||||||
const narrow = useNarrow();
|
const narrow = useNarrow();
|
||||||
const [showChannelsList, setShowChannelsList] = useState(false);
|
const [showChannelsList, setShowChannelsList] = useState(false);
|
||||||
|
@ -117,6 +119,7 @@ export function ChatBody({
|
||||||
messages={messages}
|
messages={messages}
|
||||||
loadNextDay={loadNextDay}
|
loadNextDay={loadNextDay}
|
||||||
fetchMetadata={fetchMetadata}
|
fetchMetadata={fetchMetadata}
|
||||||
|
setImage={setImage}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<LoadingSkeleton />
|
<LoadingSkeleton />
|
||||||
|
|
|
@ -13,11 +13,15 @@ const regEx =
|
||||||
type ChatMessageContentProps = {
|
type ChatMessageContentProps = {
|
||||||
message: ChatMessage;
|
message: ChatMessage;
|
||||||
fetchMetadata?: (url: string) => Promise<Metadata | undefined>;
|
fetchMetadata?: (url: string) => Promise<Metadata | undefined>;
|
||||||
|
|
||||||
|
setImage: (image: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function ChatMessageContent({
|
export function ChatMessageContent({
|
||||||
message,
|
message,
|
||||||
fetchMetadata,
|
fetchMetadata,
|
||||||
|
|
||||||
|
setImage,
|
||||||
}: ChatMessageContentProps) {
|
}: ChatMessageContentProps) {
|
||||||
const { content, image } = useMemo(() => message, [message]);
|
const { content, image } = useMemo(() => message, [message]);
|
||||||
const [elements, setElements] = useState<(string | React.ReactElement)[]>([
|
const [elements, setElements] = useState<(string | React.ReactElement)[]>([
|
||||||
|
@ -71,7 +75,15 @@ export function ChatMessageContent({
|
||||||
return (
|
return (
|
||||||
<ContentWrapper>
|
<ContentWrapper>
|
||||||
<div>{elements.map((el) => el)}</div>
|
<div>{elements.map((el) => el)}</div>
|
||||||
{image && <MessageImage src={image} />}
|
{image && (
|
||||||
|
<MessageImageWrapper
|
||||||
|
onClick={() => {
|
||||||
|
setImage(image);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<MessageImage src={image} />
|
||||||
|
</MessageImageWrapper>
|
||||||
|
)}
|
||||||
{openGraph && (
|
{openGraph && (
|
||||||
<PreviewWrapper
|
<PreviewWrapper
|
||||||
onClick={() => window?.open(link, "_blank", "noopener")?.focus()}
|
onClick={() => window?.open(link, "_blank", "noopener")?.focus()}
|
||||||
|
@ -87,13 +99,19 @@ export function ChatMessageContent({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const MessageImage = styled.img`
|
const MessageImageWrapper = styled.div`
|
||||||
width: 147px;
|
width: 147px;
|
||||||
height: 196px;
|
height: 196px;
|
||||||
border-radius: 16px;
|
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const MessageImage = styled.img`
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
border-radius: 16px;
|
||||||
|
`;
|
||||||
|
|
||||||
const PreviewSiteNameWrapper = styled.div`
|
const PreviewSiteNameWrapper = styled.div`
|
||||||
font-family: Inter;
|
font-family: Inter;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
|
|
|
@ -12,12 +12,16 @@ type ChatMessagesProps = {
|
||||||
messages: ChatMessage[];
|
messages: ChatMessage[];
|
||||||
loadNextDay: () => void;
|
loadNextDay: () => void;
|
||||||
fetchMetadata?: (url: string) => Promise<Metadata | undefined>;
|
fetchMetadata?: (url: string) => Promise<Metadata | undefined>;
|
||||||
|
|
||||||
|
setImage: (image: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function ChatMessages({
|
export function ChatMessages({
|
||||||
messages,
|
messages,
|
||||||
loadNextDay,
|
loadNextDay,
|
||||||
fetchMetadata,
|
fetchMetadata,
|
||||||
|
|
||||||
|
setImage,
|
||||||
}: ChatMessagesProps) {
|
}: ChatMessagesProps) {
|
||||||
const [scrollOnBot, setScrollOnBot] = useState(true);
|
const [scrollOnBot, setScrollOnBot] = useState(true);
|
||||||
const ref = useRef<HTMLHeadingElement>(null);
|
const ref = useRef<HTMLHeadingElement>(null);
|
||||||
|
@ -80,6 +84,7 @@ export function ChatMessages({
|
||||||
<ChatMessageContent
|
<ChatMessageContent
|
||||||
message={message}
|
message={message}
|
||||||
fetchMetadata={fetchMetadata}
|
fetchMetadata={fetchMetadata}
|
||||||
|
setImage={setImage}
|
||||||
/>
|
/>
|
||||||
</MessageText>
|
</MessageText>
|
||||||
</ContentWrapper>
|
</ContentWrapper>
|
||||||
|
|
|
@ -44,7 +44,7 @@ export const Modal = ({
|
||||||
<ModalView>
|
<ModalView>
|
||||||
<ModalOverlay onClick={onClose} />
|
<ModalOverlay onClick={onClose} />
|
||||||
<ModalBody className={className}>
|
<ModalBody className={className}>
|
||||||
<CloseButton onClick={onClose}>
|
<CloseButton onClick={onClose} className={className}>
|
||||||
<CrossIcon />
|
<CrossIcon />
|
||||||
</CloseButton>
|
</CloseButton>
|
||||||
{children}
|
{children}
|
||||||
|
@ -75,6 +75,11 @@ const ModalBody = styled.div`
|
||||||
background: ${({ theme }) => theme.bodyBackgroundColor};
|
background: ${({ theme }) => theme.bodyBackgroundColor};
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
|
||||||
|
&.picture {
|
||||||
|
max-width: 820px;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const ModalOverlay = styled.div`
|
const ModalOverlay = styled.div`
|
||||||
|
@ -94,4 +99,8 @@ const CloseButton = styled.button`
|
||||||
top: 12px;
|
top: 12px;
|
||||||
right: 12px;
|
right: 12px;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
|
|
||||||
|
&.picture {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
import React from "react";
|
||||||
|
import styled from "styled-components";
|
||||||
|
|
||||||
|
import { BasicModalProps, Modal } from "./Modal";
|
||||||
|
|
||||||
|
interface PictureModalProps extends BasicModalProps {
|
||||||
|
image: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PictureModal = ({
|
||||||
|
isVisible,
|
||||||
|
onClose,
|
||||||
|
image,
|
||||||
|
}: PictureModalProps) => {
|
||||||
|
return (
|
||||||
|
<Modal isVisible={isVisible} onClose={onClose} className="picture">
|
||||||
|
<ModalImageWrapper>
|
||||||
|
<ModalImage src={image}></ModalImage>
|
||||||
|
</ModalImageWrapper>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const ModalImageWrapper = styled.div`
|
||||||
|
display: flex;
|
||||||
|
max-width: 820px;
|
||||||
|
max-height: 820px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ModalImage = styled.img`
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
`;
|
Loading…
Reference in New Issue