diff --git a/frontend/app/components/comment/__body/comment__body.scss b/frontend/app/components/comment/__body/comment__body.scss index 87324a34..02eaba19 100644 --- a/frontend/app/components/comment/__body/comment__body.scss +++ b/frontend/app/components/comment/__body/comment__body.scss @@ -1,5 +1,3 @@ .comment__body { - + .comment__user-info { - margin-top: 8px; - } + padding-left: 17px; } diff --git a/frontend/app/components/comment/__info/comment__info.scss b/frontend/app/components/comment/__info/comment__info.scss index f97b63df..ea521989 100644 --- a/frontend/app/components/comment/__info/comment__info.scss +++ b/frontend/app/components/comment/__info/comment__info.scss @@ -2,9 +2,10 @@ display: flex; align-items: center; flex-wrap: wrap; - margin-bottom: 6px; + height: 20px; + margin-bottom: 12px; padding-right: 84px; font-size: 14px; - line-height: 16px; + line-height: 20px; position: relative; } diff --git a/frontend/app/components/comment/_collapsed/comment_collapsed.scss b/frontend/app/components/comment/_collapsed/comment_collapsed.scss index 07c47849..07e7567e 100644 --- a/frontend/app/components/comment/_collapsed/comment_collapsed.scss +++ b/frontend/app/components/comment/_collapsed/comment_collapsed.scss @@ -1,8 +1,28 @@ .comment_collapsed { + padding: 12px 0 12px 28px; + > .comment__body { .comment__text, .comment__actions { display: none; } } + .comment__info { + margin-bottom: 0; + opacity: 0.8; + } + .comment__avatar { + width: 20px; + height: 20px; + opacity: 0.6; + } + + .comment__username, + .comment__time { + font-style: italic; + } + + .comment__score { + top: 2px; + } } diff --git a/frontend/app/components/comment/_view/_user/comment_view_user.scss b/frontend/app/components/comment/_view/_user/comment_view_user.scss index 5428e144..74c2acf4 100644 --- a/frontend/app/components/comment/_view/_user/comment_view_user.scss +++ b/frontend/app/components/comment/_view/_user/comment_view_user.scss @@ -10,6 +10,10 @@ margin-bottom: 0.2rem; } + .comment__body { + padding-left: 0; + } + .comment__title-link { color: #0e7e9d; font-weight: bold; diff --git a/frontend/app/components/comment/comment.scss b/frontend/app/components/comment/comment.scss index 6ddf4439..8931953c 100644 --- a/frontend/app/components/comment/comment.scss +++ b/frontend/app/components/comment/comment.scss @@ -1,7 +1,5 @@ .comment { display: block; - position: relative; - overflow: hidden; padding: 12px 0 8px; font-size: 16px; line-height: 1.2; @@ -14,3 +12,10 @@ } } } + +.comment_level_6 { + .comment__text, + .comment__actions { + padding-left: 0; + } +} diff --git a/frontend/app/components/comment/comment.tsx b/frontend/app/components/comment/comment.tsx index 3c508f3d..d48d0220 100644 --- a/frontend/app/components/comment/comment.tsx +++ b/frontend/app/components/comment/comment.tsx @@ -296,12 +296,6 @@ export class Comment extends Component { } }; - toggleCollapse = () => { - this.props.setReplyEditState!({ id: this.props.data.id, state: CommentMode.None }); - - this.props.setCollapse!(this.props.data.id, !this.props.collapsed); - }; - copyComment = () => { const username = this.props.data.user.name; const time = this.props.data.time; @@ -514,6 +508,7 @@ export class Comment extends Component { editing: props.view === 'main' && isEditing, theme: props.view === 'preview' ? null : props.theme, level: props.level, + collapsed: props.collapsed, }; if (props.view === 'preview') { @@ -572,96 +567,88 @@ export class Comment extends Component { )} -
-
- {props.view !== 'user' && } +
+ {props.view !== 'user' && !props.collapsed && ( + + )} - {props.view !== 'user' && ( - - {o.user.name} - - )} - - {isAdmin && props.view !== 'user' && ( - - )} - - {!isAdmin && !!o.user.verified && props.view !== 'user' && ( - - )} - - - {o.time} - - - {!!props.level && props.level > 0 && props.view === 'main' && ( - this.scrollToParent(e)} - > - {' '} - - )} - - {props.isUserBanned && props.view !== 'user' && Blocked} - - {isAdmin && !props.isUserBanned && props.data.delete && Deleted} - - {!props.disabled && props.view === 'main' && ( - - {props.collapsed ? '+' : '−'} - - )} - - - - Vote up - - - - {o.score.sign} - {o.score.value} - - - - Vote down - + {props.view !== 'user' && ( + + {o.user.name} -
+ )} + {isAdmin && props.view !== 'user' && ( + + )} + + {!isAdmin && !!o.user.verified && props.view !== 'user' && ( + + )} + + + {o.time} + + + {!!props.level && props.level > 0 && props.view === 'main' && ( + this.scrollToParent(e)} + > + {' '} + + )} + + {props.isUserBanned && props.view !== 'user' && Blocked} + + {isAdmin && !props.isUserBanned && props.data.delete && Deleted} + + + + Vote up + + + + {o.score.sign} + {o.score.value} + + + + Vote down + + +
+
{!!state.voteErrorMessage && (
Voting error: {state.voteErrorMessage} diff --git a/frontend/app/components/comment/connected-comment.tsx b/frontend/app/components/comment/connected-comment.tsx index ebc79da4..6ff516b3 100644 --- a/frontend/app/components/comment/connected-comment.tsx +++ b/frontend/app/components/comment/connected-comment.tsx @@ -22,7 +22,6 @@ import { putVote, setCommentMode, } from '@app/store/comments/actions'; -import { setCollapse } from '@app/store/thread/actions'; import { blockUser, unblockUser, hideUser, setVerifiedStatus } from '@app/store/user/actions'; import { Comment, Props } from './comment'; @@ -65,7 +64,6 @@ export const boundActions = bindActions({ updateComment, removeComment, setReplyEditState: setCommentMode, - setCollapse, setPinState, putCommentVote: putVote, blockUser, diff --git a/frontend/app/components/thread/__collapse/thread__collapse.scss b/frontend/app/components/thread/__collapse/thread__collapse.scss new file mode 100644 index 00000000..a2980e18 --- /dev/null +++ b/frontend/app/components/thread/__collapse/thread__collapse.scss @@ -0,0 +1,72 @@ +.thread__collapse { + height: 100%; + width: 11px; + position: absolute; + top: 50px; + left: -4px; + cursor: pointer; + + &:after { + display: block; + content: ''; + position: absolute; + left: 5px; + top: 0; + border-left: 1px dotted #d9d9d9; + height: 100%; + } + + &:hover:after { + transform: translateX(-1px); + border-left: 3px solid #777; + z-index: 10; + } +} + +.thread__collapse_collapsed { + width: 18px; + height: 18px; + top: 12px; + left: 0; + display: flex; + border-radius: 2px; + text-align: center; + opacity: 0.8; + border-radius: 2px; + border: 1px solid; + + &:after { + display: none; + } + + &:hover { + opacity: 1; + } + + &:hover:after { + transform: translateX(0); + } +} + +.thread__collapse_collapsed > div { + position: relative; + top: 6px; + left: 3px; + width: 12px; + height: 2px; + border-bottom: 2px solid; + + &:before, + &:after { + content: ''; + width: 100%; + height: 2px; + border-bottom: 2px solid; + position: absolute; + top: -4px; + left: 0; + } + &:after { + top: 4px !important; + } +} diff --git a/frontend/app/components/thread/_theme_dark/thread_theme_dark.scss b/frontend/app/components/thread/_theme_dark/thread_theme_dark.scss index c6c177b8..433c9ea3 100644 --- a/frontend/app/components/thread/_theme_dark/thread_theme_dark.scss +++ b/frontend/app/components/thread/_theme_dark/thread_theme_dark.scss @@ -1,3 +1,11 @@ .thread_theme_dark { - border-left-color: rgba(255, 255, 255, 0.35); + .thread__collapse { + &:after { + border-color: #505050; + } + + &:hover:after { + border-color: #fff; + } + } } diff --git a/frontend/app/components/thread/index.ts b/frontend/app/components/thread/index.ts index f8d9ab6a..55754c9d 100644 --- a/frontend/app/components/thread/index.ts +++ b/frontend/app/components/thread/index.ts @@ -1,4 +1,6 @@ -export { ConnectedThread as Thread } from './thread'; +export { Thread } from './thread'; import './thread.scss'; import './_theme_dark/thread_theme_dark.scss'; + +import './__collapse/thread__collapse.scss'; diff --git a/frontend/app/components/thread/thread.scss b/frontend/app/components/thread/thread.scss index b735d1a3..6bb92952 100644 --- a/frontend/app/components/thread/thread.scss +++ b/frontend/app/components/thread/thread.scss @@ -1,9 +1,12 @@ -.thread_indented { - padding-left: 17px; - border-left: 1px dotted rgba(0, 0, 0, 0.15); +.thread { + position: relative; + overflow: hidden; } -.thread_level_6 { - border-left: none; - padding-left: 0; +.thread_indented { + margin-left: 17px; +} + +.thread_level_6 .thread_level_6 { + margin-left: 0; } diff --git a/frontend/app/components/thread/thread.tsx b/frontend/app/components/thread/thread.tsx index 7b107544..a1e981e9 100644 --- a/frontend/app/components/thread/thread.tsx +++ b/frontend/app/components/thread/thread.tsx @@ -1,26 +1,18 @@ /** @jsx createElement */ -import { createElement, RenderableProps, FunctionComponent } from 'preact'; -import { useStore } from 'react-redux'; +import { createElement, FunctionComponent } from 'preact'; +import { useSelector, useDispatch } from 'react-redux'; +import { useCallback } from 'preact/hooks'; import b from 'bem-react-helper'; -import { ConnectedComment as Comment } from '@app/components/comment/connected-comment'; import { Comment as CommentInterface } from '@app/common/types'; -import { getThreadIsCollapsed } from '@app/store/thread/getters'; +import { getHandleClickProps } from '@app/common/accessibility'; import { StoreState } from '@app/store'; -import { InView } from '../root/in-view/in-view'; +import { setCollapse } from '@app/store/thread/actions'; +import { getThreadIsCollapsed } from '@app/store/thread/getters'; +import { InView } from '@app/components/root/in-view/in-view'; +import { ConnectedComment as Comment } from '@app/components/comment/connected-comment'; -const mapStateToProps = (state: StoreState, props: { id: CommentInterface['id'] }) => { - const comment = state.comments[props.id]; - return { - comment, - childs: state.childComments[props.id], - collapsed: getThreadIsCollapsed(comment)(state), - isCommentsDisabled: !!state.info.read_only, - theme: state.theme, - }; -}; - -interface OwnProps { +interface Props { id: CommentInterface['id']; childs?: CommentInterface['id'][]; level: number; @@ -29,10 +21,21 @@ interface OwnProps { getPreview(text: string): Promise; } -type Props = OwnProps & ReturnType; +const commentSelector = (id: string) => (state: StoreState) => { + const { theme, comments, childComments } = state; + const comment = comments[id]; + const childs = childComments[id]; + const collapsed = getThreadIsCollapsed(comment)(state); -function Thread(props: RenderableProps) { - const { collapsed, comment, childs, level, theme } = props; + return { comment, childs, collapsed, theme }; +}; + +export const Thread: FunctionComponent = ({ id, level, mix, getPreview }) => { + const dispatch = useDispatch(); + const { collapsed, comment, childs, theme } = useSelector(commentSelector(id)); + const collapse = useCallback(() => { + dispatch(setCollapse(id, !collapsed)); + }, [id, collapsed]); if (comment.hidden) return null; @@ -41,7 +44,7 @@ function Thread(props: RenderableProps) { return (
@@ -49,7 +52,7 @@ function Thread(props: RenderableProps) { {inviewProps => ( inviewProps.ref(ref)} - key={`comment-${props.id}`} + key={`comment-${id}`} view="main" data={comment} repliesCount={repliesCount} @@ -62,14 +65,18 @@ function Thread(props: RenderableProps) { {!collapsed && childs && !!childs.length && - childs.map(id => ( - + childs.map(currentId => ( + ))} + {level < 6 && ( +
+
+
+ )}
); -} - -export const ConnectedThread: FunctionComponent = props => { - const providedProps = mapStateToProps(useStore().getState(), props); - return ; };