mirror of
https://github.com/muerwre/vault-frontend.git
synced 2025-04-25 21:06:42 +07:00
showing comments left count
This commit is contained in:
parent
911beea4ad
commit
319e66616c
9 changed files with 101 additions and 115 deletions
|
@ -9,22 +9,46 @@ 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' }) => {
|
||||||
|
const comments_left = useMemo(() => Math.max(0, comment_count - comments.length), [
|
||||||
comments,
|
comments,
|
||||||
|
comment_count,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const groupped: ICommentGroup[] = useMemo(
|
||||||
|
() => (order === 'DESC' ? [...comments].reverse() : comments).reduce(groupCommentsByUser, []),
|
||||||
|
[comments, order]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.wrap}>
|
<div className={styles.wrap}>
|
||||||
|
{comment_count > 0 && (
|
||||||
|
<div className={styles.more}>
|
||||||
|
Показать ещё{' '}
|
||||||
|
{plural(
|
||||||
|
Math.min(comments_left, COMMENTS_DISPLAY),
|
||||||
|
'комментарий',
|
||||||
|
'комментария',
|
||||||
|
'комментариев'
|
||||||
|
)}
|
||||||
|
{comments_left > COMMENTS_DISPLAY ? ` из ${comments_left} оставшихся` : ''}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{groupped.map(group => (
|
{groupped.map(group => (
|
||||||
<Comment
|
<Comment
|
||||||
key={group.ids.join()}
|
key={group.ids.join()}
|
||||||
|
@ -37,6 +61,7 @@ const NodeComments: FC<IProps> = memo(({ comments, comment_data, user, onDelete,
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
export { NodeComments };
|
export { NodeComments };
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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 } },
|
||||||
|
|
|
@ -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}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue