1
0
mirror of https://github.com/spacebarchat/client.git synced 2024-11-25 19:52:31 +01:00

implement image viewer modal

This commit is contained in:
Puyodead1 2023-12-19 13:19:05 -05:00
parent 126eec5b64
commit f720ce8975
No known key found for this signature in database
GPG Key ID: BA5F91AAEF68E5CE
8 changed files with 84 additions and 15 deletions

View File

@ -2,6 +2,7 @@
// https://github.com/revoltchat/revite/blob/master/src/components/common/messaging/embed/Embed.tsx // https://github.com/revoltchat/revite/blob/master/src/components/common/messaging/embed/Embed.tsx
import { APIEmbed, EmbedType } from "@spacebarchat/spacebar-api-types/v9"; import { APIEmbed, EmbedType } from "@spacebarchat/spacebar-api-types/v9";
import { modalController } from "../../controllers/modals";
import styles from "./Embed.module.css"; import styles from "./Embed.module.css";
interface Props { interface Props {
@ -111,7 +112,16 @@ function EmbedMedia({ embed, width, height, thumbnail }: Props) {
src={url} src={url}
loading="lazy" loading="lazy"
style={{ width: "100%", height: "100%" }} style={{ width: "100%", height: "100%" }}
onClick={() => window.open(url, "_blank")} onClick={() => {
if (!embed.image) {
console.error("embed has no image... wtf");
return;
}
modalController.push({
type: "image_viewer",
attachment: embed.image,
});
}}
/> />
); );
} else if (embed.thumbnail) { } else if (embed.thumbnail) {
@ -123,7 +133,16 @@ function EmbedMedia({ embed, width, height, thumbnail }: Props) {
src={url} src={url}
loading="lazy" loading="lazy"
style={{ width, height }} style={{ width, height }}
onClick={() => window.open(url, "_blank")} onClick={() => {
if (!embed.thumbnail) {
console.error("embed has no thumbnail... wtf");
return;
}
modalController.push({
type: "image_viewer",
attachment: embed.thumbnail,
});
}}
/> />
); );
} }

View File

@ -1,5 +1,6 @@
import { APIAttachment } from "@spacebarchat/spacebar-api-types/v9"; import { APIAttachment } from "@spacebarchat/spacebar-api-types/v9";
import styled from "styled-components"; import styled from "styled-components";
import { modalController } from "../../controllers/modals";
import useLogger from "../../hooks/useLogger"; import useLogger from "../../hooks/useLogger";
import { calculateImageRatio, calculateScaledDimensions } from "../../utils/Message"; import { calculateImageRatio, calculateScaledDimensions } from "../../utils/Message";
import { getFileDetails, zoomFit } from "../../utils/Utils"; import { getFileDetails, zoomFit } from "../../utils/Utils";
@ -57,7 +58,12 @@ export default function MessageAttachment({ attachment, maxWidth, maxHeight }: A
onClick={() => { onClick={() => {
if (!attachment.content_type?.startsWith("image")) return; if (!attachment.content_type?.startsWith("image")) return;
const { width, height } = zoomFit(attachment.width!, attachment.height!); const { width, height } = zoomFit(attachment.width!, attachment.height!);
// TODO: preview modal modalController.push({
type: "image_viewer",
attachment,
width,
height,
});
}} }}
> >
{finalElement} {finalElement}

View File

@ -1,3 +0,0 @@
export function AttachmentPreviewModal() {
return null;
}

View File

@ -0,0 +1,36 @@
import { APIAttachment, APIEmbedImage, APIEmbedThumbnail } from "@spacebarchat/spacebar-api-types/v9";
import styled from "styled-components";
import { Modal } from "./ModalComponents";
const Container = styled.div`
display: flex;
overflow: hidden;
flex-direction: column;
max-width: 90vw;
max-height: 75vh;
img {
width: auto;
height: auto;
object-fit: contain;
}
`;
interface Props {
attachment: APIAttachment | APIEmbedImage | APIEmbedThumbnail;
width?: number;
height?: number;
}
export function ImageViewerModal(props: Props) {
const width = props.width ?? props.attachment.width ?? 0;
const height = props.height ?? props.attachment.height ?? 0;
return (
<Modal {...props} transparent maxWidth="100vw" maxHeight="100vh" withoutCloseButton withEmptyActionBar>
<Container>
<img src={props.attachment.url} width={width} height={height} loading="eager" />
</Container>
</Modal>
);
}

View File

@ -26,6 +26,7 @@ interface ModalProps {
actions?: ModalAction[]; actions?: ModalAction[];
disabled?: boolean; disabled?: boolean;
withEmptyActionBar?: boolean; withEmptyActionBar?: boolean;
withoutCloseButton?: boolean;
} }
/** /**
@ -205,13 +206,15 @@ export function Modal({ title, description, ...props }: ModalProps) {
<Portal> <Portal>
<ModalBase closing={closing} onClick={() => !props.nonDismissable && closeModal()}> <ModalBase closing={closing} onClick={() => !props.nonDismissable && closeModal()}>
<ModalWrapper {...props} onClick={(e) => e.stopPropagation()} actions={false}> <ModalWrapper {...props} onClick={(e) => e.stopPropagation()} actions={false}>
<div style={{ position: "relative" }}> {!props.withoutCloseButton && (
{!props.nonDismissable && ( <div style={{ position: "relative" }}>
<ModalCloseWrapper onClick={closeModal}> {!props.nonDismissable && (
<Icon icon="mdiClose" size={1} /> <ModalCloseWrapper onClick={closeModal}>
</ModalCloseWrapper> <Icon icon="mdiClose" size={1} />
)} </ModalCloseWrapper>
</div> )}
</div>
)}
{(title || description) && ( {(title || description) && (
<ModalHeader> <ModalHeader>
{title && typeof title === "string" ? <ModalHeaderText>{title}</ModalHeaderText> : title} {title && typeof title === "string" ? <ModalHeaderText>{title}</ModalHeaderText> : title}

View File

@ -1,11 +1,11 @@
export * from "./AddServerModal"; export * from "./AddServerModal";
export * from "./AttachmentPreviewModal";
export * from "./BanMemberModal"; export * from "./BanMemberModal";
export * from "./CreateInviteModal"; export * from "./CreateInviteModal";
export * from "./CreateServerModal"; export * from "./CreateServerModal";
export * from "./DeleteMessageModal"; export * from "./DeleteMessageModal";
export * from "./ErrorModal"; export * from "./ErrorModal";
export * from "./ForgotPasswordModal"; export * from "./ForgotPasswordModal";
export * from "./ImageViewerModal";
export * from "./JoinServerModal"; export * from "./JoinServerModal";
export * from "./KickMemberModal"; export * from "./KickMemberModal";
export * from "./LeaveServerModal"; export * from "./LeaveServerModal";

View File

@ -8,6 +8,7 @@ import {
CreateServerModal, CreateServerModal,
DeleteMessageModal, DeleteMessageModal,
ErrorModal, ErrorModal,
ImageViewerModal,
JoinServerModal, JoinServerModal,
KickMemberModal, KickMemberModal,
LeaveServerModal, LeaveServerModal,
@ -162,7 +163,7 @@ export const modalController = new ModalControllerExtended({
// custom_status: CustomStatus, // custom_status: CustomStatus,
delete_message: DeleteMessageModal, delete_message: DeleteMessageModal,
error: ErrorModal, error: ErrorModal,
// image_viewer: ImageViewer, image_viewer: ImageViewerModal,
kick_member: KickMemberModal, kick_member: KickMemberModal,
// link_warning: LinkWarning, // link_warning: LinkWarning,
// mfa_flow: MFAFlow, // mfa_flow: MFAFlow,

View File

@ -1,5 +1,6 @@
// adapted from https://github.com/revoltchat/revite/blob/master/src/controllers/modals/types.ts // adapted from https://github.com/revoltchat/revite/blob/master/src/controllers/modals/types.ts
import { APIAttachment, APIEmbedImage, APIEmbedThumbnail } from "@spacebarchat/spacebar-api-types/v9";
import Channel from "../../stores/objects/Channel"; import Channel from "../../stores/objects/Channel";
import Guild from "../../stores/objects/Guild"; import Guild from "../../stores/objects/Guild";
import GuildMember from "../../stores/objects/GuildMember"; import GuildMember from "../../stores/objects/GuildMember";
@ -42,6 +43,12 @@ export type Modal = {
type: "leave_server"; type: "leave_server";
target: Guild; target: Guild;
} }
| {
type: "image_viewer";
attachment: APIAttachment | APIEmbedImage | APIEmbedThumbnail;
width?: number;
height?: number;
}
); );
export type ModalProps<T extends Modal["type"]> = Modal & { type: T } & { export type ModalProps<T extends Modal["type"]> = Modal & { type: T } & {