mirror of
https://github.com/muerwre/vault-frontend.git
synced 2025-04-25 04:46:40 +07:00
commit
9d8346b315
14 changed files with 249 additions and 73 deletions
5
.env
5
.env
|
@ -1,5 +0,0 @@
|
||||||
#API_HOST = http://localhost:7777/
|
|
||||||
#REMOTE_CURRENT = http://localhost:7777/static/
|
|
||||||
REACT_APP_API_HOST = https://pig.staging.vault48.org/
|
|
||||||
REACT_APP_REMOTE_CURRENT = https://pig.staging.vault48.org/static/
|
|
||||||
EXPOSE = 4000
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -2,3 +2,4 @@
|
||||||
/npm-debug.log
|
/npm-debug.log
|
||||||
/.idea
|
/.idea
|
||||||
/dist
|
/dist
|
||||||
|
/.env
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
"connected-react-router": "^6.5.2",
|
"connected-react-router": "^6.5.2",
|
||||||
"date-fns": "^2.4.1",
|
"date-fns": "^2.4.1",
|
||||||
"flexbin": "^0.2.0",
|
"flexbin": "^0.2.0",
|
||||||
|
"marked": "^2.0.0",
|
||||||
"node-sass": "4.14.1",
|
"node-sass": "4.14.1",
|
||||||
"photoswipe": "^4.1.3",
|
"photoswipe": "^4.1.3",
|
||||||
"raleway-cyrillic": "^4.0.2",
|
"raleway-cyrillic": "^4.0.2",
|
||||||
|
@ -62,6 +63,7 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@craco/craco": "5.8.0",
|
"@craco/craco": "5.8.0",
|
||||||
"@types/classnames": "^2.2.7",
|
"@types/classnames": "^2.2.7",
|
||||||
|
"@types/marked": "^1.2.2",
|
||||||
"@types/node": "^11.13.22",
|
"@types/node": "^11.13.22",
|
||||||
"@types/ramda": "^0.26.33",
|
"@types/ramda": "^0.26.33",
|
||||||
"@types/react-redux": "^7.1.11",
|
"@types/react-redux": "^7.1.11",
|
||||||
|
|
|
@ -49,6 +49,14 @@ const CommentContent: FC<IProps> = memo(
|
||||||
[can_edit, comment, onEditClick, onLockClick]
|
[can_edit, comment, onEditClick, onLockClick]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const blocks = useMemo(
|
||||||
|
() =>
|
||||||
|
!!comment.text.trim()
|
||||||
|
? formatCommentText(path(['user', 'username'], comment), comment.text)
|
||||||
|
: [],
|
||||||
|
[comment.text]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.wrap}>
|
<div className={styles.wrap}>
|
||||||
{comment.text && (
|
{comment.text && (
|
||||||
|
@ -56,7 +64,7 @@ const CommentContent: FC<IProps> = memo(
|
||||||
{menu}
|
{menu}
|
||||||
|
|
||||||
<Group className={styles.renderers}>
|
<Group className={styles.renderers}>
|
||||||
{formatCommentText(path(['user', 'username'], comment), comment.text).map(
|
{blocks.map(
|
||||||
(block, key) =>
|
(block, key) =>
|
||||||
COMMENT_BLOCK_RENDERERS[block.type] &&
|
COMMENT_BLOCK_RENDERERS[block.type] &&
|
||||||
createElement(COMMENT_BLOCK_RENDERERS[block.type], { block, key })
|
createElement(COMMENT_BLOCK_RENDERERS[block.type], { block, key })
|
||||||
|
|
|
@ -1,15 +1,20 @@
|
||||||
import React, { FC } from 'react';
|
import React, { FC, useMemo } from 'react';
|
||||||
import { ICommentBlockProps } from '~/constants/comment';
|
import { ICommentBlockProps } from '~/constants/comment';
|
||||||
import styles from './styles.module.scss';
|
import styles from './styles.module.scss';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import markdown from '~/styles/common/markdown.module.scss';
|
||||||
|
import { formatText } from '~/utils/dom';
|
||||||
|
|
||||||
interface IProps extends ICommentBlockProps {}
|
interface IProps extends ICommentBlockProps {}
|
||||||
|
|
||||||
const CommentTextBlock: FC<IProps> = ({ block }) => {
|
const CommentTextBlock: FC<IProps> = ({ block }) => {
|
||||||
|
const content = useMemo(() => formatText(block.content), [block.content]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={styles.text}
|
className={classNames(styles.text, markdown.wrapper)}
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: `<p>${block.content}</p>`,
|
__html: content,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { formatCellText, getURL } from '~/utils/dom';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import styles from './styles.module.scss';
|
import styles from './styles.module.scss';
|
||||||
|
import markdown from '~/styles/common/markdown.module.scss';
|
||||||
import { Icon } from '~/components/input/Icon';
|
import { Icon } from '~/components/input/Icon';
|
||||||
import { flowSetCellView } from '~/redux/flow/actions';
|
import { flowSetCellView } from '~/redux/flow/actions';
|
||||||
import { PRESETS } from '~/constants/urls';
|
import { PRESETS } from '~/constants/urls';
|
||||||
|
@ -118,6 +119,8 @@ const Cell: FC<IProps> = ({
|
||||||
}
|
}
|
||||||
}, [title]);
|
}, [title]);
|
||||||
|
|
||||||
|
const cellText = useMemo(() => formatCellText(text), [text]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames(styles.cell, styles[(flow && flow.display) || 'single'])} ref={ref}>
|
<div className={classNames(styles.cell, styles[(flow && flow.display) || 'single'])} ref={ref}>
|
||||||
{is_visible && (
|
{is_visible && (
|
||||||
|
@ -149,7 +152,10 @@ const Cell: FC<IProps> = ({
|
||||||
<div className={styles.text}>
|
<div className={styles.text}>
|
||||||
{title && <div className={styles.text_title}>{title}</div>}
|
{title && <div className={styles.text_title}>{title}</div>}
|
||||||
|
|
||||||
<Group dangerouslySetInnerHTML={{ __html: formatCellText(text) }} />
|
<div
|
||||||
|
dangerouslySetInnerHTML={{ __html: cellText }}
|
||||||
|
className={markdown.wrapper}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
@ -157,7 +163,10 @@ const Cell: FC<IProps> = ({
|
||||||
<div className={styles.text_only}>
|
<div className={styles.text_only}>
|
||||||
{title && <div className={styles.text_title}>{title}</div>}
|
{title && <div className={styles.text_title}>{title}</div>}
|
||||||
|
|
||||||
<Group dangerouslySetInnerHTML={{ __html: formatCellText(text) }} />
|
<div
|
||||||
|
dangerouslySetInnerHTML={{ __html: cellText }}
|
||||||
|
className={markdown.wrapper}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -327,6 +327,7 @@
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
font-size: 6px;
|
||||||
|
|
||||||
&::after {
|
&::after {
|
||||||
content: ' ';
|
content: ' ';
|
||||||
|
|
|
@ -1,19 +1,26 @@
|
||||||
import React, { FC } from 'react';
|
import React, { FC, useMemo } from 'react';
|
||||||
import { INode } from '~/redux/types';
|
|
||||||
import { path } from 'ramda';
|
import { path } from 'ramda';
|
||||||
import { formatTextParagraphs } from '~/utils/dom';
|
import { formatTextParagraphs } from '~/utils/dom';
|
||||||
import styles from './styles.module.scss';
|
|
||||||
import { INodeComponentProps } from '~/redux/node/constants';
|
import { INodeComponentProps } from '~/redux/node/constants';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import styles from './styles.module.scss';
|
||||||
|
import markdown from '~/styles/common/markdown.module.scss';
|
||||||
|
|
||||||
interface IProps extends INodeComponentProps {}
|
interface IProps extends INodeComponentProps {}
|
||||||
|
|
||||||
const NodeTextBlock: FC<IProps> = ({ node }) => (
|
const NodeTextBlock: FC<IProps> = ({ node }) => {
|
||||||
<div
|
const content = useMemo(() => formatTextParagraphs(path(['blocks', 0, 'text'], node)), [
|
||||||
className={styles.text}
|
node.blocks,
|
||||||
dangerouslySetInnerHTML={{
|
]);
|
||||||
__html: formatTextParagraphs(path(['blocks', 0, 'text'], node)),
|
|
||||||
}}
|
return (
|
||||||
/>
|
<div
|
||||||
);
|
className={classNames(styles.text, markdown.wrapper)}
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: content,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export { NodeTextBlock };
|
export { NodeTextBlock };
|
||||||
|
|
79
src/styles/common/markdown.module.scss
Normal file
79
src/styles/common/markdown.module.scss
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
@import "../variables";
|
||||||
|
|
||||||
|
.wrapper {
|
||||||
|
pre {
|
||||||
|
background-color: darken($comment_bg, 2%);
|
||||||
|
border: {
|
||||||
|
left: 4px solid $red;
|
||||||
|
right: 4px solid $red;
|
||||||
|
}
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 0.9em;
|
||||||
|
border-radius: $radius;
|
||||||
|
width: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
padding: $gap;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin-bottom: 0.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
max-width: 100%;
|
||||||
|
margin-bottom: 0.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin-bottom: 0.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h5, h4, h3, h2, h1 {
|
||||||
|
color: white;
|
||||||
|
font-weight: 800;
|
||||||
|
line-height: 1.2em;
|
||||||
|
margin-bottom: 0.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 1.8em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 1.6em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-size: 1.4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h5 {
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
list-style: disc;
|
||||||
|
padding-left: 20px;
|
||||||
|
margin-bottom: 0.3em;
|
||||||
|
|
||||||
|
li {
|
||||||
|
margin: 0.1em 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
strong {
|
||||||
|
font-weight: bold;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
em {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.grey) {
|
||||||
|
color: #555555;
|
||||||
|
white-space: pre-line;
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,6 +8,18 @@ import Axios from 'axios';
|
||||||
import { PRESETS } from '~/constants/urls';
|
import { PRESETS } from '~/constants/urls';
|
||||||
import { COMMENT_BLOCK_DETECTORS, COMMENT_BLOCK_TYPES, ICommentBlock } from '~/constants/comment';
|
import { COMMENT_BLOCK_DETECTORS, COMMENT_BLOCK_TYPES, ICommentBlock } from '~/constants/comment';
|
||||||
import format from 'date-fns/format';
|
import format from 'date-fns/format';
|
||||||
|
import { pipe } from 'ramda';
|
||||||
|
import {
|
||||||
|
formatTextDash,
|
||||||
|
formatExclamations,
|
||||||
|
formatTextMarkdown,
|
||||||
|
formatTextClickableUsernames,
|
||||||
|
formatTextComments,
|
||||||
|
formatTextSanitizeTags,
|
||||||
|
formatTextSanitizeYoutube,
|
||||||
|
formatTextTodos,
|
||||||
|
} from '~/utils/formatText';
|
||||||
|
import { splitTextByYoutube, splitTextOmitEmpty } from '~/utils/splitText';
|
||||||
|
|
||||||
export const getStyle = (oElm: any, strCssRule: string) => {
|
export const getStyle = (oElm: any, strCssRule: string) => {
|
||||||
if (document.defaultView && document.defaultView.getComputedStyle) {
|
if (document.defaultView && document.defaultView.getComputedStyle) {
|
||||||
|
@ -82,50 +94,18 @@ export const getURL = (file: Partial<IFile>, size?: typeof PRESETS[keyof typeof
|
||||||
return file?.url ? getURLFromString(file.url, size) : null;
|
return file?.url ? getURLFromString(file.url, size) : null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const formatText = (text: string): string =>
|
export const formatText = pipe(
|
||||||
!text
|
formatTextSanitizeTags,
|
||||||
? ''
|
formatTextSanitizeYoutube,
|
||||||
: text
|
formatTextClickableUsernames,
|
||||||
.replace(
|
formatTextComments,
|
||||||
/(https?:\/\/(www\.)?(youtube\.com|youtu\.be)\/(watch)?(\?v=)?[\w\-\&\=]+)/gim,
|
formatTextTodos,
|
||||||
'\n$1\n'
|
formatExclamations,
|
||||||
)
|
formatTextDash,
|
||||||
.replace(/\n{1,}/gim, '\n')
|
formatTextMarkdown
|
||||||
.replace(/</g, '<')
|
);
|
||||||
.replace(/>/g, '>')
|
|
||||||
.replace(
|
|
||||||
/~([\wа-яА-Я-]+)/giu,
|
|
||||||
'<span class="username" onClick="window.postMessage({ type: \'username\', username: \'$1\'});">~$1</span>'
|
|
||||||
)
|
|
||||||
.replace(/:\/\//gim, ':|--|')
|
|
||||||
.replace(/(\/\/[^\n]+)/gim, '<span class="grey">$1</span>')
|
|
||||||
.replace(/\/\/\s*(todo|туду):?\s*([^\n]+)/gim, '// <span class="todo">$1</span> $2')
|
|
||||||
.replace(
|
|
||||||
/\/\/\s*(done|сделано|сделал|готово|fixed|пофикшено|фиксед):?\s*([^\n]+)/gim,
|
|
||||||
'// <span class="done">$1</span> $2'
|
|
||||||
)
|
|
||||||
.replace(/(\*\*[\s\S]*?\*\*)/gim, '<b class="bold white">$1</b>')
|
|
||||||
.replace(/(\_\_[\s\S]*?\_\_)/gim, '<i>$1</i>')
|
|
||||||
.replace(/(\!\![\s\S]*?(\!\!|\n|$))/gim, '<span class="green">$1</span>')
|
|
||||||
.replace(/(\/\*[\s\S]*?\*\/)/gim, '<span class="grey">$1</span>')
|
|
||||||
.replace(/([=|-]{5,})/gim, '<hr />')
|
|
||||||
.replace(/:\|--\|/gim, '://')
|
|
||||||
.replace(
|
|
||||||
/(\b(https?|ftp|file):\/\/([-A-Z0-9+&@#%?=~_|!:,.;]*)([-A-Z0-9+&@#%?\/=~_|!:,.;]*)[-A-Z0-9+&@#\/%=~_|])/gi,
|
|
||||||
'<a href="$1" target="blank" rel="nofollow">$1</a>'
|
|
||||||
)
|
|
||||||
.replace(' -- ', ' — ')
|
|
||||||
.split('\n')
|
|
||||||
.filter(el => el.trim().length)
|
|
||||||
.join('\n');
|
|
||||||
|
|
||||||
export const formatTextParagraphs = (text: string): string =>
|
export const formatTextParagraphs = (text: string): string => (text && formatText(text)) || null;
|
||||||
(text &&
|
|
||||||
formatText(text)
|
|
||||||
.split('\n')
|
|
||||||
.map(str => `<p>${str}</p>`)
|
|
||||||
.join('\n')) ||
|
|
||||||
null;
|
|
||||||
|
|
||||||
export const findBlockType = (line: string): ValueOf<typeof COMMENT_BLOCK_TYPES> => {
|
export const findBlockType = (line: string): ValueOf<typeof COMMENT_BLOCK_TYPES> => {
|
||||||
const match = Object.values(COMMENT_BLOCK_DETECTORS).find(detector => line.match(detector.test));
|
const match = Object.values(COMMENT_BLOCK_DETECTORS).find(detector => line.match(detector.test));
|
||||||
|
@ -133,13 +113,16 @@ export const findBlockType = (line: string): ValueOf<typeof COMMENT_BLOCK_TYPES>
|
||||||
};
|
};
|
||||||
|
|
||||||
export const splitCommentByBlocks = (text: string): ICommentBlock[] =>
|
export const splitCommentByBlocks = (text: string): ICommentBlock[] =>
|
||||||
text.split('\n').map(line => ({
|
pipe(
|
||||||
|
splitTextByYoutube,
|
||||||
|
splitTextOmitEmpty
|
||||||
|
)([text]).map(line => ({
|
||||||
type: findBlockType(line),
|
type: findBlockType(line),
|
||||||
content: line,
|
content: line,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const formatCommentText = (author: string, text: string): ICommentBlock[] =>
|
export const formatCommentText = (author: string, text: string): ICommentBlock[] =>
|
||||||
text ? splitCommentByBlocks(formatText(text)) : null;
|
text ? splitCommentByBlocks(text) : null;
|
||||||
|
|
||||||
export const formatCellText = (text: string): string => formatTextParagraphs(text);
|
export const formatCellText = (text: string): string => formatTextParagraphs(text);
|
||||||
|
|
||||||
|
|
71
src/utils/formatText.ts
Normal file
71
src/utils/formatText.ts
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
import marked from 'marked';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleans youtube urls
|
||||||
|
*/
|
||||||
|
export const formatTextSanitizeYoutube = (text: string): string =>
|
||||||
|
text.replace(
|
||||||
|
/(https?:\/\/(www\.)?(youtube\.com|youtu\.be)\/(watch)?(\?v=)?[\w\-\&\=]+)/gim,
|
||||||
|
'\n$1\n'
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes HTML tags
|
||||||
|
*/
|
||||||
|
export const formatTextSanitizeTags = (text: string): string =>
|
||||||
|
text.replace(/</g, '<').replace(/>/g, '>');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns clickable usernames
|
||||||
|
*/
|
||||||
|
export const formatTextClickableUsernames = (text: string): string =>
|
||||||
|
text.replace(
|
||||||
|
/~([\wа-яА-Я-]+)/giu,
|
||||||
|
'<span class="username" onClick="window.postMessage({ type: \'username\', username: \'$1\'});">~$1</span>'
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes gray comments
|
||||||
|
*/
|
||||||
|
export const formatTextComments = (text: string): string =>
|
||||||
|
text
|
||||||
|
.replace(/:\/\//gim, ':|--|')
|
||||||
|
.replace(/(\/\/[^\n]+)/gim, '<span class="grey">$1</span>')
|
||||||
|
.replace(/(\/\*[\s\S]*?\*\/)/gim, '<span class="grey">$1</span>')
|
||||||
|
.replace(/:\|--\|/gim, '://');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Highlights todos
|
||||||
|
*/
|
||||||
|
export const formatTextTodos = (text: string): string =>
|
||||||
|
text
|
||||||
|
.replace(/\/\/\s*(todo|туду):?\s*([^\n]+)/gim, '// <span class="todo">$1</span> $2')
|
||||||
|
.replace(
|
||||||
|
/\/\/\s*(done|сделано|сделал|готово|fixed|пофикшено|фиксед):?\s*([^\n]+)/gim,
|
||||||
|
'// <span class="done">$1</span> $2'
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats !!exclamation messages with green color
|
||||||
|
*/
|
||||||
|
export const formatExclamations = (text: string): string =>
|
||||||
|
text.replace(/(\!\![\s\S]*?(\!\!|\n|$))/gim, '<span class="green">$1$2</span>');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats links
|
||||||
|
*/
|
||||||
|
export const formatLinks = (text: string): string =>
|
||||||
|
text.replace(
|
||||||
|
/(\b(https?|ftp|file):\/\/([-A-Z0-9+&@#%?=~_|!:,.;]*)([-A-Z0-9+&@#%?\/=~_|!:,.;]*)[-A-Z0-9+&@#\/%=~_|])/gi,
|
||||||
|
'<a href="$1" target="blank" rel="nofollow">$1</a>'
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces -- with dash
|
||||||
|
*/
|
||||||
|
export const formatTextDash = (text: string): string => text.replace(' -- ', ' — ');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats with markdown
|
||||||
|
*/
|
||||||
|
export const formatTextMarkdown = (text: string): string => marked(text);
|
|
@ -5,11 +5,5 @@ type Handlers<State, Types extends string, Actions extends Action<Types>> = {
|
||||||
readonly [Type in Types]: (state: State, action: Actions) => State;
|
readonly [Type in Types]: (state: State, action: Actions) => State;
|
||||||
};
|
};
|
||||||
|
|
||||||
// export const createReducer = <State, Types extends string, Actions extends Action<Types>>(
|
|
||||||
// initialState: State,
|
|
||||||
// handlers: Handlers<State, Types, Actions>,
|
|
||||||
// ) => (state = initialState, action: Actions) =>
|
|
||||||
// handlers.hasOwnProperty(action.type) ? handlers[action.type as Types](state, action) : state;
|
|
||||||
|
|
||||||
export const createReducer = (initialState, handlers) => (state = initialState, action) =>
|
export const createReducer = (initialState, handlers) => (state = initialState, action) =>
|
||||||
handlers.hasOwnProperty(action.type) ? handlers[action.type](state, action) : state;
|
handlers.hasOwnProperty(action.type) ? handlers[action.type](state, action) : state;
|
||||||
|
|
11
src/utils/splitText.ts
Normal file
11
src/utils/splitText.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import { flatten, isEmpty } from 'ramda';
|
||||||
|
|
||||||
|
export const splitTextByYoutube = (strings: string[]): string[] =>
|
||||||
|
flatten(
|
||||||
|
strings.map(str =>
|
||||||
|
str.split(/(https?:\/\/(?:www\.)(?:youtube\.com|youtu\.be)\/(?:watch)(?:\?v=)[\w\-\&\=]+)/)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
export const splitTextOmitEmpty = (strings: string[]): string[] =>
|
||||||
|
strings.map(el => el.trim()).filter(el => !isEmpty(el));
|
10
yarn.lock
10
yarn.lock
|
@ -1684,6 +1684,11 @@
|
||||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.6.tgz#f4c7ec43e81b319a9815115031709f26987891f0"
|
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.6.tgz#f4c7ec43e81b319a9815115031709f26987891f0"
|
||||||
integrity sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==
|
integrity sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==
|
||||||
|
|
||||||
|
"@types/marked@^1.2.2":
|
||||||
|
version "1.2.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/marked/-/marked-1.2.2.tgz#1f858a0e690247ecf3b2eef576f98f86e8d960d4"
|
||||||
|
integrity sha512-wLfw1hnuuDYrFz97IzJja0pdVsC0oedtS4QsKH1/inyW9qkLQbXgMUqEQT0MVtUBx3twjWeInUfjQbhBVLECXw==
|
||||||
|
|
||||||
"@types/minimatch@*":
|
"@types/minimatch@*":
|
||||||
version "3.0.3"
|
version "3.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
|
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
|
||||||
|
@ -7129,6 +7134,11 @@ map-visit@^1.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
object-visit "^1.0.0"
|
object-visit "^1.0.0"
|
||||||
|
|
||||||
|
marked@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/marked/-/marked-2.0.0.tgz#9662bbcb77ebbded0662a7be66ff929a8611cee5"
|
||||||
|
integrity sha512-NqRSh2+LlN2NInpqTQnS614Y/3NkVMFFU6sJlRFEpxJ/LHuK/qJECH7/fXZjk4VZstPW/Pevjil/VtSONsLc7Q==
|
||||||
|
|
||||||
md5.js@^1.3.4:
|
md5.js@^1.3.4:
|
||||||
version "1.3.5"
|
version "1.3.5"
|
||||||
resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f"
|
resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue