diff --git a/package.json b/package.json index f118802c26..23a3cfe52e 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,7 @@ "lemmy-js-client": "0.18.0", "lodash": "^4.17.21", "mdast-util-gfm-autolink-literal-lemmy": "^1.0.3", + "photoswipe": "^5.3.8", "mdast-util-gfm-strikethrough": "^1.0.3", "mdast-util-gfm-table": "^1.0.7", "micromark": "^3.0.0", @@ -77,7 +78,6 @@ "react-animate-height": "^3.1.1", "react-dom": "^18.2.0", "react-markdown": "^8.0.7", - "react-photoswipe-gallery": "^2.2.7", "react-redux": "^8.1.1", "react-router": "^5.3.4", "react-router-dom": "^5.3.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1a3c14ccb6..a7f9afa78d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -181,9 +181,6 @@ devDependencies: react-markdown: specifier: ^8.0.7 version: 8.0.7(@types/react@18.2.14)(react@18.2.0) - react-photoswipe-gallery: - specifier: ^2.2.7 - version: 2.2.7(photoswipe@5.3.7)(prop-types@15.8.1)(react@18.2.0) react-redux: specifier: ^8.1.1 version: 8.1.1(@types/react-dom@18.2.6)(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0)(redux@4.2.1) @@ -6242,18 +6239,6 @@ packages: - supports-color dev: true - /react-photoswipe-gallery@2.2.7(photoswipe@5.3.7)(prop-types@15.8.1)(react@18.2.0): - resolution: {integrity: sha512-AEYNoL4/IIRosIUonn4haaFQNn1ui4vdVgAY9LHd/imVamNCkqUcyWeT6317UILp/yJI2gohsd3lWmcJEbjCag==} - peerDependencies: - photoswipe: '>= 5.2.2' - prop-types: '>= 15.7.0' - react: '>= 16.8.0' - dependencies: - photoswipe: 5.3.7 - prop-types: 15.8.1 - react: 18.2.0 - dev: true - /react-redux@8.1.1(@types/react-dom@18.2.6)(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0)(redux@4.2.1): resolution: {integrity: sha512-5W0QaKtEhj+3bC0Nj0NkqkhIv8gLADH/2kYFMTHxCVqQILiWzLv6MaLuV5wJU3BQEdHKzTfcvPN0WMS6SC1oyA==} peerDependencies: diff --git a/src/App.tsx b/src/App.tsx index 3a24afdea0..9028ba08bf 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -26,6 +26,7 @@ import Router from "./Router"; import BeforeInstallPromptProvider from "./BeforeInstallPromptProvider"; import { UpdateContextProvider } from "./pages/settings/update/UpdateContext"; import GlobalStyles from "./GlobalStyles"; +import GalleryProvider from "./features/gallery/GalleryProvider"; setupIonicReact({ rippleEffect: false, @@ -44,7 +45,9 @@ export default function App() { - + + + diff --git a/src/features/auth/Login.tsx b/src/features/auth/Login.tsx index e5c911e2c7..af9f1cb1a8 100644 --- a/src/features/auth/Login.tsx +++ b/src/features/auth/Login.tsx @@ -25,7 +25,7 @@ import { getClient } from "../../services/lemmy"; import { IonInputCustomEvent } from "@ionic/core"; import TermsSheet from "../settings/terms/TermsSheet"; import { LEMMY_SERVERS } from "../../helpers/lemmy"; -import { preventPhotoswipeGalleryFocusTrap } from "../gallery/Gallery"; +import { preventPhotoswipeGalleryFocusTrap } from "../gallery/GalleryImg"; export const Spinner = styled(IonSpinner)` width: 1.5rem; diff --git a/src/features/comment/reply/CommentReply.tsx b/src/features/comment/reply/CommentReply.tsx index 2e6e69772d..5e54f9ff0b 100644 --- a/src/features/comment/reply/CommentReply.tsx +++ b/src/features/comment/reply/CommentReply.tsx @@ -22,7 +22,7 @@ import { useAppSelector } from "../../../store"; import { Centered, Spinner } from "../../auth/Login"; import { handleSelector, jwtSelector } from "../../auth/authSlice"; import { css } from "@emotion/react"; -import { preventPhotoswipeGalleryFocusTrap } from "../../gallery/Gallery"; +import { preventPhotoswipeGalleryFocusTrap } from "../../gallery/GalleryImg"; export const Container = styled.div` position: absolute; diff --git a/src/features/feed/Feed.tsx b/src/features/feed/Feed.tsx index 2ee622fcac..674e8f1fa6 100644 --- a/src/features/feed/Feed.tsx +++ b/src/features/feed/Feed.tsx @@ -173,7 +173,7 @@ export default function Feed({ fetchMore(); }} components={{ Header: header, Footer: footer }} - increaseViewportBy={800} + increaseViewportBy={5000} /> ); diff --git a/src/features/gallery/Gallery.tsx b/src/features/gallery/Gallery.tsx deleted file mode 100644 index c9947e2d43..0000000000 --- a/src/features/gallery/Gallery.tsx +++ /dev/null @@ -1,124 +0,0 @@ -import React, { - FocusEvent, - KeyboardEvent, - forwardRef, - useImperativeHandle, - useRef, - useState, -} from "react"; -import { Gallery as PhotoswipeGallery, Item } from "react-photoswipe-gallery"; - -import "photoswipe/dist/photoswipe.css"; -import { createPortal } from "react-dom"; -import styled from "@emotion/styled"; -import PhotoSwipe, { PreparedPhotoSwipeOptions } from "photoswipe"; -import { getSafeArea, isAndroid } from "../../helpers/device"; -import { v4 as uuidv4 } from "uuid"; - -const Container = styled.div` - position: absolute; - bottom: 0; - right: 0; - left: 0; - padding: 1rem; - padding-top: 4rem; - padding-bottom: calc(1rem + env(safe-area-inset-bottom, 0)); - - color: white; - background: linear-gradient(0deg, rgba(0, 0, 0, 1), transparent); -`; -interface ImgProps { - src?: string; - alt?: string; - className?: string; - onClick?: React.MouseEventHandler; - footer?: React.ReactElement; - animationType?: PreparedPhotoSwipeOptions["showHideAnimationType"]; -} - -export type GalleryHandle = { - close: () => void; -}; - -/** - * TODO: photoswipe traps focus, so onFocusCapture and onKeyDown is a hack to prevent it - * from detecting that we're changing focus. It's not great.. but it's what we got - * https://github.com/dimsemenov/PhotoSwipe/issues/1968 - */ -export const preventPhotoswipeGalleryFocusTrap = { - onFocusCapture: (e: FocusEvent) => e.stopPropagation(), - onKeyDown: (e: KeyboardEvent) => e.stopPropagation(), -}; - -export const Gallery = forwardRef(function Gallery( - { src, alt, footer, className, onClick, animationType }, - ref -) { - const [dim, setDim] = useState<{ w: number; h: number } | undefined>(); - const [actionContainer, setActionContainer] = useState( - null - ); - const photoswipeRef = useRef(); - - // Adding a ID to photoswipe creates hash URL, which allows back button - // to close the dialog - const [id] = useState(isAndroid() ? uuidv4() : undefined); - - useImperativeHandle(ref, () => ({ - close: () => { - photoswipeRef.current?.destroy(); - }, - })); - - return ( - <> - {actionContainer !== null && - footer && - createPortal({footer}, actionContainer)} - getSafeArea(), - }} - onOpen={(instance) => { - photoswipeRef.current = instance; - }} - uiElements={[ - { - appendTo: "root", - onInit: (el) => { - setActionContainer(el); - }, - }, - ]} - > - - {({ ref, open }) => ( - } - alt={alt} - onClick={(e) => { - onClick?.(e); - open(e); - }} - src={src} - className={className} - onLoad={(e) => { - if (!(e.target instanceof HTMLImageElement)) return; - - setDim({ w: e.target.naturalWidth, h: e.target.naturalHeight }); - }} - /> - )} - - - - ); -}); diff --git a/src/features/gallery/GalleryImg.tsx b/src/features/gallery/GalleryImg.tsx new file mode 100644 index 0000000000..bf7f2fcdf8 --- /dev/null +++ b/src/features/gallery/GalleryImg.tsx @@ -0,0 +1,69 @@ +import React, { FocusEvent, KeyboardEvent, useContext, useRef } from "react"; +import "photoswipe/dist/photoswipe.css"; +import { useAppDispatch } from "../../store"; +import { imageLoaded } from "./gallerySlice"; +import { PostView } from "lemmy-js-client"; +import { GalleryContext } from "./GalleryProvider"; +import { PreparedPhotoSwipeOptions } from "photoswipe"; + +export interface GalleryImgProps { + src?: string; + alt?: string; + className?: string; + post?: PostView; + animationType?: PreparedPhotoSwipeOptions["showHideAnimationType"]; +} + +/** + * TODO: photoswipe traps focus, so onFocusCapture and onKeyDown is a hack to prevent it + * from detecting that we're changing focus. It's not great.. but it's what we got + * https://github.com/dimsemenov/PhotoSwipe/issues/1968 + */ +export const preventPhotoswipeGalleryFocusTrap = { + onFocusCapture: (e: FocusEvent) => e.stopPropagation(), + onKeyDown: (e: KeyboardEvent) => e.stopPropagation(), +}; + +export function GalleryImg({ + src, + alt, + className, + post, + animationType, +}: GalleryImgProps) { + const dispatch = useAppDispatch(); + const loaded = useRef(false); + const imgRef = useRef(null); + const { open } = useContext(GalleryContext); + + return ( + {alt} { + if (!loaded.current) return; + + e.stopPropagation(); + + open(e.currentTarget, post, animationType); + }} + src={src} + className={className} + onLoad={(e) => { + if (!(e.target instanceof HTMLImageElement)) return; + if (!src) return; + + loaded.current = true; + + dispatch( + imageLoaded({ + src, + width: e.target.naturalWidth, + height: e.target.naturalHeight, + }) + ); + }} + /> + ); +} diff --git a/src/features/gallery/GalleryPostActions.tsx b/src/features/gallery/GalleryPostActions.tsx index 20553c2da7..2190872dd1 100644 --- a/src/features/gallery/GalleryPostActions.tsx +++ b/src/features/gallery/GalleryPostActions.tsx @@ -9,6 +9,8 @@ import { getHandle } from "../../helpers/lemmy"; import MoreActions from "../post/shared/MoreActions"; import { calculateCurrentVotesCount } from "../../helpers/vote"; import { useLocation } from "react-router"; +import { useContext } from "react"; +import { GalleryContext } from "./GalleryProvider"; const Container = styled.div` display: flex; @@ -32,13 +34,9 @@ const Amount = styled.div` interface GalleryPostActionsProps { post: PostView; - close: () => void; } -export default function GalleryPostActions({ - post, - close, -}: GalleryPostActionsProps) { +export default function GalleryPostActions({ post }: GalleryPostActionsProps) { const postVotesById = useAppSelector((state) => state.post.postVotesById); const buildGeneralBrowseLink = useBuildGeneralBrowseLink(); const link = buildGeneralBrowseLink( @@ -47,6 +45,7 @@ export default function GalleryPostActions({ const router = useIonRouter(); const score = calculateCurrentVotesCount(post, postVotesById); const location = useLocation(); + const { close } = useContext(GalleryContext); function share() { navigator.share({ url: post.post.ap_id }); diff --git a/src/features/gallery/GalleryProvider.tsx b/src/features/gallery/GalleryProvider.tsx new file mode 100644 index 0000000000..22f8d3afd4 --- /dev/null +++ b/src/features/gallery/GalleryProvider.tsx @@ -0,0 +1,257 @@ +import styled from "@emotion/styled"; +import React, { + createContext, + useCallback, + useEffect, + useRef, + useState, +} from "react"; +import GalleryPostActions from "./GalleryPostActions"; +import { createPortal } from "react-dom"; +import { PostView } from "lemmy-js-client"; +import PhotoSwipeLightbox, { PreparedPhotoSwipeOptions } from "photoswipe"; +import { useAppSelector } from "../../store"; +import { getSafeArea, isAndroid } from "../../helpers/device"; + +import "photoswipe/style.css"; +import { useLocation } from "react-router"; + +const Container = styled.div` + position: absolute; + bottom: 0; + right: 0; + left: 0; + padding: 1rem; + padding-top: 4rem; + padding-bottom: calc(1rem + env(safe-area-inset-bottom, 0)); + + color: white; + background: linear-gradient(0deg, rgba(0, 0, 0, 1), transparent); +`; + +interface IGalleryContext { + // used for determining whether page needs to be scrolled up first + open: ( + img: HTMLImageElement, + post?: PostView, + animationType?: PreparedPhotoSwipeOptions["showHideAnimationType"] + ) => void; + close: () => void; +} + +export const GalleryContext = createContext({ + open: () => {}, + close: () => {}, +}); + +const galleryHashEnabled = isAndroid(); +const OPEN_HASH = "galleryOpen"; + +interface GalleryProviderProps { + children: React.ReactNode; +} + +export default function GalleryProvider({ children }: GalleryProviderProps) { + const imageDimensionsBySrc = useAppSelector( + (state) => state.gallery.imageDimensionsBySrc + ); + const [actionContainer, setActionContainer] = useState( + null + ); + const [post, setPost] = useState(); + const lightboxRef = useRef(null); + const location = useLocation(); + + useEffect(() => { + return () => { + lightboxRef.current?.destroy(); + lightboxRef.current = null; + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + useEffect(() => { + if (!lightboxRef.current) return; + + lightboxRef.current.destroy(); + lightboxRef.current = null; + }, [location.pathname]); + + const close = useCallback(() => { + if (!lightboxRef.current) return; + + lightboxRef.current.close(); + }, []); + + const open = useCallback( + ( + img: HTMLImageElement, + post?: PostView, + animationType?: PreparedPhotoSwipeOptions["showHideAnimationType"] + ) => { + if (lightboxRef.current) return; + if (!imageDimensionsBySrc[img.src]) return; + + setPost(post); + + const instance = new PhotoSwipeLightbox({ + dataSource: [ + { + src: img.src, + ...imageDimensionsBySrc[img.src], + }, + ], + showHideAnimationType: animationType ?? "fade", + zoom: false, + bgOpacity: 1, + // Put in ion-app element so share IonActionSheet is on top + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + appendToEl: document.querySelector("ion-app")!, + paddingFn: () => getSafeArea(), + pswpModule: () => import("photoswipe"), + }); + + instance.addFilter("thumbEl", () => { + return img; + }); + + instance.addFilter("placeholderSrc", () => { + return img.src; + }); + + instance.on("closingAnimationEnd", () => setPost(undefined)); + + instance.on("uiRegister", function () { + instance.ui?.registerElement({ + appendTo: "root", + onInit: (el) => { + setActionContainer(el); + }, + }); + }); + + // ----------------------------- + // Android back button logic start + const getHistoryState = () => { + return { + gallery: { + open: true, + }, + }; + }; + + instance.on("beforeOpen", () => { + if (!galleryHashEnabled) { + return; + } + + const hasHash = !!getHashValue(); + + // was opened by open() method call (click on thumbnail, for example) + // we need to create new history record to store hash navigation state + if (!hasHash) { + window.history.pushState(getHistoryState(), document.title); + return; + } + + const hasGalleryStateInHistory = Boolean(window.history.state?.gallery); + + // was openned by history.forward() + // we do not need to create new history record for hash navigation + // because we already have one + if (hasGalleryStateInHistory) { + return; + } + + // was openned by link with gid and pid + const baseUrl = getBaseUrl(); + const currentHash = getHashValue(); + const urlWithoutOpenedSlide = `${baseUrl}`; + const urlWithOpenedSlide = `${baseUrl}#${currentHash}`; + + // firstly, we need to modify current history record - set url without gid and pid + // we will return to this state after photoswipe closing + window.history.replaceState( + window.history.state, + document.title, + urlWithoutOpenedSlide + ); + // then we need to create new history record to store hash navigation state + window.history.pushState( + getHistoryState(), + document.title, + urlWithOpenedSlide + ); + }); + + instance.on("change", () => { + if (!galleryHashEnabled) { + return; + } + + const baseUrl = getBaseUrl(); + const urlWithOpenedSlide = `${baseUrl}#${OPEN_HASH}`; + // updates in current history record hash value with actual pid + window.history.replaceState( + getHistoryState(), + document.title, + urlWithOpenedSlide + ); + }); + + const closeGalleryOnHistoryPopState = () => { + if (!galleryHashEnabled) { + return; + } + + if (instance !== null) { + instance.close(); + } + }; + + window.addEventListener("popstate", closeGalleryOnHistoryPopState); + + instance.on("destroy", () => { + if (galleryHashEnabled) { + window.removeEventListener("popstate", closeGalleryOnHistoryPopState); + + // if hash in URL => this destroy was called with ordinary instance.close() call + // if not => destroy was called by history.back (browser's back button) => history has been already returned to previous state + if (getHashValue()) { + window.history.back(); + } + } + lightboxRef.current = null; + }); + // Android back button logic end + // ----------------------------- + + instance.init(); + lightboxRef.current = instance; + }, + [imageDimensionsBySrc] + ); + + return ( + + {actionContainer !== null && + post && + createPortal( + + + , + actionContainer + )} + + {children} + + ); +} + +function getBaseUrl(): string { + return `${window.location.pathname}${window.location.search}`; +} + +function getHashValue(): string { + return window.location.hash.substring(1); +} diff --git a/src/features/gallery/PostGallery.tsx b/src/features/gallery/PostGallery.tsx deleted file mode 100644 index d4ec9df009..0000000000 --- a/src/features/gallery/PostGallery.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import { useMemo, useRef } from "react"; -import GalleryPostActions from "./GalleryPostActions"; -import { Gallery, GalleryHandle } from "./Gallery"; -import { PostView } from "lemmy-js-client"; -import { findLoneImage } from "../../helpers/markdown"; -import { isUrlImage } from "../../helpers/lemmy"; -import { PreparedPhotoSwipeOptions } from "photoswipe"; - -interface PostGalleryProps { - post: PostView; - - className?: string; - animationType?: PreparedPhotoSwipeOptions["showHideAnimationType"]; -} - -export default function PostGallery({ - post, - className, - animationType, -}: PostGalleryProps) { - const galleryRef = useRef(null); - - const images = useMemo(() => getImages(post), [post]); - - if (!images) return null; - - return ( - e.stopPropagation()} - ref={galleryRef} - src={images[0]} - footer={ - galleryRef.current?.close()} - /> - } - animationType={animationType} - className={className} - /> - ); -} - -function getImages(post: PostView): string[] | undefined { - if (post.post.url && isUrlImage(post.post.url)) return [post.post.url]; - - const loneImage = post.post.body && findLoneImage(post.post.body); - if (loneImage) return [loneImage.url]; -} diff --git a/src/features/gallery/PostGalleryImg.tsx b/src/features/gallery/PostGalleryImg.tsx new file mode 100644 index 0000000000..e05737d23d --- /dev/null +++ b/src/features/gallery/PostGalleryImg.tsx @@ -0,0 +1,22 @@ +import { PostView } from "lemmy-js-client"; +import { isUrlImage } from "../../helpers/lemmy"; +import { findLoneImage } from "../../helpers/markdown"; +import { GalleryImg, GalleryImgProps } from "./GalleryImg"; + +export interface PostGalleryImgProps extends Omit { + post: PostView; +} + +export default function PostGalleryImg({ + post, + ...props +}: PostGalleryImgProps) { + return ; +} + +function getPostImage(post: PostView): string | undefined { + if (post.post.url && isUrlImage(post.post.url)) return post.post.url; + + const loneImage = post.post.body && findLoneImage(post.post.body); + if (loneImage) return loneImage.url; +} diff --git a/src/features/gallery/gallerySlice.tsx b/src/features/gallery/gallerySlice.tsx new file mode 100644 index 0000000000..8734e7f1b7 --- /dev/null +++ b/src/features/gallery/gallerySlice.tsx @@ -0,0 +1,34 @@ +import { Dictionary, PayloadAction, createSlice } from "@reduxjs/toolkit"; + +interface ImageDimension { + width: number; + height: number; +} + +interface Image extends ImageDimension { + src: string; +} + +interface CommentState { + imageDimensionsBySrc: Dictionary; +} + +const initialState: CommentState = { + imageDimensionsBySrc: {}, +}; + +export const gallerySlice = createSlice({ + name: "gallery", + initialState, + reducers: { + imageLoaded: (state, action: PayloadAction) => { + state.imageDimensionsBySrc[action.payload.src] = action.payload; + }, + resetGallery: () => initialState, + }, +}); + +// Action creators are generated for each case reducer function +export const { imageLoaded } = gallerySlice.actions; + +export default gallerySlice.reducer; diff --git a/src/features/post/detail/PostDetail.tsx b/src/features/post/detail/PostDetail.tsx index 59f7cdf351..a6c1ef2a2b 100644 --- a/src/features/post/detail/PostDetail.tsx +++ b/src/features/post/detail/PostDetail.tsx @@ -37,7 +37,7 @@ import CommentSort from "../../comment/CommentSort"; import Nsfw, { isNsfw } from "../../labels/Nsfw"; import { PageContext } from "../../auth/PageContext"; import MoreActions from "../shared/MoreActions"; -import PostGallery from "../../gallery/PostGallery"; +import PostGalleryImg from "../../gallery/PostGalleryImg"; const BorderlessIonItem = styled(IonItem)` --padding-start: 0; @@ -65,7 +65,7 @@ const lightboxCss = css` background: var(--lightroom-bg); `; -const LightboxImg = styled(PostGallery)` +const LightboxImg = styled(PostGalleryImg)` ${lightboxCss} `; diff --git a/src/features/post/inFeed/compact/Thumbnail.tsx b/src/features/post/inFeed/compact/Thumbnail.tsx index 07a7d0532f..15b111afa1 100644 --- a/src/features/post/inFeed/compact/Thumbnail.tsx +++ b/src/features/post/inFeed/compact/Thumbnail.tsx @@ -6,7 +6,7 @@ import { useMemo } from "react"; import { findLoneImage } from "../../../../helpers/markdown"; import { css } from "@emotion/react"; import { isNsfw } from "../../../labels/Nsfw"; -import PostGallery from "../../../gallery/PostGallery"; +import PostGalleryImg from "../../../gallery/PostGalleryImg"; const Container = styled.div` display: flex; @@ -28,7 +28,7 @@ const Container = styled.div` } `; -const StyledImg = styled(PostGallery)<{ blur: boolean }>` +const StyledImg = styled(PostGalleryImg)<{ blur: boolean }>` width: 100%; height: 100%; object-fit: cover; @@ -63,7 +63,11 @@ export default function Thumbnail({ post }: ImgProps) { return ( src && e.stopPropagation()}> - {src ? : } + {src ? ( + + ) : ( + + )} ); } diff --git a/src/features/post/inFeed/large/Image.tsx b/src/features/post/inFeed/large/Image.tsx index 9192dcfcfa..5f8c49895e 100644 --- a/src/features/post/inFeed/large/Image.tsx +++ b/src/features/post/inFeed/large/Image.tsx @@ -1,10 +1,12 @@ import styled from "@emotion/styled"; -import PostGallery from "../../../gallery/PostGallery"; import { css } from "@emotion/react"; +import PostGalleryImg from "../../../gallery/PostGalleryImg"; -export const Image = styled(PostGallery)<{ blur: boolean }>` +export const Image = styled(PostGalleryImg)<{ blur: boolean }>` width: 100%; max-width: none; + max-height: max(200vh, 2000px); + object-fit: contain; ${({ blur }) => blur && diff --git a/src/features/shared/Markdown.tsx b/src/features/shared/Markdown.tsx index af0a7bb574..2ef08b3c81 100644 --- a/src/features/shared/Markdown.tsx +++ b/src/features/shared/Markdown.tsx @@ -8,7 +8,7 @@ import LinkInterceptor from "./markdown/LinkInterceptor"; import buildCommunityPlugin from "./markdown/buildCommunityPlugin"; import customRemarkGfm from "./markdown/customRemarkGfm"; import { useMemo } from "react"; -import { Gallery } from "../gallery/Gallery"; +import { GalleryImg } from "../gallery/GalleryImg"; const Blockquote = styled.blockquote` padding-left: 0.5rem; @@ -64,13 +64,7 @@ export default function Markdown(props: ReactMarkdownOptions) { linkTarget="_blank" {...props} components={{ - img: (props) => ( - e.stopPropagation()} - animationType="zoom" - /> - ), + img: (props) => , blockquote: (props) =>
, code: (props) => , table: (props) => ( diff --git a/src/index.css b/src/index.css index c4f2e128e2..27cba5cbe7 100644 --- a/src/index.css +++ b/src/index.css @@ -94,7 +94,7 @@ ion-action-sheet ion-icon { margin-inline-end: 16px !important; } -/* react-photoswipe */ +/* photoswipe */ .pswp__top-bar { top: env(safe-area-inset-top, 0); diff --git a/src/store.ts b/src/store.ts index e72e8e7c8b..884d39b7fd 100644 --- a/src/store.ts +++ b/src/store.ts @@ -6,6 +6,7 @@ import commentSlice from "./features/comment/commentSlice"; import communitySlice from "./features/community/communitySlice"; import userSlice from "./features/user/userSlice"; import inboxSlice from "./features/inbox/inboxSlice"; +import gallerySlice from "./features/gallery/gallerySlice"; import appearanceSlice, { fetchSettingsFromDatabase, } from "./features/settings/appearance/appearanceSlice"; @@ -19,6 +20,7 @@ const store = configureStore({ user: userSlice, inbox: inboxSlice, appearance: appearanceSlice, + gallery: gallerySlice, }, }); export type RootState = ReturnType; diff --git a/yarn.lock b/yarn.lock index 966e16ac2c..7104934ea4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5378,10 +5378,10 @@ pend@~1.2.0: resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg== -photoswipe@^5.3.7: - version "5.3.7" - resolved "https://registry.yarnpkg.com/photoswipe/-/photoswipe-5.3.7.tgz#c67df67aaddb5705bcf8ff265bd2086f57805756" - integrity sha512-zsyLsTTLFrj0XR1m4/hO7qNooboFKUrDy+Zt5i2d6qjFPAtBjzaj/Xtydso4uxzcXpcqbTmyxDibb3BcSISseg== +photoswipe@^5.3.8: + version "5.3.8" + resolved "https://registry.yarnpkg.com/photoswipe/-/photoswipe-5.3.8.tgz#bea7db18e79383833f85c005b833da2029f0daee" + integrity sha512-4vTzOQt8GP4Chsm0s+8j2xDtVHAEN252PxrU12A1zXauNn0zD5HRHgjALKO2GKTyBnTnOrJUOxbV8LTrFIMrYw== picocolors@^1.0.0: version "1.0.0" @@ -5661,11 +5661,6 @@ react-markdown@^8.0.7: unist-util-visit "^4.0.0" vfile "^5.0.0" -react-photoswipe-gallery@^2.2.7: - version "2.2.7" - resolved "https://registry.yarnpkg.com/react-photoswipe-gallery/-/react-photoswipe-gallery-2.2.7.tgz#27fd45e84a3b7f15846761830c600b2c719053e3" - integrity sha512-AEYNoL4/IIRosIUonn4haaFQNn1ui4vdVgAY9LHd/imVamNCkqUcyWeT6317UILp/yJI2gohsd3lWmcJEbjCag== - react-redux@^8.1.1: version "8.1.1" resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-8.1.1.tgz#8e740f3fd864a4cd0de5ba9cdc8ad39cc9e7c81a"