markdown fixes - blockquotes and embedded videos
This commit is contained in:
parent
c553ee46f6
commit
a574a5c1de
6 changed files with 127 additions and 43 deletions
|
@ -1,24 +1,24 @@
|
||||||
// @flow
|
// @flow
|
||||||
import * as React from 'react';
|
import { CHANNEL_STAKED_LEVEL_VIDEO_COMMENTS } from 'config';
|
||||||
import classnames from 'classnames';
|
|
||||||
import remark from 'remark';
|
|
||||||
import remarkAttr from 'remark-attr';
|
|
||||||
import remarkStrip from 'strip-markdown';
|
|
||||||
import remarkEmoji from 'remark-emoji';
|
|
||||||
import remarkBreaks from 'remark-breaks';
|
|
||||||
import remarkFrontMatter from 'remark-frontmatter';
|
|
||||||
import reactRenderer from 'remark-react';
|
|
||||||
import MarkdownLink from 'component/markdownLink';
|
|
||||||
import defaultSchema from 'hast-util-sanitize/lib/github.json';
|
|
||||||
import { formattedLinks, inlineLinks } from 'util/remark-lbry';
|
import { formattedLinks, inlineLinks } from 'util/remark-lbry';
|
||||||
import { formattedTimestamp, inlineTimestamp } from 'util/remark-timestamp';
|
import { formattedTimestamp, inlineTimestamp } from 'util/remark-timestamp';
|
||||||
import { formattedEmote, inlineEmote } from 'util/remark-emote';
|
import { formattedEmote, inlineEmote } from 'util/remark-emote';
|
||||||
import ZoomableImage from 'component/zoomableImage';
|
|
||||||
import { CHANNEL_STAKED_LEVEL_VIDEO_COMMENTS } from 'config';
|
|
||||||
import Button from 'component/button';
|
|
||||||
import * as ICONS from 'constants/icons';
|
import * as ICONS from 'constants/icons';
|
||||||
import { parse } from 'node-html-parser';
|
import * as React from 'react';
|
||||||
|
import Button from 'component/button';
|
||||||
|
import classnames from 'classnames';
|
||||||
|
import defaultSchema from 'hast-util-sanitize/lib/github.json';
|
||||||
|
import MarkdownLink from 'component/markdownLink';
|
||||||
import OptimizedImage from 'component/optimizedImage';
|
import OptimizedImage from 'component/optimizedImage';
|
||||||
|
import reactRenderer from 'remark-react';
|
||||||
|
import remark from 'remark';
|
||||||
|
import remarkAttr from 'remark-attr';
|
||||||
|
import remarkBreaks from 'remark-breaks';
|
||||||
|
import remarkEmoji from 'remark-emoji';
|
||||||
|
import remarkFrontMatter from 'remark-frontmatter';
|
||||||
|
import remarkStrip from 'strip-markdown';
|
||||||
|
import ZoomableImage from 'component/zoomableImage';
|
||||||
|
import { parse } from 'node-html-parser';
|
||||||
|
|
||||||
const RE_EMOTE = /:\+1:|:-1:|:[\w-]+:/;
|
const RE_EMOTE = /:\+1:|:-1:|:[\w-]+:/;
|
||||||
|
|
||||||
|
@ -171,17 +171,20 @@ const MarkdownPreview = (props: MarkdownProps) => {
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
if (lbrySrc && lbrySrc.startsWith('lbry://')) {
|
if (lbrySrc && lbrySrc.startsWith('lbry://')) {
|
||||||
|
console.log('lbrysrc', lbrySrc);
|
||||||
return lbrySrc;
|
return lbrySrc;
|
||||||
}
|
}
|
||||||
|
//
|
||||||
|
// if (lbrySrc && lbrySrc.startsWith('https://odysee.com')) {
|
||||||
|
// console.log('lbrysrc', lbrySrc)
|
||||||
|
// return lbrySrc;
|
||||||
|
// }
|
||||||
|
// console.log('iframeh', iframeHtml)
|
||||||
|
|
||||||
return iframeHtml;
|
return iframeHtml;
|
||||||
})
|
})
|
||||||
: '';
|
: '';
|
||||||
|
|
||||||
const initialQuote = strippedContent.split(' ').find((word) => word.length > 0 || word.charAt(0) === '>');
|
|
||||||
let stripQuote;
|
|
||||||
if (initialQuote && initialQuote.charAt(0) === '>') stripQuote = true;
|
|
||||||
|
|
||||||
const remarkOptions: Object = {
|
const remarkOptions: Object = {
|
||||||
sanitize: schema,
|
sanitize: schema,
|
||||||
fragment: React.Fragment,
|
fragment: React.Fragment,
|
||||||
|
@ -216,22 +219,10 @@ const MarkdownPreview = (props: MarkdownProps) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Strip all content and just render text
|
// Strip all content and just render text
|
||||||
if (strip || stripQuote) {
|
if (strip) {
|
||||||
// Remove new lines and extra space
|
// Remove new lines and extra space
|
||||||
remarkOptions.remarkReactComponents.p = SimpleText;
|
remarkOptions.remarkReactComponents.p = SimpleText;
|
||||||
return stripQuote ? (
|
return (
|
||||||
<span dir="auto" className="markdown-preview">
|
|
||||||
<blockquote>
|
|
||||||
{
|
|
||||||
remark()
|
|
||||||
.use(remarkStrip)
|
|
||||||
.use(remarkFrontMatter, ['yaml'])
|
|
||||||
.use(reactRenderer, remarkOptions)
|
|
||||||
.processSync(content).contents
|
|
||||||
}
|
|
||||||
</blockquote>
|
|
||||||
</span>
|
|
||||||
) : (
|
|
||||||
<span dir="auto" className="markdown-preview">
|
<span dir="auto" className="markdown-preview">
|
||||||
{
|
{
|
||||||
remark()
|
remark()
|
||||||
|
|
|
@ -24,6 +24,7 @@ import PdfViewer from 'component/viewers/pdfViewer';
|
||||||
type Props = {
|
type Props = {
|
||||||
uri: string,
|
uri: string,
|
||||||
streamingUrl: string,
|
streamingUrl: string,
|
||||||
|
embedded?: boolean,
|
||||||
contentType: string,
|
contentType: string,
|
||||||
claim: StreamClaim,
|
claim: StreamClaim,
|
||||||
currentTheme: string,
|
currentTheme: string,
|
||||||
|
@ -44,8 +45,9 @@ class FileRender extends React.PureComponent<Props> {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
const { embedded } = this.props;
|
||||||
window.addEventListener('keydown', this.escapeListener, true);
|
window.addEventListener('keydown', this.escapeListener, true);
|
||||||
analytics.playerLoadedEvent();
|
analytics.playerLoadedEvent(embedded);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
|
@ -144,12 +146,13 @@ class FileRender extends React.PureComponent<Props> {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { renderMode, className } = this.props;
|
const { embedded, renderMode, className } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={classnames('file-render', className, {
|
className={classnames('file-render', className, {
|
||||||
'file-render--document': RENDER_MODES.TEXT_MODES.includes(renderMode),
|
'file-render--document': RENDER_MODES.TEXT_MODES.includes(renderMode),
|
||||||
|
'file-render--embed': embedded,
|
||||||
'file-render--video': renderMode === RENDER_MODES.VIDEO || renderMode === RENDER_MODES.AUDIO,
|
'file-render--video': renderMode === RENDER_MODES.VIDEO || renderMode === RENDER_MODES.AUDIO,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
|
|
|
@ -69,3 +69,4 @@
|
||||||
@import 'component/swipe-list';
|
@import 'component/swipe-list';
|
||||||
@import 'component/utils';
|
@import 'component/utils';
|
||||||
@import 'component/settings';
|
@import 'component/settings';
|
||||||
|
@import 'component/embed-player';
|
||||||
|
|
67
ui/scss/component/_embed-player.scss
Normal file
67
ui/scss/component/_embed-player.scss
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
.embed__wrapper {
|
||||||
|
height: 100vh;
|
||||||
|
width: 100vw;
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
background-color: var(--color-black);
|
||||||
|
}
|
||||||
|
|
||||||
|
.embed__wrapper--light-background {
|
||||||
|
@extend .embed__wrapper;
|
||||||
|
|
||||||
|
.vjs-poster,
|
||||||
|
video {
|
||||||
|
background-color: var(--color-white);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.embed__inline-button {
|
||||||
|
@include thumbnail;
|
||||||
|
position: relative;
|
||||||
|
background-position: 50% 50%;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: 100%;
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
border-top-left-radius: var(--border-radius);
|
||||||
|
border-top-right-radius: var(--border-radius);
|
||||||
|
background-color: var(--color-black);
|
||||||
|
|
||||||
|
@media (max-width: $breakpoint-small) {
|
||||||
|
height: 200px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.embed__inline-button-preview {
|
||||||
|
@extend .embed__inline-button;
|
||||||
|
background-color: var(--color-editor-inline-code-bg);
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.embed__loading {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.embed__loading-text {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: var(--color-white);
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: var(--font-large);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.embed__overlay-logo {
|
||||||
|
max-height: 2rem;
|
||||||
|
max-width: 7rem;
|
||||||
|
}
|
|
@ -12,13 +12,19 @@ const invalidRegex = /[-_.+=?!@#$%^&*:;,{}<>\w/\\]/;
|
||||||
const mentionRegex = /@[^\s()"-_.+=?!@$%^&*;,{}<>/\\]*/gm;
|
const mentionRegex = /@[^\s()"-_.+=?!@$%^&*;,{}<>/\\]*/gm;
|
||||||
|
|
||||||
function handlePunctuation(value) {
|
function handlePunctuation(value) {
|
||||||
const modifierIndex =
|
const protocolIndex = value.indexOf('lbry://') === 0 ? protocol.length - 1 : 0;
|
||||||
(value.indexOf(':') >= 0 && value.indexOf(':')) || (value.indexOf('#') >= 0 && value.indexOf('#'));
|
const channelModifierIndex =
|
||||||
|
(value.indexOf(':', protocolIndex) >= 0 && value.indexOf(':', protocolIndex)) ||
|
||||||
|
(value.indexOf('#', protocolIndex) >= 0 && value.indexOf('#', protocolIndex));
|
||||||
|
const claimModifierIndex =
|
||||||
|
(value.indexOf(':', channelModifierIndex + 1) >= 0 && value.indexOf(':', channelModifierIndex + 1)) ||
|
||||||
|
(value.indexOf('#', channelModifierIndex + 1) >= 0 && value.indexOf('#', channelModifierIndex + 1)) ||
|
||||||
|
channelModifierIndex;
|
||||||
|
|
||||||
let punctuationIndex;
|
let punctuationIndex;
|
||||||
punctuationMarks.some((p) => {
|
punctuationMarks.some((p) => {
|
||||||
if (modifierIndex) {
|
if (claimModifierIndex) {
|
||||||
punctuationIndex = value.indexOf(p, modifierIndex + 1) >= 0 && value.indexOf(p, modifierIndex + 1);
|
punctuationIndex = value.indexOf(p, claimModifierIndex + 1) >= 0 && value.indexOf(p, claimModifierIndex + 1);
|
||||||
}
|
}
|
||||||
return punctuationIndex;
|
return punctuationIndex;
|
||||||
});
|
});
|
||||||
|
@ -108,15 +114,15 @@ function tokenizeURI(eat, value, silent) {
|
||||||
|
|
||||||
// Configure tokenizer for lbry urls
|
// Configure tokenizer for lbry urls
|
||||||
tokenizeURI.locator = locateURI;
|
tokenizeURI.locator = locateURI;
|
||||||
tokenizeURI.notInList = true;
|
tokenizeURI.notInList = false;
|
||||||
tokenizeURI.notInLink = true;
|
tokenizeURI.notInLink = true;
|
||||||
tokenizeURI.notInBlock = true;
|
tokenizeURI.notInBlock = false;
|
||||||
|
|
||||||
// Configure tokenizer for lbry channels
|
// Configure tokenizer for lbry channels
|
||||||
tokenizeMention.locator = locateMention;
|
tokenizeMention.locator = locateMention;
|
||||||
tokenizeMention.notInList = true;
|
tokenizeMention.notInList = false;
|
||||||
tokenizeMention.notInLink = true;
|
tokenizeMention.notInLink = true;
|
||||||
tokenizeMention.notInBlock = true;
|
tokenizeMention.notInBlock = false;
|
||||||
|
|
||||||
const visitor = (node, index, parent) => {
|
const visitor = (node, index, parent) => {
|
||||||
if (node.type === 'link' && parent && parent.type === 'paragraph') {
|
if (node.type === 'link' && parent && parent.type === 'paragraph') {
|
||||||
|
|
16
web/middleware/iframe-destroyer.js
Normal file
16
web/middleware/iframe-destroyer.js
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
const PAGES = require('../../ui/constants/pages');
|
||||||
|
|
||||||
|
async function iframeDestroyerMiddleware(ctx, next) {
|
||||||
|
const {
|
||||||
|
request: { path },
|
||||||
|
} = ctx;
|
||||||
|
const decodedPath = decodeURIComponent(path);
|
||||||
|
|
||||||
|
if (!decodedPath.startsWith(`/$/${PAGES.EMBED}`)) {
|
||||||
|
ctx.set('X-Frame-Options', 'DENY');
|
||||||
|
}
|
||||||
|
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = iframeDestroyerMiddleware;
|
Loading…
Reference in a new issue