1
0
Fork 0
mirror of https://github.com/muerwre/vault-frontend.git synced 2025-04-24 20:36:40 +07:00

video node editor

This commit is contained in:
Fedor Katurov 2019-10-19 19:42:39 +07:00
parent 12e366fce9
commit 8612cc5ce7
10 changed files with 105 additions and 6 deletions

View file

@ -18,6 +18,7 @@ type IProps = typeof mapDispatchToProps & {};
const SubmitBarUnconnected: FC<IProps> = ({ nodeCreate }) => {
const onOpenImageEditor = useCallback(() => nodeCreate(NODE_TYPES.IMAGE), [nodeCreate]);
const onOpenTextEditor = useCallback(() => nodeCreate(NODE_TYPES.TEXT), [nodeCreate]);
const onOpenVideoEditor = useCallback(() => nodeCreate(NODE_TYPES.VIDEO), [nodeCreate]);
return (
<div className={styles.wrap}>
@ -27,7 +28,11 @@ const SubmitBarUnconnected: FC<IProps> = ({ nodeCreate }) => {
</div>
<div onClick={onOpenTextEditor}>
<Icon icon="image" />
<Icon icon="text" />
</div>
<div onClick={onOpenVideoEditor}>
<Icon icon="video" />
</div>
</div>

View file

@ -0,0 +1,40 @@
import React, { FC, useCallback, useMemo } from 'react';
import { INode } from '~/redux/types';
import * as styles from './styles.scss';
import path from 'ramda/es/path';
import { InputText } from '~/components/input/InputText';
interface IProps {
data: INode;
setData: (val: INode) => void;
}
const VideoEditor: FC<IProps> = ({ data, setData }) => {
const setUrl = useCallback(
(url: string) => setData({ ...data, blocks: [{ type: 'video', url }] }),
[data, setData]
);
const url = (path(['blocks', 0, 'url'], data) as string) || '';
const preview = useMemo(() => {
const match =
url &&
url.match(
/http(?:s?):\/\/(?:www\.)?youtu(?:be\.com\/watch\?v=|\.be\/)([\w\-\_]*)(&(amp;)?[\w\?=]*)?/
);
return match && match[1] ? `http://img.youtube.com/vi/${match[1]}/maxresdefault.jpg` : null;
}, [url]);
return (
<div className={styles.preview} style={{ backgroundImage: preview && `url("${preview}")` }}>
<div className={styles.input_wrap}>
<div className={styles.input}>
<InputText value={url} handler={setUrl} placeholder="Адрес видео" />
</div>
</div>
</div>
);
};
export { VideoEditor };

View file

@ -0,0 +1,30 @@
.preview {
padding-top: 56.25%;
position: relative;
// background: darken($color: $content_bg, $amount: 2%);
}
.input_wrap {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.input {
// @include outer_shadow();
flex: 1 0 50%;
padding: $gap * 2 $gap $gap * 2 $gap;
// border-radius: $radius;
background: $content_bg;
// margin: 20px;
input {
text-align: center;
}
}

View file

@ -0,0 +1,10 @@
import React, { FC } from 'react';
import { EditorDialog } from '~/containers/dialogs/EditorDialog';
import { IDialogProps } from '~/redux/types';
import { NODE_TYPES } from '~/redux/node/constants';
type IProps = IDialogProps & {};
const EditorDialogVideo: FC<IProps> = props => <EditorDialog type={NODE_TYPES.VIDEO} {...props} />;
export { EditorDialogVideo };

View file

@ -13,13 +13,11 @@ function* onGetFlow() {
}: IResultWithStatus<{ nodes: INode[] }> = yield call(getNodes, {});
if (!nodes || !nodes.length) {
// todo: set error empty response
yield put(flowSetNodes([]));
return;
}
// todo: write nodes
yield put(flowSetNodes(nodes));
// console.log('flow', { nodes, error });
}
export default function* nodeSaga() {

View file

@ -2,6 +2,7 @@ import { ValueOf } from '~/redux/types';
import { LoginDialog } from '~/containers/dialogs/LoginDialog';
import { EditorDialogImage } from '~/containers/editors/EditorDialogImage';
import { EditorDialogText } from '~/containers/editors/EditorDialogText';
import { EditorDialogVideo } from '~/containers/editors/EditorDialogVideo';
import { NODE_TYPES } from '../node/constants';
export const MODAL_ACTIONS = {
@ -13,18 +14,21 @@ export const MODAL_ACTIONS = {
export const DIALOGS = {
EDITOR_IMAGE: 'EDITOR_IMAGE',
EDITOR_TEXT: 'EDITOR_TEXT',
EDITOR_VIDEO: 'EDITOR_VIDEO',
LOGIN: 'LOGIN',
};
export const DIALOG_CONTENT = {
[DIALOGS.EDITOR_IMAGE]: EditorDialogImage,
[DIALOGS.EDITOR_TEXT]: EditorDialogText,
[DIALOGS.EDITOR_VIDEO]: EditorDialogVideo,
[DIALOGS.LOGIN]: LoginDialog,
};
export const NODE_EDITOR_DIALOGS = {
[NODE_TYPES.IMAGE]: DIALOGS.EDITOR_IMAGE,
[NODE_TYPES.TEXT]: DIALOGS.EDITOR_TEXT,
[NODE_TYPES.VIDEO]: DIALOGS.EDITOR_VIDEO,
};
export interface IDialogProps {

View file

@ -4,6 +4,7 @@ import { NodeImageSlideBlock } from '~/components/node/NodeImageSlideBlock';
import { NodeTextBlock } from '~/components/node/NodeTextBlock';
import { ImageEditor } from '~/components/editors/ImageEditor';
import { TextEditor } from '~/components/editors/TextEditor';
import { VideoEditor } from '~/components/editors/VideoEditor';
const prefix = 'NODE.';
export const NODE_ACTIONS = {
@ -82,6 +83,7 @@ export const EMPTY_COMMENT: IComment = {
export const NODE_EDITORS = {
[NODE_TYPES.IMAGE]: ImageEditor,
[NODE_TYPES.TEXT]: TextEditor,
[NODE_TYPES.VIDEO]: VideoEditor,
};
export const NODE_EDITOR_DATA: Record<

View file

@ -100,7 +100,7 @@ export interface IBlockText {
}
export interface IBlockEmbed {
type: 'embed';
type: 'video';
url: string;
}

View file

@ -98,6 +98,16 @@ const Sprites: FC<{}> = () => (
<path fill="none" d="M0 0h24v24H0V0z" />
<path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z" />
</g>
<g id="text" stroke="none">
<path fill="none" d="M0 0h24v24H0V0z" />
<path d="M14 17H4v2h10v-2zm6-8H4v2h16V9zM4 15h16v-2H4v2zM4 5v2h16V5H4z" />
</g>
<g id="video" stroke="none">
<path fill="none" d="M0 0h24v24H0V0z" />
<path d="M17 10.5V7c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.55 0 1-.45 1-1v-3.5l4 4v-11l-4 4z" />
</g>
</svg>
);