refactor: preview image modal
This commit is contained in:
+34
-32
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "vocechat-web",
|
||||
"version": "0.9.11",
|
||||
"version": "0.9.15",
|
||||
"homepage": "https://voce.chat",
|
||||
"dependencies": {
|
||||
"@metamask/onboarding": "^1.0.1",
|
||||
"@pmmmwh/react-refresh-webpack-plugin": "^0.6.0",
|
||||
"@radix-ui/react-popover": "^1.1.13",
|
||||
"@pmmmwh/react-refresh-webpack-plugin": "^0.6.1",
|
||||
"@radix-ui/react-popover": "^1.1.14",
|
||||
"@react-oauth/google": "^0.12.2",
|
||||
"@reduxjs/toolkit": "^2.8.2",
|
||||
"@svgr/webpack": "^8.1.0",
|
||||
@@ -24,50 +24,50 @@
|
||||
"@udecode/plate-paragraph": "^33.0.0",
|
||||
"@udecode/plate-trailing-block": "^33.0.0",
|
||||
"@uiball/loaders": "^1.3.1",
|
||||
"agora-rtc-sdk-ng": "^4.23.3",
|
||||
"agora-rtc-sdk-ng": "^4.24.0",
|
||||
"broadcast-channel": "^7.1.0",
|
||||
"browserslist": "^4.24.5",
|
||||
"browserslist": "^4.25.1",
|
||||
"clsx": "^2.1.1",
|
||||
"copy-image-clipboard": "^2.1.2",
|
||||
"css-loader": "^7.1.2",
|
||||
"css-minimizer-webpack-plugin": "^7.0.2",
|
||||
"dayjs": "^1.11.13",
|
||||
"firebase": "^11.7.3",
|
||||
"framer-motion": "12.11.3",
|
||||
"fs-extra": "^11.3.0",
|
||||
"framer-motion": "12.23.12",
|
||||
"fs-extra": "^11.3.1",
|
||||
"heic2any": "^0.0.4",
|
||||
"html-webpack-plugin": "^5.6.3",
|
||||
"i18next": "^24.2.2",
|
||||
"i18next-browser-languagedetector": "^8.1.0",
|
||||
"i18next": "^25.3.2",
|
||||
"i18next-browser-languagedetector": "^8.2.0",
|
||||
"i18next-http-backend": "^3.0.2",
|
||||
"linkify-plugin-mention": "^4.2.0",
|
||||
"linkify-react": "^4.3.1",
|
||||
"linkify-string": "^4.3.1",
|
||||
"linkifyjs": "^4.3.1",
|
||||
"linkify-plugin-mention": "^4.3.2",
|
||||
"linkify-react": "^4.3.2",
|
||||
"linkify-string": "^4.3.2",
|
||||
"linkifyjs": "^4.3.2",
|
||||
"localforage": "^1.10.0",
|
||||
"localforage-setitems": "^1.4.0",
|
||||
"lodash": "^4.17.21",
|
||||
"masonry-layout": "^4.2.2",
|
||||
"mini-css-extract-plugin": "^2.9.2",
|
||||
"mini-css-extract-plugin": "^2.9.3",
|
||||
"prismjs": "^1.30.0",
|
||||
"qrcode.react": "^4.2.0",
|
||||
"react": "^19.1.0",
|
||||
"react": "^19.1.1",
|
||||
"react-dev-utils": "^12.0.1",
|
||||
"react-dnd": "16.0.1",
|
||||
"react-dnd-html5-backend": "16.0.1",
|
||||
"react-dom": "^19.1.0",
|
||||
"react-dom": "^19.1.1",
|
||||
"react-error-boundary": "^6.0.0",
|
||||
"react-hot-toast": "^2.5.2",
|
||||
"react-i18next": "^15.5.1",
|
||||
"react-i18next": "^15.6.1",
|
||||
"react-redux": "^9.2.0",
|
||||
"react-refresh": "0.17.0",
|
||||
"react-router-dom": "^7.6.0",
|
||||
"react-router-dom": "^7.8.0",
|
||||
"react-syntax-highlighter": "^15.6.1",
|
||||
"react-textarea-autosize": "^8.5.9",
|
||||
"react-use-wizard": "^2.3.0",
|
||||
"react-viewport-list": "^7.1.2",
|
||||
"react-virtuoso": "^4.12.7",
|
||||
"rooks": "^7.14.1",
|
||||
"react-virtuoso": "^4.13.0",
|
||||
"rooks": "^8.4.0",
|
||||
"slate": "^0.112.0",
|
||||
"slate-history": "^0.110.3",
|
||||
"slate-hyperscript": "^0.100.0",
|
||||
@@ -77,8 +77,8 @@
|
||||
"tailwind-merge": "2.5.4",
|
||||
"terser-webpack-plugin": "^5.3.14",
|
||||
"tippy.js": "^6.3.7",
|
||||
"wavesurfer.js": "^7.9.5",
|
||||
"webpack": "^5.99.8",
|
||||
"wavesurfer.js": "^7.10.1",
|
||||
"webpack": "^5.101.0",
|
||||
"webpack-dev-server": "^4.15.1",
|
||||
"webpack-manifest-plugin": "^5.0.1",
|
||||
"workbox-core": "^7.3.0",
|
||||
@@ -86,7 +86,8 @@
|
||||
"workbox-precaching": "^7.3.0",
|
||||
"workbox-routing": "^7.3.0",
|
||||
"workbox-strategies": "^7.3.0",
|
||||
"workbox-webpack-plugin": "^7.3.0"
|
||||
"workbox-webpack-plugin": "^7.3.0",
|
||||
"yet-another-react-lightbox": "^3.25.0"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "REACT_APP_BUILD_TIME=$(date +%s) node scripts/start.js",
|
||||
@@ -113,29 +114,30 @@
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.27.1",
|
||||
"@eslint/js": "^9.26.0",
|
||||
"@types/lodash": "^4.17.16",
|
||||
"@babel/core": "^7.28.0",
|
||||
"@eslint/js": "^9.32.0",
|
||||
"@types/lodash": "^4.17.20",
|
||||
"@types/masonry-layout": "^4.2.8",
|
||||
"@types/node": "^22.15.18",
|
||||
"@types/react": "18.3.18",
|
||||
"@types/react-dom": "^18.3.5",
|
||||
"@types/react": "19.1.9",
|
||||
"@types/react-dom": "^19.1.7",
|
||||
"@types/react-helmet": "^6.1.11",
|
||||
"@types/react-syntax-highlighter": "^15.5.13",
|
||||
"autoprefixer": "^10.4.21",
|
||||
"babel-loader": "^9.2.1",
|
||||
"babel-plugin-named-asset-import": "^0.3.8",
|
||||
"babel-preset-react-app": "^10.1.0",
|
||||
"eslint": "^9.26.0",
|
||||
"eslint": "^9.32.0",
|
||||
"gh-pages": "^6.3.0",
|
||||
"md5-file": "^5.0.0",
|
||||
"postcss": "^8.5.3",
|
||||
"postcss": "^8.5.6",
|
||||
"postcss-loader": "^8.1.1",
|
||||
"tailwindcss": "^3.4.14",
|
||||
"typescript": "^5.8.3",
|
||||
"typescript-eslint": "^8.32.1",
|
||||
"typescript": "^5.9.2",
|
||||
"typescript-eslint": "^8.39.0",
|
||||
"webpack-bundle-analyzer": "^4.10.2"
|
||||
},
|
||||
"packageManager": "pnpm@10.14.0",
|
||||
"pnpm": {
|
||||
"patchedDependencies": {
|
||||
"slate-react": "patches/slate-react.patch",
|
||||
|
||||
Generated
+1514
-1782
File diff suppressed because it is too large
Load Diff
+13
-6
@@ -187,13 +187,20 @@ function build(previousFileSizes) {
|
||||
}
|
||||
|
||||
function copyPublicFolder() {
|
||||
const ignoreFiles = ["widget_demo.html", "CNAME", "email.tpl.html", "robots.txt"];
|
||||
fs.copySync(paths.appPublic, paths.appBuild, {
|
||||
dereference: true,
|
||||
filter: (file) => {
|
||||
const [fileName] = file.split("/").slice(-1);
|
||||
const filters = file !== paths.appHtml && !ignoreFiles.includes(fileName);
|
||||
return filters;
|
||||
},
|
||||
filter: (file) => file !== paths.appHtml,
|
||||
});
|
||||
}
|
||||
|
||||
// function copyPublicFolder() {
|
||||
// const ignoreFiles = ["widget_demo.html", "CNAME", "email.tpl.html", "robots.txt"];
|
||||
// fs.copySync(paths.appPublic, paths.appBuild, {
|
||||
// dereference: true,
|
||||
// filter: (file) => {
|
||||
// const [fileName] = file.split("/").slice(-1);
|
||||
// const filters = file !== paths.appHtml && !ignoreFiles.includes(fileName);
|
||||
// return filters;
|
||||
// },
|
||||
// });
|
||||
// }
|
||||
|
||||
+2
-2
@@ -12,8 +12,8 @@ const prices: Price[] = [
|
||||
},
|
||||
];
|
||||
const official_dev = `https://dev.voce.chat`;
|
||||
const local_dev = `http://localhost:3000`;
|
||||
// const local_dev = `http://chat.jcdl369.top:3009`;
|
||||
const local_dev = `http://localhost:3333`;
|
||||
// const local_dev = `https://dev.voce.chat`;
|
||||
const dev_origin = process.env.REACT_APP_OFFICIAL_DEMO ? official_dev : local_dev;
|
||||
|
||||
// const local_dev = `https://im.ttt.td`;
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { FC, useEffect, useRef, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Ring } from "@uiball/loaders";
|
||||
import { useKey, useOutsideClick } from "rooks";
|
||||
|
||||
import Modal from "./Modal";
|
||||
|
||||
import { FC } from "react";
|
||||
import { Lightbox } from "yet-another-react-lightbox";
|
||||
import "yet-another-react-lightbox/styles.css";
|
||||
import Fullscreen from "yet-another-react-lightbox/plugins/fullscreen";
|
||||
import Download from "yet-another-react-lightbox/plugins/download";
|
||||
import Captions from "yet-another-react-lightbox/plugins/captions";
|
||||
import Zoom from "yet-another-react-lightbox/plugins/zoom";
|
||||
import "yet-another-react-lightbox/plugins/captions.css";
|
||||
export interface PreviewImageData {
|
||||
originUrl: string;
|
||||
thumbnail?: string;
|
||||
@@ -20,69 +21,32 @@ interface Props {
|
||||
}
|
||||
|
||||
const ImagePreviewModal: FC<Props> = ({ download = true, data, closeModal }) => {
|
||||
const { t } = useTranslation();
|
||||
const [url, setUrl] = useState(data?.thumbnail);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const wrapperRef = useRef<HTMLDivElement>(null);
|
||||
useOutsideClick(wrapperRef, closeModal);
|
||||
|
||||
useKey(
|
||||
"Escape",
|
||||
() => {
|
||||
closeModal();
|
||||
},
|
||||
{ eventTypes: ["keyup"] }
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
const { originUrl } = data;
|
||||
if (!originUrl) setLoading(false);
|
||||
const img = new Image();
|
||||
img.src = originUrl;
|
||||
img.onload = () => {
|
||||
setUrl(originUrl);
|
||||
setLoading(false);
|
||||
};
|
||||
img.onerror = () => {
|
||||
setLoading(false);
|
||||
};
|
||||
}
|
||||
}, [data]);
|
||||
|
||||
if (!data) return null;
|
||||
const { originUrl, downloadLink, name, type } = data;
|
||||
return (
|
||||
<Modal>
|
||||
<div className="w-screen h-screen flex-center bg-black/90">
|
||||
<div className={`relative flex flex-col justify-start gap-3`} ref={wrapperRef}>
|
||||
<div className="overflow-auto">
|
||||
<img
|
||||
className={`max-w-[70vw] max-h-[80vh] ${loading ? "blur-sm" : ""}`}
|
||||
src={url}
|
||||
alt="preview"
|
||||
/>
|
||||
</div>
|
||||
{download && (
|
||||
<a
|
||||
// onClick={handleDownload}
|
||||
className="font-semibold text-sm text-gray-600 hover:underline hover:text-white"
|
||||
download={name}
|
||||
type={type}
|
||||
href={downloadLink || originUrl}
|
||||
rel="noreferrer"
|
||||
>
|
||||
{t("action.download_origin")}
|
||||
</a>
|
||||
)}
|
||||
{loading && (
|
||||
<div className="absolute top-[40%] left-1/2 -translate-x-1/2">
|
||||
<Ring />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
<Lightbox
|
||||
open
|
||||
close={closeModal}
|
||||
styles={{
|
||||
navigationNext: {
|
||||
display: "none",
|
||||
},
|
||||
navigationPrev: {
|
||||
display: "none",
|
||||
},
|
||||
}}
|
||||
slides={[
|
||||
{
|
||||
src: originUrl,
|
||||
alt: name,
|
||||
title: name,
|
||||
description: name,
|
||||
download: downloadLink || originUrl,
|
||||
},
|
||||
]}
|
||||
plugins={download ? [Captions, Fullscreen, Zoom, Download] : [Captions, Fullscreen, Zoom]}
|
||||
captions={{ descriptionTextAlign: "center" }}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
import { forwardRef } from "react";
|
||||
import { PlateElement, PlateElementProps } from "@udecode/plate-common";
|
||||
|
||||
import { cn } from "@/utils";
|
||||
|
||||
const ParagraphElement = forwardRef<React.ElementRef<typeof PlateElement>, PlateElementProps>(
|
||||
({ className, children, ...props }: PlateElementProps, ref) => {
|
||||
return (
|
||||
<PlateElement ref={ref} className={cn("m-0 px-0 py-1", className)} {...props}>
|
||||
{children}
|
||||
</PlateElement>
|
||||
);
|
||||
}
|
||||
);
|
||||
const ParagraphElement = ({ className, children, ref, ...props }: PlateElementProps) => {
|
||||
return (
|
||||
<PlateElement ref={ref} className={cn("m-0 px-0 py-1", className)} {...props}>
|
||||
{children}
|
||||
</PlateElement>
|
||||
);
|
||||
};
|
||||
ParagraphElement.displayName = "ParagraphElement";
|
||||
|
||||
export { ParagraphElement };
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { forwardRef } from "react";
|
||||
|
||||
// @ts-ignore
|
||||
const CustomList = forwardRef(({ style, ...props }, ref) => {
|
||||
const CustomList = ({ style, ref, ...props }) => {
|
||||
// @ts-ignore
|
||||
return <div style={{ ...style, width: `calc(100% - 2rem)` }} {...props} ref={ref} />;
|
||||
});
|
||||
};
|
||||
|
||||
export default CustomList;
|
||||
|
||||
Reference in New Issue
Block a user