From 91a61e058a482484c024d532ec75414aac1fc4aa Mon Sep 17 00:00:00 2001 From: Zhou Yunliang Date: Wed, 2 Nov 2022 20:00:28 +0800 Subject: [PATCH] feat: view all images of a memo (#393) * feat: view all images of a memo * fix: function arguments * refactor: unified image preview * refactor: image preview for resource dialog Co-authored-by: XQ --- web/src/components/Image.tsx | 4 +- web/src/components/Memo.tsx | 12 ++- .../components/PreviewImageCarouselDialog.tsx | 76 ------------------- web/src/components/PreviewImageDialog.tsx | 35 +++++++-- web/src/components/ResourcesDialog.tsx | 11 ++- .../less/preview-image-carousel-dialog.less | 34 --------- 6 files changed, 47 insertions(+), 125 deletions(-) delete mode 100644 web/src/components/PreviewImageCarouselDialog.tsx delete mode 100644 web/src/less/preview-image-carousel-dialog.less diff --git a/web/src/components/Image.tsx b/web/src/components/Image.tsx index 85881ef5..1a299abc 100644 --- a/web/src/components/Image.tsx +++ b/web/src/components/Image.tsx @@ -1,4 +1,4 @@ -import showPreviewImageCarouselDialog from "./PreviewImageCarouselDialog"; +import showPreviewImageDialog from "./PreviewImageDialog"; import "../less/image.less"; interface Props { @@ -11,7 +11,7 @@ const Image: React.FC = (props: Props) => { const { className, imgUrls, index } = props; const handleImageClick = () => { - showPreviewImageCarouselDialog(imgUrls, index); + showPreviewImageDialog(imgUrls, index); }; return ( diff --git a/web/src/components/Memo.tsx b/web/src/components/Memo.tsx index b8748522..c4604b4d 100644 --- a/web/src/components/Memo.tsx +++ b/web/src/components/Memo.tsx @@ -161,9 +161,15 @@ const Memo: React.FC = (props: Props) => { } } } else if (targetEl.tagName === "IMG") { - const imgUrl = targetEl.getAttribute("src"); - if (imgUrl) { - showPreviewImageDialog(imgUrl); + const currImgUrl = targetEl.getAttribute("src"); + + if (currImgUrl) { + // use regex to get all image urls from memo content + const imageUrls = memo.content.match(/!\[.*?\]\((.*?)\)/g)?.map((item) => item.match(/\((.*?)\)/)?.[1] ?? "") ?? []; + showPreviewImageDialog( + imageUrls, + imageUrls.findIndex((item) => item === currImgUrl) + ); } } }; diff --git a/web/src/components/PreviewImageCarouselDialog.tsx b/web/src/components/PreviewImageCarouselDialog.tsx deleted file mode 100644 index faaba130..00000000 --- a/web/src/components/PreviewImageCarouselDialog.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import * as utils from "../helpers/utils"; -import Icon from "./Icon"; -import { generateDialog } from "./Dialog"; -import "../less/preview-image-carousel-dialog.less"; - -interface Props extends DialogProps { - imgUrls: string[]; - index: number; -} - -const PreviewImageDialog: React.FC = ({ destroy, imgUrls, index }: Props) => { - const handleCloseBtnClick = () => { - destroy(); - }; - - const handleDownloadBtnClick = () => { - const a = document.createElement("a"); - a.href = imgUrls[index]; - a.download = `memos-${utils.getDateTimeString(Date.now())}.png`; - a.click(); - }; - - const handleImgContainerClick = () => { - destroy(); - }; - - const handlePrevBtnClick = () => { - destroy(); - if (index > 0) { - showPreviewImageCarouselDialog(imgUrls, index - 1); - } else { - showPreviewImageCarouselDialog(imgUrls, imgUrls.length - 1); - } - }; - - const handleNextBtnClick = () => { - destroy(); - if (index < imgUrls.length - 1) { - showPreviewImageCarouselDialog(imgUrls, index + 1); - } else { - showPreviewImageCarouselDialog(imgUrls, 0); - } - }; - - return ( - <> -
- - - - -
-
- e.stopPropagation()} src={imgUrls[index]} /> -
- - ); -}; - -export default function showPreviewImageCarouselDialog(imgUrls: string[], index: number): void { - generateDialog( - { - className: "preview-image-carousel-dialog", - }, - PreviewImageDialog, - { imgUrls, index } - ); -} diff --git a/web/src/components/PreviewImageDialog.tsx b/web/src/components/PreviewImageDialog.tsx index 3266672d..999af02d 100644 --- a/web/src/components/PreviewImageDialog.tsx +++ b/web/src/components/PreviewImageDialog.tsx @@ -1,26 +1,42 @@ +import { useState } from "react"; import * as utils from "../helpers/utils"; import Icon from "./Icon"; import { generateDialog } from "./Dialog"; import "../less/preview-image-dialog.less"; interface Props extends DialogProps { - imgUrl: string; + imgUrls: string[]; + initialIndex: number; } -const PreviewImageDialog: React.FC = ({ destroy, imgUrl }: Props) => { +const PreviewImageDialog: React.FC = ({ destroy, imgUrls, initialIndex }: Props) => { + const [currentIndex, setCurrentIndex] = useState(initialIndex); + const handleCloseBtnClick = () => { destroy(); }; const handleDownloadBtnClick = () => { const a = document.createElement("a"); - a.href = imgUrl; + a.href = imgUrls[currentIndex]; a.download = `memos-${utils.getDateTimeString(Date.now())}.png`; a.click(); }; - const handleImgContainerClick = () => { - destroy(); + const handleImgContainerClick = (event: React.MouseEvent) => { + if (event.clientX < window.innerWidth / 2) { + if (currentIndex > 0) { + setCurrentIndex(currentIndex - 1); + } else { + destroy(); + } + } else { + if (currentIndex < imgUrls.length - 1) { + setCurrentIndex(currentIndex + 1); + } else { + destroy(); + } + } }; return ( @@ -34,18 +50,21 @@ const PreviewImageDialog: React.FC = ({ destroy, imgUrl }: Props) => {
- e.stopPropagation()} src={imgUrl} /> + e.stopPropagation()} src={imgUrls[currentIndex]} />
); }; -export default function showPreviewImageDialog(imgUrl: string): void { +export default function showPreviewImageDialog(imgUrls: string[] | string, initialIndex?: number): void { generateDialog( { className: "preview-image-dialog", }, PreviewImageDialog, - { imgUrl } + { + imgUrls: Array.isArray(imgUrls) ? imgUrls : [imgUrls], + initialIndex: initialIndex || 0, + } ); } diff --git a/web/src/components/ResourcesDialog.tsx b/web/src/components/ResourcesDialog.tsx index 3fa7e47a..37cf9eb1 100644 --- a/web/src/components/ResourcesDialog.tsx +++ b/web/src/components/ResourcesDialog.tsx @@ -83,10 +83,17 @@ const ResourcesDialog: React.FC = (props: Props) => { inputEl.click(); }; + const getResouceUrl = useCallback((resource: Resource) => { + return `${window.location.origin}/o/r/${resource.id}/${resource.filename}`; + }, []); + const handlePreviewBtnClick = (resource: Resource) => { - const resourceUrl = `${window.location.origin}/o/r/${resource.id}/${resource.filename}`; + const resourceUrl = getResouceUrl(resource); if (resource.type.startsWith("image")) { - showPreviewImageDialog(resourceUrl); + showPreviewImageDialog( + resources.filter((r) => r.type.startsWith("image")).map((r) => getResouceUrl(r)), + resources.findIndex((r) => r.id === resource.id) + ); } else { window.open(resourceUrl); } diff --git a/web/src/less/preview-image-carousel-dialog.less b/web/src/less/preview-image-carousel-dialog.less deleted file mode 100644 index e3a54cd2..00000000 --- a/web/src/less/preview-image-carousel-dialog.less +++ /dev/null @@ -1,34 +0,0 @@ -@import "./mixin.less"; - -.preview-image-carousel-dialog { - @apply p-0; - z-index: 101; - background-color: rgba(0, 0, 0, 0.6); - - > .dialog-container { - @apply flex flex-col justify-center items-center relative w-full h-full p-0; - background-color: unset; - - > .btns-container { - @apply fixed top-8 right-8 flex flex-col justify-start items-center; - - > .btn { - @apply mb-3 last:mb-0 w-8 h-8 p-1 cursor-pointer rounded opacity-90 bg-gray-300 z-10 shadow-md hover:opacity-70; - - > .icon-img { - @apply w-6 h-auto; - } - } - } - - > .img-container { - @apply w-full h-full p-4 flex flex-col justify-center items-center; - background-color: unset; - .hide-scroll-bar(); - - > img { - @apply h-auto w-auto max-w-full max-h-full shadow; - } - } - } -}