mirror of
https://github.com/muerwre/vault-frontend.git
synced 2025-04-25 21:06:42 +07:00
node cover display
This commit is contained in:
parent
c8593b7e7a
commit
ad0b9e6a28
9 changed files with 94 additions and 7 deletions
|
@ -1,4 +1,7 @@
|
|||
.blur {
|
||||
filter: blur(0);
|
||||
transition: filter 0.25s;
|
||||
max-height: 100vh;
|
||||
width: 100vw;
|
||||
overflow: visible auto;
|
||||
}
|
||||
|
|
25
src/components/containers/PageCover/index.tsx
Normal file
25
src/components/containers/PageCover/index.tsx
Normal file
|
@ -0,0 +1,25 @@
|
|||
import React, { FC } from 'react';
|
||||
import * as styles from './styles.scss';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { selectNode } from '~/redux/node/selectors';
|
||||
import { connect } from 'react-redux';
|
||||
import pick from 'ramda/es/pick';
|
||||
import { getURL } from '~/utils/dom';
|
||||
|
||||
const mapStateToProps = state => pick(['current_cover_image'], selectNode(state));
|
||||
|
||||
type IProps = ReturnType<typeof mapStateToProps> & {};
|
||||
|
||||
const PageCoverUnconnected: FC<IProps> = ({ current_cover_image }) =>
|
||||
current_cover_image
|
||||
? createPortal(
|
||||
<div
|
||||
className={styles.wrap}
|
||||
style={{ backgroundImage: `url("${getURL(current_cover_image)}")` }}
|
||||
/>,
|
||||
document.body
|
||||
)
|
||||
: null;
|
||||
|
||||
const PageCover = connect(mapStateToProps)(PageCoverUnconnected);
|
||||
export { PageCover };
|
30
src/components/containers/PageCover/styles.scss
Normal file
30
src/components/containers/PageCover/styles.scss
Normal file
|
@ -0,0 +1,30 @@
|
|||
@keyframes fadeIn {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.wrap {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: -1;
|
||||
background: 50% 50% no-repeat;
|
||||
background-size: cover;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
animation: fadeIn 2s;
|
||||
|
||||
&::after {
|
||||
content: ' ';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: url(~/sprites/dots.svg) rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
}
|
|
@ -14,18 +14,22 @@ import { URLS } from '~/constants/urls';
|
|||
import { Modal } from '~/containers/dialogs/Modal';
|
||||
import { selectModal } from '~/redux/modal/selectors';
|
||||
import { BlurWrapper } from '~/components/containers/BlurWrapper';
|
||||
import { PageCover } from '~/components/containers/PageCover';
|
||||
import { NodeLayout } from './node/NodeLayout';
|
||||
import { BottomContainer } from '~/containers/main/BottomContainer';
|
||||
|
||||
const mapStateToProps = selectModal;
|
||||
const mapStateToProps = state => ({
|
||||
modal: selectModal(state),
|
||||
});
|
||||
const mapDispatchToProps = {};
|
||||
|
||||
type IProps = typeof mapDispatchToProps & ReturnType<typeof mapStateToProps> & {};
|
||||
|
||||
const Component: FC<IProps> = ({ is_shown }) => (
|
||||
const Component: FC<IProps> = ({ modal: { is_shown } }) => (
|
||||
<ConnectedRouter history={history}>
|
||||
<div>
|
||||
<BlurWrapper is_blurred={is_shown}>
|
||||
<PageCover />
|
||||
<MainLayout>
|
||||
<Modal />
|
||||
<Sprites />
|
||||
|
|
|
@ -26,6 +26,7 @@ const mapStateToProps = state => ({
|
|||
const mapDispatchToProps = {
|
||||
nodeLoadNode: NODE_ACTIONS.nodeLoadNode,
|
||||
nodeUpdateTags: NODE_ACTIONS.nodeUpdateTags,
|
||||
nodeSetCoverImage: NODE_ACTIONS.nodeSetCoverImage,
|
||||
nodeEdit: NODE_ACTIONS.nodeEdit,
|
||||
nodeLike: NODE_ACTIONS.nodeLike,
|
||||
};
|
||||
|
@ -38,13 +39,14 @@ const NodeLayoutUnconnected: FC<IProps> = ({
|
|||
match: {
|
||||
params: { id },
|
||||
},
|
||||
node: { is_loading, is_loading_comments, comments = [], current: node },
|
||||
node: { is_loading, is_loading_comments, comments = [], current: node, current_cover_image },
|
||||
user,
|
||||
user: { is_user, role },
|
||||
user: { is_user },
|
||||
nodeLoadNode,
|
||||
nodeUpdateTags,
|
||||
nodeEdit,
|
||||
nodeLike,
|
||||
nodeSetCoverImage,
|
||||
}) => {
|
||||
const [layout, setLayout] = useState({});
|
||||
|
||||
|
@ -71,6 +73,12 @@ const NodeLayoutUnconnected: FC<IProps> = ({
|
|||
const onEdit = useCallback(() => nodeEdit(node.id), [nodeEdit, node]);
|
||||
const onLike = useCallback(() => nodeLike(node.id), [nodeLike, node]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!node.cover) return;
|
||||
nodeSetCoverImage(node.cover);
|
||||
return () => nodeSetCoverImage(null);
|
||||
}, [nodeSetCoverImage, node.cover]);
|
||||
|
||||
return (
|
||||
<Card className={styles.node} seamless>
|
||||
{block && createElement(block, { node, is_loading, updateLayout, layout })}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { INode, IValidationErrors, IComment, ITag } from '../types';
|
||||
import { INode, IValidationErrors, IComment, ITag, IFile } from '../types';
|
||||
import { NODE_ACTIONS, NODE_TYPES } from './constants';
|
||||
import { INodeState } from './reducer';
|
||||
|
||||
|
@ -84,3 +84,8 @@ export const nodeSetEditor = (editor: INode) => ({
|
|||
type: NODE_ACTIONS.SET_EDITOR,
|
||||
editor,
|
||||
});
|
||||
|
||||
export const nodeSetCoverImage = (current_cover_image: IFile) => ({
|
||||
type: NODE_ACTIONS.SET_COVER_IMAGE,
|
||||
current_cover_image,
|
||||
});
|
||||
|
|
|
@ -35,6 +35,7 @@ export const NODE_ACTIONS = {
|
|||
|
||||
UPDATE_TAGS: `${prefix}UPDATE_TAGS`,
|
||||
SET_TAGS: `${prefix}SET_TAGS`,
|
||||
SET_COVER_IMAGE: `${prefix}SET_COVER_IMAGE`,
|
||||
};
|
||||
|
||||
export const EMPTY_NODE: INode = {
|
||||
|
@ -99,8 +100,10 @@ export const NODE_EDITORS = {
|
|||
};
|
||||
|
||||
export const NODE_PANEL_COMPONENTS = {
|
||||
[NODE_TYPES.TEXT]: [EditorUploadCoverButton],
|
||||
[NODE_TYPES.VIDEO]: [EditorUploadCoverButton],
|
||||
[NODE_TYPES.IMAGE]: [EditorImageUploadButton, EditorUploadCoverButton],
|
||||
[NODE_TYPES.AUDIO]: [EditorAudioUploadButton, EditorImageUploadButton],
|
||||
[NODE_TYPES.AUDIO]: [EditorAudioUploadButton, EditorImageUploadButton, EditorUploadCoverButton],
|
||||
};
|
||||
|
||||
export const NODE_EDITOR_DATA: Record<
|
||||
|
|
|
@ -10,6 +10,7 @@ import {
|
|||
nodeSetCommentData,
|
||||
nodeSetTags,
|
||||
nodeSetEditor,
|
||||
nodeSetCoverImage,
|
||||
} from './actions';
|
||||
import { INodeState } from './reducer';
|
||||
|
||||
|
@ -46,6 +47,11 @@ const setTags = (state: INodeState, { tags }: ReturnType<typeof nodeSetTags>) =>
|
|||
const setEditor = (state: INodeState, { editor }: ReturnType<typeof nodeSetEditor>) =>
|
||||
assocPath(['editor'], editor, state);
|
||||
|
||||
const setCoverImage = (
|
||||
state: INodeState,
|
||||
{ current_cover_image }: ReturnType<typeof nodeSetCoverImage>
|
||||
) => assocPath(['current_cover_image'], current_cover_image, state);
|
||||
|
||||
export const NODE_HANDLERS = {
|
||||
[NODE_ACTIONS.SET_SAVE_ERRORS]: setSaveErrors,
|
||||
[NODE_ACTIONS.SET_LOADING]: setLoading,
|
||||
|
@ -56,4 +62,5 @@ export const NODE_HANDLERS = {
|
|||
[NODE_ACTIONS.SET_COMMENT_DATA]: setCommentData,
|
||||
[NODE_ACTIONS.SET_TAGS]: setTags,
|
||||
[NODE_ACTIONS.SET_EDITOR]: setEditor,
|
||||
[NODE_ACTIONS.SET_COVER_IMAGE]: setCoverImage,
|
||||
};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { createReducer } from '~/utils/reducer';
|
||||
import { INode, IComment } from '../types';
|
||||
import { INode, IComment, IFile } from '../types';
|
||||
import { EMPTY_NODE, EMPTY_COMMENT } from './constants';
|
||||
import { NODE_HANDLERS } from './handlers';
|
||||
|
||||
|
@ -8,6 +8,7 @@ export type INodeState = Readonly<{
|
|||
current: INode;
|
||||
comments: IComment[];
|
||||
comment_data: Record<number, IComment>;
|
||||
current_cover_image: IFile;
|
||||
|
||||
error: string;
|
||||
errors: Record<string, string>;
|
||||
|
@ -27,6 +28,7 @@ const INITIAL_STATE: INodeState = {
|
|||
current: { ...EMPTY_NODE },
|
||||
comment_data: { 0: { ...EMPTY_COMMENT } },
|
||||
comments: [],
|
||||
current_cover_image: null,
|
||||
|
||||
is_loading: false,
|
||||
is_loading_comments: false,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue