Zoomable image viewer in Markdown (posts and comments)
## Issue 4899: Ability to expand images in markdown posts for viewing
This commit is contained in:
parent
153ebbca63
commit
7419fefa2d
11 changed files with 89 additions and 0 deletions
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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 <span>{props.children}</span>;
|
||||
};
|
||||
|
||||
// ****************************************************************************
|
||||
// ****************************************************************************
|
||||
|
||||
const SimpleLink = (props: SimpleLinkProps) => {
|
||||
const { title, children, href } = props;
|
||||
|
||||
|
@ -70,6 +77,9 @@ const SimpleLink = (props: SimpleLinkProps) => {
|
|||
return <a title={title}>{children}</a>;
|
||||
};
|
||||
|
||||
// ****************************************************************************
|
||||
// ****************************************************************************
|
||||
|
||||
// Use github sanitation schema
|
||||
const schema = { ...defaultSchema };
|
||||
|
||||
|
@ -79,6 +89,9 @@ schema.attributes.a.push('embed');
|
|||
|
||||
const REPLACE_REGEX = /(<iframe\s+src=["'])(.*?(?=))(["']\s*><\/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,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
9
ui/component/zoomableImage/index.js
Normal file
9
ui/component/zoomableImage/index.js
Normal file
|
@ -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);
|
20
ui/component/zoomableImage/view.jsx
Normal file
20
ui/component/zoomableImage/view.jsx
Normal file
|
@ -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 <img className="img__zoomable" {...imgProps} onClick={onClick} />;
|
||||
}
|
||||
|
||||
export default ZoomableImage;
|
|
@ -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';
|
||||
|
|
|
@ -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 <ModalSyncEnable {...modalProps} />;
|
||||
case MODALS.MOBILE_SEARCH:
|
||||
return <ModalMobileSearch {...modalProps} />;
|
||||
case MODALS.VIEW_IMAGE:
|
||||
return <ModalViewImage {...modalProps} />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
|
9
ui/modal/modalViewImage/index.js
Normal file
9
ui/modal/modalViewImage/index.js
Normal file
|
@ -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);
|
21
ui/modal/modalViewImage/view.jsx
Normal file
21
ui/modal/modalViewImage/view.jsx
Normal file
|
@ -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 (
|
||||
<Modal onAborted={closeModal} isOpen type="custom">
|
||||
<Lightbox image={src} title={title} onClose={closeModal} />
|
||||
</Modal>
|
||||
);
|
||||
}
|
|
@ -128,6 +128,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
.img__zoomable {
|
||||
cursor: pointer; // 'zoom-in' would be ideal, but browser-dependant.
|
||||
}
|
||||
|
||||
// Horizontal Rule
|
||||
hr {
|
||||
margin-bottom: 2rem;
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in a new issue