feat: full screen image preview
This commit is contained in:
parent
805ce1d96e
commit
5089cd63eb
14
packages/app/src/Element/MediaElement.css
Normal file
14
packages/app/src/Element/MediaElement.css
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
.modal.spotlight .modal-body {
|
||||||
|
max-width: 100vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal.spotlight img,
|
||||||
|
.modal.spotlight video {
|
||||||
|
max-width: 90vw;
|
||||||
|
max-height: 90vh;
|
||||||
|
aspect-ratio: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal.spotlight .close {
|
||||||
|
text-align: right;
|
||||||
|
}
|
82
packages/app/src/Element/MediaElement.tsx
Normal file
82
packages/app/src/Element/MediaElement.tsx
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
import { ProxyImg } from "Element/ProxyImg";
|
||||||
|
import React, { MouseEvent, useState } from "react";
|
||||||
|
|
||||||
|
import "./MediaElement.css";
|
||||||
|
import Modal from "Element/Modal";
|
||||||
|
import Icon from "Icons/Icon";
|
||||||
|
|
||||||
|
/*
|
||||||
|
[
|
||||||
|
"imeta",
|
||||||
|
"url https://nostr.build/i/148e3e8cbe29ae268b0d6aad0065a086319d3c3b1fdf8b89f1e2327d973d2d05.jpg",
|
||||||
|
"blurhash e6A0%UE2t6D*R%?u?a9G?aM|~pM|%LR*RjR-%2NG%2t7_2R*%1IVWB",
|
||||||
|
"dim 3024x4032"
|
||||||
|
],
|
||||||
|
*/
|
||||||
|
interface MediaElementProps {
|
||||||
|
mime: string;
|
||||||
|
url: string;
|
||||||
|
magnet?: string;
|
||||||
|
sha256?: string;
|
||||||
|
blurHash?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function MediaElement(props: MediaElementProps) {
|
||||||
|
if (props.mime.startsWith("image/")) {
|
||||||
|
return (
|
||||||
|
<SpotlightMedia>
|
||||||
|
<ProxyImg key={props.url} src={props.url} />
|
||||||
|
</SpotlightMedia>
|
||||||
|
);
|
||||||
|
} else if (props.mime.startsWith("audio/")) {
|
||||||
|
return <audio key={props.url} src={props.url} controls />;
|
||||||
|
} else if (props.mime.startsWith("video/")) {
|
||||||
|
return (
|
||||||
|
<SpotlightMedia>
|
||||||
|
<video key={props.url} src={props.url} controls />
|
||||||
|
</SpotlightMedia>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<a
|
||||||
|
key={props.url}
|
||||||
|
href={props.url}
|
||||||
|
onClick={e => e.stopPropagation()}
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
className="ext">
|
||||||
|
{props.url}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SpotlightMedia({ children }: { children: React.ReactNode }) {
|
||||||
|
const [showModal, setShowModal] = useState(false);
|
||||||
|
|
||||||
|
function onClick(e: MouseEvent<HTMLDivElement>) {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
setShowModal(s => !s);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onClose(e: MouseEvent<HTMLDivElement>) {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
setShowModal(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{showModal && (
|
||||||
|
<Modal onClose={onClose} className="spotlight">
|
||||||
|
<div className="close" onClick={onClose}>
|
||||||
|
<Icon name="close" />
|
||||||
|
</div>
|
||||||
|
{children}
|
||||||
|
</Modal>
|
||||||
|
)}
|
||||||
|
<div onClick={onClick}>{children}</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
@ -1,31 +0,0 @@
|
|||||||
import { ProxyImg } from "Element/ProxyImg";
|
|
||||||
|
|
||||||
interface MediaElementProps {
|
|
||||||
mime: string;
|
|
||||||
url: string;
|
|
||||||
magnet?: string;
|
|
||||||
sha256?: string;
|
|
||||||
blurHash?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function MediaElement(props: MediaElementProps) {
|
|
||||||
if (props.mime.startsWith("image/")) {
|
|
||||||
return <ProxyImg key={props.url} src={props.url} />;
|
|
||||||
} else if (props.mime.startsWith("audio/")) {
|
|
||||||
return <audio key={props.url} src={props.url} controls />;
|
|
||||||
} else if (props.mime.startsWith("video/")) {
|
|
||||||
return <video key={props.url} src={props.url} controls />;
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<a
|
|
||||||
key={props.url}
|
|
||||||
href={props.url}
|
|
||||||
onClick={e => e.stopPropagation()}
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
className="ext">
|
|
||||||
{props.url}
|
|
||||||
</a>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,32 +1,15 @@
|
|||||||
import "./Modal.css";
|
import "./Modal.css";
|
||||||
import { useEffect, useRef } from "react";
|
import { useEffect, MouseEventHandler, ReactNode } from "react";
|
||||||
import * as React from "react";
|
|
||||||
|
|
||||||
export interface ModalProps {
|
export interface ModalProps {
|
||||||
className?: string;
|
className?: string;
|
||||||
onClose?: () => void;
|
onClose?: MouseEventHandler;
|
||||||
children: React.ReactNode;
|
children: ReactNode;
|
||||||
}
|
|
||||||
|
|
||||||
function useOnClickOutside(ref: React.MutableRefObject<Element | null>, onClickOutside: () => void) {
|
|
||||||
useEffect(() => {
|
|
||||||
function handleClickOutside(ev: MouseEvent) {
|
|
||||||
if (ref && ref.current && !ref.current.contains(ev.target as Node)) {
|
|
||||||
onClickOutside();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
document.addEventListener("mousedown", handleClickOutside);
|
|
||||||
return () => {
|
|
||||||
document.removeEventListener("mousedown", handleClickOutside);
|
|
||||||
};
|
|
||||||
}, [ref]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Modal(props: ModalProps) {
|
export default function Modal(props: ModalProps) {
|
||||||
const ref = useRef(null);
|
|
||||||
const onClose = props.onClose || (() => undefined);
|
const onClose = props.onClose || (() => undefined);
|
||||||
const className = props.className || "";
|
const className = props.className || "";
|
||||||
useOnClickOutside(ref, onClose);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.body.classList.add("scroll-lock");
|
document.body.classList.add("scroll-lock");
|
||||||
@ -34,10 +17,8 @@ export default function Modal(props: ModalProps) {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`modal ${className}`}>
|
<div className={`modal ${className}`} onClick={onClose}>
|
||||||
<div ref={ref} className="modal-body">
|
<div className="modal-body">{props.children}</div>
|
||||||
{props.children}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ import { findTag, NostrLink } from "Util";
|
|||||||
import useEventFeed from "Feed/EventFeed";
|
import useEventFeed from "Feed/EventFeed";
|
||||||
import PageSpinner from "Element/PageSpinner";
|
import PageSpinner from "Element/PageSpinner";
|
||||||
import Reveal from "Element/Reveal";
|
import Reveal from "Element/Reveal";
|
||||||
import { MediaElement } from "Element/MediaLink";
|
import { MediaElement } from "Element/MediaElement";
|
||||||
|
|
||||||
export default function NostrFileHeader({ link }: { link: NostrLink }) {
|
export default function NostrFileHeader({ link }: { link: NostrLink }) {
|
||||||
const ev = useEventFeed(link);
|
const ev = useEventFeed(link);
|
||||||
|
@ -3,7 +3,7 @@ import { FormattedMessage } from "react-intl";
|
|||||||
import { FileExtensionRegex } from "Const";
|
import { FileExtensionRegex } from "Const";
|
||||||
import Reveal from "Element/Reveal";
|
import Reveal from "Element/Reveal";
|
||||||
import useLogin from "Hooks/useLogin";
|
import useLogin from "Hooks/useLogin";
|
||||||
import { MediaElement } from "Element/MediaLink";
|
import { MediaElement } from "Element/MediaElement";
|
||||||
|
|
||||||
interface RevealMediaProps {
|
interface RevealMediaProps {
|
||||||
creator: string;
|
creator: string;
|
||||||
|
@ -417,27 +417,6 @@ div.form-col {
|
|||||||
grid-template-columns: auto;
|
grid-template-columns: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal {
|
|
||||||
position: absolute;
|
|
||||||
width: 100vw;
|
|
||||||
height: 100vh;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
background-color: rgba(0, 0, 0, 0.8);
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal .modal-content {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal .modal-content > div {
|
|
||||||
padding: 10px;
|
|
||||||
border-radius: 10px;
|
|
||||||
background-color: var(--gray);
|
|
||||||
margin-top: 5vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
body.scroll-lock {
|
body.scroll-lock {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user