1
0
Fork 0
mirror of https://github.com/muerwre/vault-frontend.git synced 2025-04-25 12:56:41 +07:00

showing comments left count

This commit is contained in:
Fedor Katurov 2020-04-09 13:48:26 +07:00
parent 911beea4ad
commit 319e66616c
9 changed files with 101 additions and 115 deletions

View file

@ -9,34 +9,59 @@ import { IUser } from '~/redux/auth/types';
import { canEditComment } from '~/utils/node'; import { canEditComment } from '~/utils/node';
import { nodeLockComment, nodeEditComment } from '~/redux/node/actions'; import { nodeLockComment, nodeEditComment } from '~/redux/node/actions';
import { INodeState } from '~/redux/node/reducer'; import { INodeState } from '~/redux/node/reducer';
import { COMMENTS_DISPLAY } from '~/redux/node/constants';
import { plural } from '~/utils/dom';
interface IProps { interface IProps {
comments?: IComment[]; comments?: IComment[];
comment_data: INodeState['comment_data']; comment_data: INodeState['comment_data'];
comment_count: INodeState['comment_count'];
user: IUser; user: IUser;
onDelete: typeof nodeLockComment; onDelete: typeof nodeLockComment;
onEdit: typeof nodeEditComment; onEdit: typeof nodeEditComment;
order?: 'ASC' | 'DESC';
} }
const NodeComments: FC<IProps> = memo(({ comments, comment_data, user, onDelete, onEdit }) => { const NodeComments: FC<IProps> = memo(
const groupped: ICommentGroup[] = useMemo(() => comments.reduce(groupCommentsByUser, []), [ ({ comments, comment_data, user, onDelete, onEdit, comment_count = 0, order = 'DESC' }) => {
comments, const comments_left = useMemo(() => Math.max(0, comment_count - comments.length), [
]); comments,
comment_count,
]);
return ( const groupped: ICommentGroup[] = useMemo(
<div className={styles.wrap}> () => (order === 'DESC' ? [...comments].reverse() : comments).reduce(groupCommentsByUser, []),
{groupped.map(group => ( [comments, order]
<Comment );
key={group.ids.join()}
comment_group={group} return (
comment_data={comment_data} <div className={styles.wrap}>
can_edit={canEditComment(group, user)} {comment_count > 0 && (
onDelete={onDelete} <div className={styles.more}>
onEdit={onEdit} Показать ещё{' '}
/> {plural(
))} Math.min(comments_left, COMMENTS_DISPLAY),
</div> 'комментарий',
); 'комментария',
}); 'комментариев'
)}
{comments_left > COMMENTS_DISPLAY ? ` из ${comments_left} оставшихся` : ''}
</div>
)}
{groupped.map(group => (
<Comment
key={group.ids.join()}
comment_group={group}
comment_data={comment_data}
can_edit={canEditComment(group, user)}
onDelete={onDelete}
onEdit={onEdit}
/>
))}
</div>
);
}
);
export { NodeComments }; export { NodeComments };

View file

@ -7,3 +7,17 @@
} }
} }
} }
.more {
padding: $gap;
box-sizing: border-box;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
background: darken($comment_bg, 8%);
border-radius: $radius;
text-transform: uppercase;
color: darken(white, 50%);
font: $font_14_regular;
}

View file

@ -31,7 +31,7 @@ type IProps = ReturnType<typeof mapStateToProps> &
const id = 696; const id = 696;
const BorisLayoutUnconnected: FC<IProps> = ({ const BorisLayoutUnconnected: FC<IProps> = ({
node: { is_loading, is_loading_comments, comments = [], comment_data }, node: { is_loading, is_loading_comments, comments = [], comment_data, comment_count },
user, user,
user: { is_user }, user: { is_user },
nodeLoadNode, nodeLoadNode,
@ -88,6 +88,7 @@ const BorisLayoutUnconnected: FC<IProps> = ({
<NodeComments <NodeComments
comments={comments} comments={comments}
comment_data={comment_data} comment_data={comment_data}
comment_count={comment_count}
user={user} user={user}
onDelete={nodeLockComment} onDelete={nodeLockComment}
onEdit={nodeEditComment} onEdit={nodeEditComment}

View file

@ -48,7 +48,15 @@ const NodeLayoutUnconnected: FC<IProps> = memo(
match: { match: {
params: { id }, params: { id },
}, },
node: { is_loading, is_loading_comments, comments = [], current: node, related, comment_data }, node: {
is_loading,
is_loading_comments,
comments = [],
current: node,
related,
comment_data,
comment_count,
},
user, user,
user: { is_user }, user: { is_user },
nodeGotoNode, nodeGotoNode,
@ -136,9 +144,11 @@ const NodeLayoutUnconnected: FC<IProps> = memo(
<NodeComments <NodeComments
comments={comments} comments={comments}
comment_data={comment_data} comment_data={comment_data}
comment_count={comment_count}
user={user} user={user}
onDelete={nodeLockComment} onDelete={nodeLockComment}
onEdit={nodeEditComment} onEdit={nodeEditComment}
order="DESC"
/> />
)} )}

View file

@ -3,6 +3,7 @@ import { INode, IResultWithStatus, IComment } from '../types';
import { API } from '~/constants/api'; import { API } from '~/constants/api';
import { nodeUpdateTags, nodeLike, nodeStar, nodeLock, nodeLockComment } from './actions'; import { nodeUpdateTags, nodeLike, nodeStar, nodeLock, nodeLockComment } from './actions';
import { INodeState } from './reducer'; import { INodeState } from './reducer';
import { COMMENTS_DISPLAY } from './constants';
export const postNode = ({ export const postNode = ({
access, access,
@ -95,14 +96,16 @@ export const postNodeComment = ({
export const getNodeComments = ({ export const getNodeComments = ({
id, id,
access, access,
order = 'ASC', take = COMMENTS_DISPLAY,
skip = 0,
}: { }: {
id: number; id: number;
access: string; access: string;
order: 'ASC' | 'DESC'; take?: number;
skip?: number;
}): Promise<IResultWithStatus<{ comments: Comment[] }>> => }): Promise<IResultWithStatus<{ comments: Comment[] }>> =>
api api
.get(API.NODE.COMMENT(id), configWithToken(access, { params: { order } })) .get(API.NODE.COMMENT(id), configWithToken(access, { params: { take, skip } }))
.then(resultMiddleware) .then(resultMiddleware)
.catch(errorMiddleware); .catch(errorMiddleware);

View file

@ -97,94 +97,6 @@ export const EMPTY_COMMENT: IComment = {
temp_ids: [], temp_ids: [],
is_private: false, is_private: false,
user: null, user: null,
/*
files: [
{
name: 'screenshot_2019-09-29_21-13-38_502253296-1572589001092.png',
path: 'uploads/2019/10/image/',
full_path:
'public/uploads/2019/10/image/screenshot_2019-09-29_21-13-38_502253296-1572589001092.png',
url:
'REMOTE_CURRENT://uploads/2019/10/image/screenshot_2019-09-29_21-13-38_502253296-1572589001092.png',
size: 994331,
type: 'image',
mime: 'image/png',
metadata: {
width: 1919,
height: 1079,
},
id: 8709,
},
{
name: 'screenshot_2019-09-29_19-05-41_148603009-1572589001080.png',
path: 'uploads/2019/10/image/',
full_path:
'public/uploads/2019/10/image/screenshot_2019-09-29_19-05-41_148603009-1572589001080.png',
url:
'REMOTE_CURRENT://uploads/2019/10/image/screenshot_2019-09-29_19-05-41_148603009-1572589001080.png',
size: 2145,
type: 'image',
mime: 'image/png',
metadata: {
width: 445,
height: 446,
},
id: 8708,
},
{
name: 'screenshot_2019-09-29_21-13-26_924738012-1572589001110.png',
path: 'uploads/2019/10/image/',
full_path:
'public/uploads/2019/10/image/screenshot_2019-09-29_21-13-26_924738012-1572589001110.png',
url:
'REMOTE_CURRENT://uploads/2019/10/image/screenshot_2019-09-29_21-13-26_924738012-1572589001110.png',
size: 881224,
type: 'image',
mime: 'image/png',
metadata: {
width: 1919,
height: 1079,
},
id: 8710,
},
{
name:
'Advent_Chamber_Orchestra_-_05_-_Dvorak_-_Serenade_for_Strings_Op22_in_E_Major_larghetto-1572597841834.mp3',
path: 'uploads/2019/10/audio/',
full_path:
'public/uploads/2019/10/audio/Advent_Chamber_Orchestra_-_05_-_Dvorak_-_Serenade_for_Strings_Op22_in_E_Major_larghetto-1572597841834.mp3',
url:
'REMOTE_CURRENT://uploads/2019/10/audio/Advent_Chamber_Orchestra_-_05_-_Dvorak_-_Serenade_for_Strings_Op22_in_E_Major_larghetto-1572597841834.mp3',
size: 11155009,
type: 'audio',
mime: 'audio/mp3',
metadata: {
duration: 343.3795918367347,
id3title: 'Dvorak - Serenade for Strings Op22 in E Major larghetto',
id3artist: 'Advent Chamber Orchestra',
},
id: 8714,
},
{
name: '182a0d234aef882a58f240c5c0812bb2cad9506a875ca4c7c07d8f9f077ebb00-1572597841829.mp3',
path: 'uploads/2019/10/audio/',
full_path:
'public/uploads/2019/10/audio/182a0d234aef882a58f240c5c0812bb2cad9506a875ca4c7c07d8f9f077ebb00-1572597841829.mp3',
url:
'REMOTE_CURRENT://uploads/2019/10/audio/182a0d234aef882a58f240c5c0812bb2cad9506a875ca4c7c07d8f9f077ebb00-1572597841829.mp3',
size: 6038673,
type: 'audio',
mime: 'audio/mp3',
metadata: {
duration: 251.58530612244897,
id3title: null,
id3artist: null,
},
id: 8713,
},
],
*/
}; };
export const NODE_EDITORS = { export const NODE_EDITORS = {
@ -219,3 +131,5 @@ export const NODE_SETTINGS = {
MAX_FILES: 16, MAX_FILES: 16,
MAX_IMAGE_ASPECT: 1.2, MAX_IMAGE_ASPECT: 1.2,
}; };
export const COMMENTS_DISPLAY = 25;

View file

@ -12,6 +12,7 @@ export type INodeState = Readonly<{
similar: Partial<INode[]>; similar: Partial<INode[]>;
}; };
comment_data: Record<number, IComment>; comment_data: Record<number, IComment>;
comment_count: number;
current_cover_image: IFile; current_cover_image: IFile;
error: string; error: string;
@ -35,6 +36,7 @@ const INITIAL_STATE: INodeState = {
...EMPTY_COMMENT, ...EMPTY_COMMENT,
}, },
}, },
comment_count: 0,
comments: [], comments: [],
related: null, related: null,
current_cover_image: null, current_cover_image: null,

View file

@ -2,7 +2,13 @@ import { takeLatest, call, put, select, delay, all } from 'redux-saga/effects';
import { push } from 'connected-react-router'; import { push } from 'connected-react-router';
import omit from 'ramda/es/omit'; import omit from 'ramda/es/omit';
import { NODE_ACTIONS, EMPTY_NODE, EMPTY_COMMENT, NODE_EDITOR_DATA } from './constants'; import {
NODE_ACTIONS,
EMPTY_NODE,
EMPTY_COMMENT,
NODE_EDITOR_DATA,
COMMENTS_DISPLAY,
} from './constants';
import { import {
nodeSave, nodeSave,
nodeSetSaveErrors, nodeSetSaveErrors,
@ -132,19 +138,20 @@ function* onNodeLoad({ id, order = 'ASC' }: ReturnType<typeof nodeLoadNode>) {
const { const {
comments: { comments: {
data: { comments }, data: { comments, comment_count },
}, },
related: { related: {
data: { related }, data: { related },
}, },
} = yield all({ } = yield all({
comments: call(reqWrapper, getNodeComments, { id, order }), comments: call(reqWrapper, getNodeComments, { id, take: COMMENTS_DISPLAY, skip: 0 }),
related: call(reqWrapper, getNodeRelated, { id }), related: call(reqWrapper, getNodeRelated, { id }),
}); });
yield put( yield put(
nodeSet({ nodeSet({
comments, comments,
comment_count,
related, related,
is_loading_comments: false, is_loading_comments: false,
comment_data: { 0: { ...EMPTY_COMMENT } }, comment_data: { 0: { ...EMPTY_COMMENT } },

View file

@ -152,3 +152,13 @@ export const getYoutubeThumb = (url: string) => {
return match && match[1] ? `https://i.ytimg.com/vi/${match[1]}/hqdefault.jpg` : null; return match && match[1] ? `https://i.ytimg.com/vi/${match[1]}/hqdefault.jpg` : null;
}; };
export function plural(n: number, one: string, two: string, five: string) {
if (n % 10 === 1 && n % 100 !== 11) {
return `${n} ${one}`;
} else if (n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20)) {
return `${n} ${two}`;
} else {
return `${n} ${five}`;
}
}