From 11348aa4119713787390994cfb9b843658ce671b Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Fri, 15 Nov 2019 13:36:13 +0700 Subject: [PATCH] several bug fixes --- .../node/NodeImageSlideBlock/index.tsx | 16 +-- src/components/node/NodeNoComments/index.tsx | 16 ++- .../node/NodeNoComments/styles.scss | 16 ++- src/components/node/NodePanel/index.tsx | 43 +++++-- src/components/node/NodePanelInner/index.tsx | 38 ++++-- .../node/NodeRelated/placeholder.tsx | 31 +++++ src/components/node/NodeRelated/styles.scss | 12 ++ src/components/node/NodeTags/placeholder.tsx | 17 +++ src/components/node/Tags/index.tsx | 49 +++++--- .../placeholders/Placeholder/styles.scss | 2 +- src/containers/node/NodeLayout/index.tsx | 118 ++++++++++++------ src/containers/node/NodeLayout/styles.scss | 4 + src/styles/variables.scss | 29 +++-- 13 files changed, 281 insertions(+), 110 deletions(-) create mode 100644 src/components/node/NodeRelated/placeholder.tsx create mode 100644 src/components/node/NodeTags/placeholder.tsx diff --git a/src/components/node/NodeImageSlideBlock/index.tsx b/src/components/node/NodeImageSlideBlock/index.tsx index a20812d3..4a800395 100644 --- a/src/components/node/NodeImageSlideBlock/index.tsx +++ b/src/components/node/NodeImageSlideBlock/index.tsx @@ -228,12 +228,14 @@ const NodeImageSlideBlock: FC = ({ )} - + {!is_loading && ( + + )}
= ({ is_active: index === current && loaded[index] })} ref={setRef(index)} - key={file.id} + key={node.updated_at + file.id} > = ({ is_loading = false }) => (
{!is_loading && t(ERRORS.NO_COMMENTS)}
- - ); diff --git a/src/components/node/NodeNoComments/styles.scss b/src/components/node/NodeNoComments/styles.scss index 30cbc870..c191b425 100644 --- a/src/components/node/NodeNoComments/styles.scss +++ b/src/components/node/NodeNoComments/styles.scss @@ -1,30 +1,34 @@ @keyframes fade { 0% { - opacity: 0.25; + // opacity: 0.25; } 100% { - opacity: 0.1; + // opacity: 0.1; } } .wrap { + user-select: none; height: 300px; overflow: hidden; position: relative; - @include after_shade($node_bg); + // @include after_shade($node_bg); &:global(.is_loading) { + opacity: 1; + .card { + background: $placeholder_bg; animation: fade 0.5s infinite alternate; } } } .card { - opacity: 0.2; + opacity: 0.3; border-radius: $radius; height: 96px; - background: $comment_bg; + background: $placeholder_bg; display: flex; align-items: center; justify-content: center; @@ -33,7 +37,7 @@ color: transparentize(white, 0.5); flex: 0 0 $comment_height; - @include outer_shadow(); + // @include outer_shadow(); &:nth-child(2) { // animation-delay: -300ms !important; diff --git a/src/components/node/NodePanel/index.tsx b/src/components/node/NodePanel/index.tsx index ba745934..439ead15 100644 --- a/src/components/node/NodePanel/index.tsx +++ b/src/components/node/NodePanel/index.tsx @@ -1,9 +1,16 @@ -import React, { FC, useCallback, useEffect, useRef, useState, memo } from 'react'; -import * as styles from './styles.scss'; -import { INode } from '~/redux/types'; -import { createPortal } from 'react-dom'; -import { NodePanelInner } from '~/components/node/NodePanelInner'; -import pick from 'ramda/es/pick'; +import React, { + FC, + useCallback, + useEffect, + useRef, + useState, + memo +} from "react"; +import * as styles from "./styles.scss"; +import { INode } from "~/redux/types"; +import { createPortal } from "react-dom"; +import { NodePanelInner } from "~/components/node/NodePanelInner"; +import pick from "ramda/es/pick"; interface IProps { node: Partial; @@ -13,13 +20,25 @@ interface IProps { can_like: boolean; can_star: boolean; + is_loading?: boolean; + onEdit: () => void; onLike: () => void; onStar: () => void; } const NodePanel: FC = memo( - ({ node, layout, can_edit, can_like, can_star, onEdit, onLike, onStar }) => { + ({ + node, + layout, + can_edit, + can_like, + can_star, + is_loading, + onEdit, + onLike, + onStar + }) => { const [stack, setStack] = useState(false); const ref = useRef(null); @@ -35,12 +54,12 @@ const NodePanel: FC = memo( useEffect(() => { getPlace(); - window.addEventListener('scroll', getPlace); - window.addEventListener('resize', getPlace); + window.addEventListener("scroll", getPlace); + window.addEventListener("resize", getPlace); return () => { - window.removeEventListener('scroll', getPlace); - window.removeEventListener('resize', getPlace); + window.removeEventListener("scroll", getPlace); + window.removeEventListener("resize", getPlace); }; }, [layout]); @@ -57,6 +76,7 @@ const NodePanel: FC = memo( can_edit={can_edit} can_like={can_like} can_star={can_star} + is_loading={is_loading} />, document.body ) @@ -69,6 +89,7 @@ const NodePanel: FC = memo( can_edit={can_edit} can_like={can_like} can_star={can_star} + is_loading={is_loading} /> )}
diff --git a/src/components/node/NodePanelInner/index.tsx b/src/components/node/NodePanelInner/index.tsx index caaf6275..8d112757 100644 --- a/src/components/node/NodePanelInner/index.tsx +++ b/src/components/node/NodePanelInner/index.tsx @@ -1,10 +1,11 @@ -import React, { FC } from 'react'; -import * as styles from './styles.scss'; -import { Group } from '~/components/containers/Group'; -import { Filler } from '~/components/containers/Filler'; -import { Icon } from '~/components/input/Icon'; -import { INode } from '~/redux/types'; -import classNames from 'classnames'; +import React, { FC } from "react"; +import * as styles from "./styles.scss"; +import { Group } from "~/components/containers/Group"; +import { Filler } from "~/components/containers/Filler"; +import { Icon } from "~/components/input/Icon"; +import { INode } from "~/redux/types"; +import classNames from "classnames"; +import { Placeholder } from "~/components/placeholders/Placeholder"; interface IProps { node: Partial; @@ -13,6 +14,9 @@ interface IProps { can_edit: boolean; can_like: boolean; can_star: boolean; + + is_loading: boolean; + onEdit: () => void; onLike: () => void; onStar: () => void; @@ -21,20 +25,34 @@ interface IProps { const NodePanelInner: FC = ({ node: { title, user, is_liked, is_heroic }, stack, + can_star, can_edit, can_like, + + is_loading, + onStar, onEdit, - onLike, + onLike }) => { return (
-
{title || '...'}
- {user && user.username &&
~{user.username}
} +
+ {is_loading ? : title || "..."} +
+ {user && user.username && ( +
+ {is_loading ? ( + + ) : ( + `~${user.username}` + )} +
+ )}
diff --git a/src/components/node/NodeRelated/placeholder.tsx b/src/components/node/NodeRelated/placeholder.tsx new file mode 100644 index 00000000..42b6a2e0 --- /dev/null +++ b/src/components/node/NodeRelated/placeholder.tsx @@ -0,0 +1,31 @@ +import React, { FC, memo } from "react"; +import styles from "./styles.scss"; +import cell_style from "~/components/node/NodeRelatedItem/styles.scss"; +import { Group } from "~/components/containers/Group"; +import { Placeholder } from "~/components/placeholders/Placeholder"; +import range from "ramda/es/range"; +import classNames from "classnames"; + +interface IProps {} + +const NodeRelatedPlaceholder: FC = memo(() => { + return ( + +
+
+
+ +
+
+
+ +
+ {range(0, 6).map(el => ( +
+ ))} +
+ + ); +}); + +export { NodeRelatedPlaceholder }; diff --git a/src/components/node/NodeRelated/styles.scss b/src/components/node/NodeRelated/styles.scss index b0b43968..f11b52ac 100644 --- a/src/components/node/NodeRelated/styles.scss +++ b/src/components/node/NodeRelated/styles.scss @@ -36,3 +36,15 @@ .text { margin: 0 $gap; } + +.placeholder { + .text { + opacity: 1; + } + + .grid { + div { + background: $placeholder_bg; + } + } +} diff --git a/src/components/node/NodeTags/placeholder.tsx b/src/components/node/NodeTags/placeholder.tsx new file mode 100644 index 00000000..f5b015b4 --- /dev/null +++ b/src/components/node/NodeTags/placeholder.tsx @@ -0,0 +1,17 @@ +import React, { FC, memo } from "react"; +import { Tags } from "../Tags"; +import { ITag } from "~/redux/types"; + +interface IProps { + is_editable?: boolean; + tags: ITag[]; + onChange?: (tags: string[]) => void; +} + +const NodeTagsPlaceholder: FC = memo( + ({ is_editable, tags, onChange }) => ( + + ) +); + +export { NodeTagsPlaceholder }; diff --git a/src/components/node/Tags/index.tsx b/src/components/node/Tags/index.tsx index c3505ccb..8af67240 100644 --- a/src/components/node/Tags/index.tsx +++ b/src/components/node/Tags/index.tsx @@ -6,13 +6,12 @@ import React, { useEffect, KeyboardEvent, ChangeEvent, - useRef, -} from 'react'; -import { TagField } from '~/components/containers/TagField'; -import { ITag } from '~/redux/types'; -import { Tag } from '~/components/node/Tag'; -import uniq from 'ramda/es/uniq'; -import assocPath from 'ramda/es/assocPath'; + useRef +} from "react"; +import { TagField } from "~/components/containers/TagField"; +import { ITag } from "~/redux/types"; +import { Tag } from "~/components/node/Tag"; +import uniq from "ramda/es/uniq"; type IProps = HTMLAttributes & { tags: Partial[]; @@ -20,8 +19,13 @@ type IProps = HTMLAttributes & { onTagsChange?: (tags: string[]) => void; }; -export const Tags: FC = ({ tags, is_editable, onTagsChange, ...props }) => { - const [input, setInput] = useState(''); +export const Tags: FC = ({ + tags, + is_editable, + onTagsChange, + ...props +}) => { + const [input, setInput] = useState(""); const [data, setData] = useState([]); const timer = useRef(null); @@ -35,17 +39,17 @@ export const Tags: FC = ({ tags, is_editable, onTagsChange, ...props }) const onKeyUp = useCallback( ({ key }: KeyboardEvent) => { - if (key === 'Backspace' && input === '' && data.length) { + if (key === "Backspace" && input === "" && data.length) { setData(data.slice(0, data.length - 1)); setInput(data[data.length - 1].title); } - if (key === 'Enter' || key === ',' || key === 'Comma') { + if (key === "Enter" || key === "," || key === "Comma") { setData( uniq([ ...data, ...input - .split(',') + .split(",") .map((title: string) => title .trim() @@ -55,11 +59,11 @@ export const Tags: FC = ({ tags, is_editable, onTagsChange, ...props }) .filter(el => el.length > 0) .filter(el => !tags.some(tag => tag.title.trim() === el.trim())) .map(title => ({ - title, - })), + title + })) ]) ); - setInput(''); + setInput(""); } }, [input, setInput, data, setData] @@ -71,12 +75,16 @@ export const Tags: FC = ({ tags, is_editable, onTagsChange, ...props }) if (!items.length) return; setData(items); - setInput(''); + setInput(""); onTagsChange(uniq([...tags, ...items]).map(tag => tag.title)); }, [tags, data, onTagsChange, input, setInput]); useEffect(() => { - setData(data.filter(({ title }) => !tags.some(tag => tag.title.trim() === title.trim()))); + setData( + data.filter( + ({ title }) => !tags.some(tag => tag.title.trim() === title.trim()) + ) + ); }, [tags]); return ( @@ -90,7 +98,12 @@ export const Tags: FC = ({ tags, is_editable, onTagsChange, ...props }) ))} {is_editable && ( - + )} ); diff --git a/src/components/placeholders/Placeholder/styles.scss b/src/components/placeholders/Placeholder/styles.scss index 3d1d25e2..1c7782d8 100644 --- a/src/components/placeholders/Placeholder/styles.scss +++ b/src/components/placeholders/Placeholder/styles.scss @@ -1,6 +1,6 @@ .placeholder { height: 1em; width: 120px; - background: transparentize(white, 0.95); + background: $placeholder_bg; border-radius: 1em; } diff --git a/src/containers/node/NodeLayout/index.tsx b/src/containers/node/NodeLayout/index.tsx index 7c60d204..2bf31e50 100644 --- a/src/containers/node/NodeLayout/index.tsx +++ b/src/containers/node/NodeLayout/index.tsx @@ -1,27 +1,36 @@ -import React, { FC, createElement, useEffect, useCallback, useState, useMemo, memo } from 'react'; -import { RouteComponentProps } from 'react-router'; -import { connect } from 'react-redux'; -import { canEditNode, canLikeNode, canStarNode } from '~/utils/node'; -import { selectNode } from '~/redux/node/selectors'; -import { Card } from '~/components/containers/Card'; +import React, { + FC, + createElement, + useEffect, + useCallback, + useState, + useMemo, + memo +} from "react"; +import { RouteComponentProps } from "react-router"; +import { connect } from "react-redux"; +import { canEditNode, canLikeNode, canStarNode } from "~/utils/node"; +import { selectNode } from "~/redux/node/selectors"; +import { Card } from "~/components/containers/Card"; -import { NodePanel } from '~/components/node/NodePanel'; -import { Group } from '~/components/containers/Group'; -import { Padder } from '~/components/containers/Padder'; -import { NodeNoComments } from '~/components/node/NodeNoComments'; -import { NodeRelated } from '~/components/node/NodeRelated'; -import * as styles from './styles.scss'; -import { NodeComments } from '~/components/node/NodeComments'; -import { NodeTags } from '~/components/node/NodeTags'; -import { NODE_COMPONENTS, NODE_INLINES } from '~/redux/node/constants'; -import * as NODE_ACTIONS from '~/redux/node/actions'; -import { CommentForm } from '~/components/node/CommentForm'; -import { selectUser } from '~/redux/auth/selectors'; -import pick from 'ramda/es/pick'; +import { NodePanel } from "~/components/node/NodePanel"; +import { Group } from "~/components/containers/Group"; +import { Padder } from "~/components/containers/Padder"; +import { NodeNoComments } from "~/components/node/NodeNoComments"; +import { NodeRelated } from "~/components/node/NodeRelated"; +import * as styles from "./styles.scss"; +import { NodeComments } from "~/components/node/NodeComments"; +import { NodeTags } from "~/components/node/NodeTags"; +import { NODE_COMPONENTS, NODE_INLINES } from "~/redux/node/constants"; +import * as NODE_ACTIONS from "~/redux/node/actions"; +import { CommentForm } from "~/components/node/CommentForm"; +import { selectUser } from "~/redux/auth/selectors"; +import pick from "ramda/es/pick"; +import { NodeRelatedPlaceholder } from "~/components/node/NodeRelated/placeholder"; const mapStateToProps = state => ({ node: selectNode(state), - user: selectUser(state), + user: selectUser(state) }); const mapDispatchToProps = { @@ -30,7 +39,7 @@ const mapDispatchToProps = { nodeSetCoverImage: NODE_ACTIONS.nodeSetCoverImage, nodeEdit: NODE_ACTIONS.nodeEdit, nodeLike: NODE_ACTIONS.nodeLike, - nodeStar: NODE_ACTIONS.nodeStar, + nodeStar: NODE_ACTIONS.nodeStar }; type IProps = ReturnType & @@ -40,9 +49,15 @@ type IProps = ReturnType & const NodeLayoutUnconnected: FC = memo( ({ match: { - params: { id }, + params: { id } + }, + node: { + is_loading, + is_loading_comments, + comments = [], + current: node, + related }, - node: { is_loading, is_loading_comments, comments = [], current: node, related }, user, user: { is_user }, nodeGotoNode, @@ -50,8 +65,10 @@ const NodeLayoutUnconnected: FC = memo( nodeEdit, nodeLike, nodeStar, - nodeSetCoverImage, + nodeSetCoverImage }) => { + // const is_loading = true; + const [layout, setLayout] = useState({}); const updateLayout = useCallback(() => setLayout({}), []); @@ -86,11 +103,12 @@ const NodeLayoutUnconnected: FC = memo( }, [nodeSetCoverImage, node.cover]); return ( - - {block && createElement(block, { node, is_loading, updateLayout, layout })} + + {block && + createElement(block, { node, is_loading, updateLayout, layout })} = memo( onEdit={onEdit} onLike={onLike} onStar={onStar} + is_loading={is_loading} /> @@ -106,32 +125,57 @@ const NodeLayoutUnconnected: FC = memo( {inline_block && (
- {createElement(inline_block, { node, is_loading, updateLayout, layout })} + {createElement(inline_block, { + node, + is_loading, + updateLayout, + layout + })}
)} - {is_loading_comments || !comments.length ? ( - + {is_loading || + is_loading_comments || + (!comments.length && !inline_block) ? ( + ) : ( )} - {is_user && } + {is_user && !is_loading && }
- + {!is_loading && ( + + )} - {related && + {is_loading && } + + {!is_loading && + related && related.albums && Object.keys(related.albums).map(album => ( - + ))} - {related && related.similar && related.similar.length > 0 && ( - - )} + {!is_loading && + related && + related.similar && + related.similar.length > 0 && ( + + )}
diff --git a/src/containers/node/NodeLayout/styles.scss b/src/containers/node/NodeLayout/styles.scss index ae3fabc4..1770299b 100644 --- a/src/containers/node/NodeLayout/styles.scss +++ b/src/containers/node/NodeLayout/styles.scss @@ -7,6 +7,10 @@ .comments { flex: 3 1; min-width: 0; + display: flex; + align-items: stretch; + justify-content: flex-start; + flex-direction: column; } .panel { diff --git a/src/styles/variables.scss b/src/styles/variables.scss index c5b76470..6a41e8ce 100644 --- a/src/styles/variables.scss +++ b/src/styles/variables.scss @@ -1,4 +1,4 @@ -@import 'colors'; +@import "colors"; $cell: 280px; $gap: 10px; @@ -13,6 +13,7 @@ $cell_radius: $radius; $panel_radius: $radius; $input_radius: $radius; $dialog_radius: $radius * 2; +$placeholder_bg: transparentize(white, 0.96); $input_height: 36px; $info_height: 24px; @@ -30,9 +31,9 @@ $extra_light: 200; $upload_button_height: 52px; -$font: Montserrat, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, - 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', - 'Noto Color Emoji'; +$font: Montserrat, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, + "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", + "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; $font_48_semibold: $semibold 48px $font; $font_48_bold: $bold 48px $font; @@ -60,11 +61,14 @@ $font_8_semibold: $semibold 8px $font; $font_cell_title: $bold 30px $font; $font_hero_title: $bold 40px $font; -$shadow_depth_1: transparentize(black, 0.8) 0 1px, inset transparentize(white, 0.98) 0 1px; -$shadow_depth_2: transparentize(black, 0.8) 0 2px, inset transparentize(white, 0.98) 0 1px; +$shadow_depth_1: transparentize(black, 0.8) 0 1px, + inset transparentize(white, 0.98) 0 1px; +$shadow_depth_2: transparentize(black, 0.8) 0 2px, + inset transparentize(white, 0.98) 0 1px; $comment_shadow: $shadow_depth_2; -$node_shadow: transparentize(black, 0.8) 0 2px, transparentize(black, 0.8) 0 2px 4px; +$node_shadow: transparentize(black, 0.8) 0 2px, + transparentize(black, 0.8) 0 2px 4px; $tag_height: 26px; @@ -73,15 +77,18 @@ $input_shadow_error: inset $red 0 0 0 1px; $input_shadow_filled: $input_shadow; @mixin outer_shadow() { - box-shadow: inset transparentize(white, 0.95) 0 1px, transparentize(black, 0.8) -1px -1px; + box-shadow: inset transparentize(white, 0.95) 0 1px, + transparentize(black, 0.8) -1px -1px; } @mixin inner_shadow() { - box-shadow: inset transparentize(white, 0.95) 0 -1px, inset transparentize(black, 0.5) 0 1px; + box-shadow: inset transparentize(white, 0.95) 0 -1px, + inset transparentize(black, 0.5) 0 1px; } @mixin input_shadow() { - box-shadow: inset transparentize(white, 0.92) 0 -1px, inset transparentize(black, 0.8) 0 1px; + box-shadow: inset transparentize(white, 0.92) 0 -1px, + inset transparentize(black, 0.8) 0 1px; } @mixin modal_mixin() { @@ -97,7 +104,7 @@ $input_shadow_filled: $input_shadow; position: $position; &::after { - content: ' '; + content: " "; position: absolute; bottom: 0; left: 0;