diff --git a/CHANGELOG.md b/CHANGELOG.md index 1df80f197..83c44b1c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Added +- Zoomable image viewer in Markdown (posts and comments) _community pr!_ ([#5387](https://github.com/lbryio/lbry-desktop/pull/5387)) + ### Changed ### Fixed diff --git a/package.json b/package.json index a5ebfbd21..cb41c2fc7 100644 --- a/package.json +++ b/package.json @@ -162,6 +162,7 @@ "raw-loader": "^2.0.0", "rc-progress": "^2.0.6", "react": "^16.8.2", + "react-awesome-lightbox": "^1.7.3", "react-confetti": "^4.0.1", "react-dom": "^16.8.2", "react-draggable": "^3.3.0", diff --git a/ui/component/common/markdown-preview.jsx b/ui/component/common/markdown-preview.jsx index 4c1b94681..1242624d5 100644 --- a/ui/component/common/markdown-preview.jsx +++ b/ui/component/common/markdown-preview.jsx @@ -12,6 +12,7 @@ import MarkdownLink from 'component/markdownLink'; import defaultSchema from 'hast-util-sanitize/lib/github.json'; import { formatedLinks, inlineLinks } from 'util/remark-lbry'; import { formattedTimestamp, inlineTimestamp } from 'util/remark-timestamp'; +import ZoomableImage from 'component/zoomableImage'; type SimpleTextProps = { children?: React.Node, @@ -33,10 +34,16 @@ type MarkdownProps = { isMarkdownPost?: boolean, }; +// **************************************************************************** +// **************************************************************************** + const SimpleText = (props: SimpleTextProps) => { return {props.children}; }; +// **************************************************************************** +// **************************************************************************** + const SimpleLink = (props: SimpleLinkProps) => { const { title, children, href } = props; @@ -70,6 +77,9 @@ const SimpleLink = (props: SimpleLinkProps) => { return {children}; }; +// **************************************************************************** +// **************************************************************************** + // Use github sanitation schema const schema = { ...defaultSchema }; @@ -79,6 +89,9 @@ schema.attributes.a.push('embed'); const REPLACE_REGEX = /(<\/iframe>)/g; +// **************************************************************************** +// **************************************************************************** + const MarkdownPreview = (props: MarkdownProps) => { const { content, strip, simpleLinks, noDataStore, className, parentCommentId, isMarkdownPost } = props; const strippedContent = content @@ -116,6 +129,7 @@ const MarkdownPreview = (props: MarkdownProps) => { ), // Workaraund of remarkOptions.Fragment div: React.Fragment, + img: ZoomableImage, }, }; diff --git a/ui/component/zoomableImage/index.js b/ui/component/zoomableImage/index.js new file mode 100644 index 000000000..a4293feb0 --- /dev/null +++ b/ui/component/zoomableImage/index.js @@ -0,0 +1,9 @@ +import { connect } from 'react-redux'; +import { doOpenModal } from 'redux/actions/app'; +import ZoomableImage from './view'; + +const perform = dispatch => ({ + openModal: (modal, props) => dispatch(doOpenModal(modal, props)), +}); + +export default connect(null, perform)(ZoomableImage); diff --git a/ui/component/zoomableImage/view.jsx b/ui/component/zoomableImage/view.jsx new file mode 100644 index 000000000..737315403 --- /dev/null +++ b/ui/component/zoomableImage/view.jsx @@ -0,0 +1,20 @@ +// @flow +import React from 'react'; +import * as MODALS from 'constants/modal_types'; + +type Props = { + openModal: (string, {}) => void, +}; + +function ZoomableImage(props: Props) { + const { openModal, ...imgProps } = props; + + const onClick = () => { + // $FlowFixMe + openModal(MODALS.VIEW_IMAGE, { src: imgProps.src, title: imgProps.title || imgProps.alt }); + }; + + return ; +} + +export default ZoomableImage; diff --git a/ui/constants/modal_types.js b/ui/constants/modal_types.js index 8b6a0b4f9..6d677dbf8 100644 --- a/ui/constants/modal_types.js +++ b/ui/constants/modal_types.js @@ -44,3 +44,4 @@ export const SYNC_ENABLE = 'SYNC_ENABLE'; export const REMOVE_BLOCKED = 'remove_blocked'; export const IMAGE_UPLOAD = 'image_upload'; export const MOBILE_SEARCH = 'mobile_search'; +export const VIEW_IMAGE = 'view_image'; diff --git a/ui/modal/modalRouter/view.jsx b/ui/modal/modalRouter/view.jsx index bb16b56b0..ba72f3a9d 100644 --- a/ui/modal/modalRouter/view.jsx +++ b/ui/modal/modalRouter/view.jsx @@ -42,6 +42,7 @@ import ModalFileSelection from 'modal/modalFileSelection'; import ModalSyncEnable from 'modal/modalSyncEnable'; import ModalImageUpload from 'modal/modalImageUpload'; import ModalMobileSearch from 'modal/modalMobileSearch'; +import ModalViewImage from 'modal/modalViewImage'; type Props = { modal: { id: string, modalProps: {} }, @@ -151,6 +152,8 @@ function ModalRouter(props: Props) { return ; case MODALS.MOBILE_SEARCH: return ; + case MODALS.VIEW_IMAGE: + return ; default: return null; } diff --git a/ui/modal/modalViewImage/index.js b/ui/modal/modalViewImage/index.js new file mode 100644 index 000000000..d02f4806f --- /dev/null +++ b/ui/modal/modalViewImage/index.js @@ -0,0 +1,9 @@ +import { connect } from 'react-redux'; +import { doHideModal } from 'redux/actions/app'; +import ModalViewImage from './view'; + +const perform = dispatch => ({ + closeModal: () => dispatch(doHideModal()), +}); + +export default connect(null, perform)(ModalViewImage); diff --git a/ui/modal/modalViewImage/view.jsx b/ui/modal/modalViewImage/view.jsx new file mode 100644 index 000000000..b0967e22c --- /dev/null +++ b/ui/modal/modalViewImage/view.jsx @@ -0,0 +1,21 @@ +// @flow +import React from 'react'; +import { Modal } from 'modal/modal'; +import Lightbox from 'react-awesome-lightbox'; +import 'react-awesome-lightbox/build/style.css'; + +type Props = { + src: string, + title: String, + closeModal: () => void, +}; + +export default function ModalMobileSearch(props: Props) { + const { src, title, closeModal } = props; + + return ( + + + + ); +} diff --git a/ui/scss/component/_markdown-preview.scss b/ui/scss/component/_markdown-preview.scss index cf2f4b3d2..21c77aab6 100644 --- a/ui/scss/component/_markdown-preview.scss +++ b/ui/scss/component/_markdown-preview.scss @@ -128,6 +128,10 @@ } } + .img__zoomable { + cursor: pointer; // 'zoom-in' would be ideal, but browser-dependant. + } + // Horizontal Rule hr { margin-bottom: 2rem; diff --git a/yarn.lock b/yarn.lock index 7fdcb1670..698962f1a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9748,6 +9748,11 @@ react-async-script@^1.1.1: hoist-non-react-statics "^3.3.0" prop-types "^15.5.0" +react-awesome-lightbox@^1.7.3: + version "1.7.3" + resolved "https://registry.yarnpkg.com/react-awesome-lightbox/-/react-awesome-lightbox-1.7.3.tgz#ee1c00fd4197e0e65bf996aa219eac4d8b6db5a0" + integrity sha512-mSxdL3KGzuh2eR8I00nv9njiolmMoXITuCvfd71DBXK13JW3e+Z/sCMENS9+dngBJU8/m7dR1Ix0W6afS5cFsA== + react-compound-slider@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/react-compound-slider/-/react-compound-slider-2.5.0.tgz#99771a3397f4ab00aa2a37f8410da87e6ca2449b"