mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-21 03:18:23 +00:00
Merge pull request #7082 from haiwen/update-user-notifications
Update user notifications
This commit is contained in:
@@ -4,6 +4,8 @@ import dayjs from 'dayjs';
|
||||
import relativeTime from 'dayjs/plugin/relativeTime';
|
||||
import { gettext, siteRoot } from '../../utils/constants';
|
||||
import { Utils } from '../../utils/utils';
|
||||
import { processor } from '@seafile/seafile-editor';
|
||||
import '../../css/notice-item.css';
|
||||
|
||||
const propTypes = {
|
||||
noticeItem: PropTypes.object.isRequired,
|
||||
@@ -24,6 +26,8 @@ const MSG_TYPE_SAML_SSO_FAILED = 'saml_sso_failed';
|
||||
const MSG_TYPE_REPO_SHARE_PERM_CHANGE = 'repo_share_perm_change';
|
||||
const MSG_TYPE_REPO_SHARE_PERM_DELETE = 'repo_share_perm_delete';
|
||||
const MSG_TYPE_FACE_CLUSTER = 'face_cluster';
|
||||
const MSG_TYPE_SEADOC_REPLY = 'reply';
|
||||
const MSG_TYPE_SEADOC_COMMENT = 'comment';
|
||||
|
||||
dayjs.extend(relativeTime);
|
||||
|
||||
@@ -35,35 +39,26 @@ class NoticeItem extends React.Component {
|
||||
let detail = noticeItem.detail;
|
||||
|
||||
if (noticeType === MSG_TYPE_ADD_USER_TO_GROUP) {
|
||||
|
||||
let avatar_url = detail.group_staff_avatar_url;
|
||||
|
||||
let groupStaff = detail.group_staff_name;
|
||||
|
||||
// group name does not support special characters
|
||||
let userHref = siteRoot + 'profile/' + detail.group_staff_email + '/';
|
||||
let groupHref = siteRoot + 'group/' + detail.group_id + '/';
|
||||
let groupName = detail.group_name;
|
||||
|
||||
let username = detail.group_staff_name;
|
||||
let notice = gettext('User {user_link} has added you to {group_link}');
|
||||
let userLink = '<a href=' + userHref + '>' + groupStaff + '</a>';
|
||||
let groupLink = '<a href=' + groupHref + '>' + groupName + '</a>';
|
||||
|
||||
notice = notice.replace('{user_link}', userLink);
|
||||
notice = notice.replace('{group_link}', groupLink);
|
||||
|
||||
return { avatar_url, notice };
|
||||
return { avatar_url, notice, username };
|
||||
}
|
||||
|
||||
if (noticeType === MSG_TYPE_REPO_SHARE) {
|
||||
|
||||
let avatar_url = detail.share_from_user_avatar_url;
|
||||
|
||||
let shareFrom = detail.share_from_user_name;
|
||||
|
||||
let repoName = detail.repo_name;
|
||||
let repoUrl = siteRoot + 'library/' + detail.repo_id + '/' + repoName + '/';
|
||||
|
||||
let path = detail.path;
|
||||
let notice = '';
|
||||
// 1. handle translate
|
||||
@@ -72,21 +67,17 @@ class NoticeItem extends React.Component {
|
||||
} else { // share folder
|
||||
notice = gettext('{share_from} has shared a folder named {repo_link} to you.');
|
||||
}
|
||||
|
||||
// 2. handle xss(cross-site scripting)
|
||||
notice = notice.replace('{share_from}', shareFrom);
|
||||
notice = notice.replace('{repo_link}', `{tagA}${repoName}{/tagA}`);
|
||||
notice = Utils.HTMLescape(notice);
|
||||
|
||||
// 3. add jump link
|
||||
notice = notice.replace('{tagA}', `<a href='${Utils.encodePath(repoUrl)}'>`);
|
||||
notice = notice.replace('{/tagA}', '</a>');
|
||||
|
||||
return { avatar_url, notice };
|
||||
return { avatar_url, notice, username: shareFrom };
|
||||
}
|
||||
|
||||
if (noticeType === MSG_TYPE_REPO_SHARE_PERM_CHANGE) {
|
||||
|
||||
let avatar_url = detail.share_from_user_avatar_url;
|
||||
let shareFrom = detail.share_from_user_name;
|
||||
let permission = detail.permission;
|
||||
@@ -100,22 +91,18 @@ class NoticeItem extends React.Component {
|
||||
} else { // share folder
|
||||
notice = gettext('{share_from} has changed the permission of folder {repo_link} to {permission}.');
|
||||
}
|
||||
|
||||
// 2. handle xss(cross-site scripting)
|
||||
notice = notice.replace('{share_from}', shareFrom);
|
||||
notice = notice.replace('{repo_link}', `{tagA}${repoName}{/tagA}`);
|
||||
notice = notice.replace('{permission}', permission);
|
||||
notice = Utils.HTMLescape(notice);
|
||||
|
||||
// 3. add jump link
|
||||
notice = notice.replace('{tagA}', `<a href='${Utils.encodePath(repoUrl)}'>`);
|
||||
notice = notice.replace('{/tagA}', '</a>');
|
||||
|
||||
return { avatar_url, notice };
|
||||
return { avatar_url, notice, username: shareFrom };
|
||||
}
|
||||
|
||||
if (noticeType === MSG_TYPE_REPO_SHARE_PERM_DELETE) {
|
||||
|
||||
let avatar_url = detail.share_from_user_avatar_url;
|
||||
let shareFrom = detail.share_from_user_name;
|
||||
let repoName = detail.repo_name;
|
||||
@@ -127,26 +114,20 @@ class NoticeItem extends React.Component {
|
||||
} else { // share folder
|
||||
notice = gettext('{share_from} has cancelled the sharing of folder {repo_name}.');
|
||||
}
|
||||
|
||||
// 2. handle xss(cross-site scripting)
|
||||
notice = notice.replace('{share_from}', shareFrom);
|
||||
notice = notice.replace('{repo_name}', repoName);
|
||||
notice = Utils.HTMLescape(notice);
|
||||
return { avatar_url, notice };
|
||||
return { avatar_url, notice, username: shareFrom };
|
||||
}
|
||||
|
||||
if (noticeType === MSG_TYPE_REPO_SHARE_TO_GROUP) {
|
||||
|
||||
let avatar_url = detail.share_from_user_avatar_url;
|
||||
|
||||
let shareFrom = detail.share_from_user_name;
|
||||
|
||||
let repoName = detail.repo_name;
|
||||
let repoUrl = siteRoot + 'library/' + detail.repo_id + '/' + repoName + '/';
|
||||
|
||||
let groupUrl = siteRoot + 'group/' + detail.group_id + '/';
|
||||
let groupName = detail.group_name;
|
||||
|
||||
let path = detail.path;
|
||||
let notice = '';
|
||||
// 1. handle translate
|
||||
@@ -155,60 +136,50 @@ class NoticeItem extends React.Component {
|
||||
} else {
|
||||
notice = gettext('{share_from} has shared a folder named {repo_link} to group {group_link}.');
|
||||
}
|
||||
|
||||
// 2. handle xss(cross-site scripting)
|
||||
notice = notice.replace('{share_from}', shareFrom);
|
||||
notice = notice.replace('{repo_link}', `{tagA}${repoName}{/tagA}`);
|
||||
notice = notice.replace('{group_link}', `{tagB}${groupName}{/tagB}`);
|
||||
notice = Utils.HTMLescape(notice);
|
||||
|
||||
// 3. add jump link
|
||||
notice = notice.replace('{tagA}', `<a href='${Utils.encodePath(repoUrl)}'>`);
|
||||
notice = notice.replace('{/tagA}', '</a>');
|
||||
notice = notice.replace('{tagB}', `<a href='${Utils.encodePath(groupUrl)}'>`);
|
||||
notice = notice.replace('{/tagB}', '</a>');
|
||||
return { avatar_url, notice };
|
||||
return { avatar_url, notice, username: shareFrom };
|
||||
}
|
||||
|
||||
if (noticeType === MSG_TYPE_REPO_TRANSFER) {
|
||||
|
||||
let avatar_url = detail.transfer_from_user_avatar_url;
|
||||
|
||||
let repoOwner = detail.transfer_from_user_name;
|
||||
|
||||
let repoName = detail.repo_name;
|
||||
let repoUrl = siteRoot + 'library/' + detail.repo_id + '/' + repoName + '/';
|
||||
// 1. handle translate
|
||||
let notice = gettext('{user} has transfered a library named {repo_link} to you.');
|
||||
|
||||
// 2. handle xss(cross-site scripting)
|
||||
notice = notice.replace('{user}', repoOwner);
|
||||
notice = notice.replace('{repo_link}', `{tagA}${repoName}{/tagA}`);
|
||||
notice = Utils.HTMLescape(notice);
|
||||
|
||||
// 3. add jump link
|
||||
notice = notice.replace('{tagA}', `<a href=${Utils.encodePath(repoUrl)}>`);
|
||||
notice = notice.replace('{/tagA}', '</a>');
|
||||
return { avatar_url, notice };
|
||||
return { avatar_url, notice, username: repoOwner };
|
||||
}
|
||||
|
||||
if (noticeType === MSG_TYPE_FILE_UPLOADED) {
|
||||
let avatar_url = detail.uploaded_user_avatar_url;
|
||||
let fileName = detail.file_name;
|
||||
let fileLink = siteRoot + 'lib/' + detail.repo_id + '/' + 'file' + detail.file_path;
|
||||
|
||||
let folderName = detail.folder_name;
|
||||
let folderLink = siteRoot + 'library/' + detail.repo_id + '/' + detail.repo_name + detail.folder_path;
|
||||
let notice = '';
|
||||
if (detail.repo_id) { // todo is repo exist ?
|
||||
// 1. handle translate
|
||||
notice = gettext('A file named {upload_file_link} is uploaded to {uploaded_link}.');
|
||||
|
||||
// 2. handle xss(cross-site scripting)
|
||||
notice = notice.replace('{upload_file_link}', `{tagA}${fileName}{/tagA}`);
|
||||
notice = notice.replace('{uploaded_link}', `{tagB}${folderName}{/tagB}`);
|
||||
notice = Utils.HTMLescape(notice);
|
||||
|
||||
// 3. add jump link
|
||||
notice = notice.replace('{tagA}', `<a href=${Utils.encodePath(fileLink)}>`);
|
||||
notice = notice.replace('{/tagA}', '</a>');
|
||||
@@ -217,7 +188,6 @@ class NoticeItem extends React.Component {
|
||||
} else {
|
||||
// 1. handle translate
|
||||
notice = gettext('A file named {upload_file_link} is uploaded.');
|
||||
|
||||
// 2. handle xss(cross-site scripting)
|
||||
notice = notice.replace('{upload_file_link}', `${fileName}`);
|
||||
notice = Utils.HTMLescape(notice);
|
||||
@@ -341,17 +311,11 @@ class NoticeItem extends React.Component {
|
||||
}
|
||||
|
||||
if (noticeType === MSG_TYPE_DELETED_FILES) {
|
||||
const {
|
||||
repo_id,
|
||||
repo_name,
|
||||
} = detail;
|
||||
|
||||
const { repo_id, repo_name } = detail;
|
||||
const repoURL = `${siteRoot}library/${repo_id}/${encodeURIComponent(repo_name)}/`;
|
||||
const repoLink = `<a href=${repoURL} target="_blank">${Utils.HTMLescape(repo_name)}</a>`;
|
||||
|
||||
let notice = gettext('Your library {libraryName} has recently deleted a large number of files.');
|
||||
notice = notice.replace('{libraryName}', repoLink);
|
||||
|
||||
return { avatar_url: null, notice };
|
||||
}
|
||||
|
||||
@@ -371,15 +335,56 @@ class NoticeItem extends React.Component {
|
||||
if (noticeType === MSG_TYPE_SAML_SSO_FAILED) {
|
||||
const { error_msg } = detail;
|
||||
let notice = gettext(error_msg);
|
||||
|
||||
return { avatar_url: null, notice };
|
||||
}
|
||||
|
||||
if (noticeType === MSG_TYPE_SEADOC_COMMENT) {
|
||||
let avatar_url = detail.avatar_url;
|
||||
let notice = detail.comment;
|
||||
let username = detail.user_name;
|
||||
let is_resolved = detail.is_resolved;
|
||||
let sdoc_name = detail.sdoc_name;
|
||||
const repo_id = detail.repo_id;
|
||||
const sdoc_path = detail.sdoc_path;
|
||||
const sdoc_href = siteRoot + 'lib/' + repo_id + '/file' + sdoc_path;
|
||||
let sdoc_link = '<a href=' + sdoc_href + '>' + sdoc_name + '</a>';
|
||||
processor.process(notice, (error, vfile) => {
|
||||
notice = String(vfile);
|
||||
});
|
||||
if (is_resolved) {
|
||||
notice = 'Marked "' + detail.resolve_comment + '" as resolved in document ' + sdoc_link;
|
||||
} else {
|
||||
notice = 'Added a new comment in document ' + sdoc_link + ':' + notice;
|
||||
}
|
||||
return { avatar_url, username, notice };
|
||||
}
|
||||
|
||||
if (noticeType === MSG_TYPE_SEADOC_REPLY) {
|
||||
let avatar_url = detail.avatar_url;
|
||||
let notice = detail.reply;
|
||||
let username = detail.user_name;
|
||||
let is_resolved = detail.is_resolved;
|
||||
let sdoc_name = detail.sdoc_name;
|
||||
const repo_id = detail.repo_id;
|
||||
const sdoc_path = detail.sdoc_path;
|
||||
const sdoc_href = siteRoot + 'lib/' + repo_id + '/file' + sdoc_path;
|
||||
let sdoc_link = '<a href=' + sdoc_href + '>' + sdoc_name + '</a>';
|
||||
processor.process(notice, (error, vfile) => {
|
||||
notice = String(vfile);
|
||||
});
|
||||
if (is_resolved) {
|
||||
notice = 'Marked "' + detail.resolve_comment + '" as resolved in document ' + sdoc_link;
|
||||
} else {
|
||||
notice = 'Added a new reply in document ' + sdoc_link + ':' + notice;
|
||||
}
|
||||
return { avatar_url, username, notice };
|
||||
}
|
||||
|
||||
// if (noticeType === MSG_TYPE_GUEST_INVITATION_ACCEPTED) {
|
||||
|
||||
// }
|
||||
|
||||
return { avatar_url: null, notice: null };
|
||||
return { avatar_url: null, notice: null, username: null };
|
||||
}
|
||||
|
||||
onNoticeItemClick = () => {
|
||||
@@ -392,16 +397,19 @@ class NoticeItem extends React.Component {
|
||||
|
||||
render() {
|
||||
let noticeItem = this.props.noticeItem;
|
||||
let { avatar_url, notice } = this.generatorNoticeInfo();
|
||||
|
||||
let { avatar_url, username, notice } = this.generatorNoticeInfo();
|
||||
if (!avatar_url && !notice) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return this.props.tr ? (
|
||||
<tr className={noticeItem.seen ? 'read' : 'unread font-weight-bold'}>
|
||||
<tr className='notification-item'>
|
||||
<td className="text-center">
|
||||
{!noticeItem.seen && <span className="notification-point" onClick={this.onMarkNotificationRead}></span>}
|
||||
</td>
|
||||
<td>
|
||||
<img src={avatar_url} width="32" height="32" className="avatar" alt="" />
|
||||
<span className="ml-2 notification-user-name">{username || gettext('System')}</span>
|
||||
</td>
|
||||
<td className="pr-1 pr-md-8">
|
||||
<p className="m-0" dangerouslySetInnerHTML={{ __html: notice }}></p>
|
||||
@@ -411,13 +419,21 @@ class NoticeItem extends React.Component {
|
||||
</td>
|
||||
</tr>
|
||||
) : (
|
||||
<li onClick={this.onNoticeItemClick} className={noticeItem.seen ? 'read' : 'unread'}>
|
||||
<div className="notice-item">
|
||||
<div className="main-info">
|
||||
<img src={avatar_url} width="32" height="32" className="avatar" alt=""/>
|
||||
<p className="brief" dangerouslySetInnerHTML={{ __html: notice }}></p>
|
||||
<li className='notification-item' onClick={this.onNoticeItemClick}>
|
||||
<div className="notification-item-header">
|
||||
{!noticeItem.seen &&
|
||||
<span className="notification-point" onClick={this.onMarkNotificationRead}></span>
|
||||
}
|
||||
<div className="notification-header-info">
|
||||
<div className="notification-user-detail">
|
||||
<img className="notification-user-avatar" src={avatar_url} alt="" />
|
||||
<span className="ml-2 notification-user-name">{username || gettext('System')}</span>
|
||||
</div>
|
||||
<span className="notification-time">{dayjs(noticeItem.time).fromNow()}</span>
|
||||
</div>
|
||||
<p className="time">{dayjs(noticeItem.time).fromNow()}</p>
|
||||
</div>
|
||||
<div className="notification-content-wrapper">
|
||||
<div dangerouslySetInnerHTML={{ __html: notice }}></div>
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
|
@@ -5,7 +5,7 @@
|
||||
.notification-container {
|
||||
position: absolute;
|
||||
background: #fff;
|
||||
width: 320px;
|
||||
width: 400px;
|
||||
right: -16px;
|
||||
top: -1px;
|
||||
border-radius: 3px;
|
||||
@@ -65,21 +65,15 @@
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.notification-container .notification-body .mark-notifications {
|
||||
color: #b4b4b4;
|
||||
.notification-container .mark-all-read {
|
||||
color: #666;
|
||||
cursor: pointer;
|
||||
border-bottom: 1px solid #ededed;
|
||||
height: 36px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
|
||||
.notification-container .notification-body .mark-notifications:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.notification-body .notification-list-container {
|
||||
max-height: 260px;
|
||||
overflow: auto;
|
||||
@@ -190,3 +184,33 @@
|
||||
.notification-body .notification-footer:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.notification-container .notification-body .mark-notifications {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
border-bottom: 1px solid #ededed;
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
.notification-container .notification-body .mark-notifications .mark-all-read:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.notification-container .notification-body .nav .nav-item .nav-link {
|
||||
height: 46px;
|
||||
margin-right: 15px;
|
||||
margin-left: 15px;
|
||||
font-size: 14px;
|
||||
color: #212529;
|
||||
}
|
||||
|
||||
.notification-container .notification-body .nav .nav-item .nav-link.active {
|
||||
color: #ED7109;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.notification-container {
|
||||
right: -60px;
|
||||
width: 360px;
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,8 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Popover } from 'reactstrap';
|
||||
import { gettext } from '../../../utils/constants';
|
||||
|
||||
import './index.css';
|
||||
|
||||
export default class NotificationPopover extends React.Component {
|
||||
@@ -13,7 +15,11 @@ export default class NotificationPopover extends React.Component {
|
||||
onNotificationDialogToggle: PropTypes.func,
|
||||
listNotifications: PropTypes.func,
|
||||
onMarkAllNotifications: PropTypes.func,
|
||||
tabItemClick: PropTypes.func,
|
||||
children: PropTypes.any,
|
||||
currentTab: PropTypes.string,
|
||||
generalNoticeListUnseen: PropTypes.number,
|
||||
discussionNoticeListUnseen: PropTypes.number,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
@@ -47,8 +53,12 @@ export default class NotificationPopover extends React.Component {
|
||||
}
|
||||
};
|
||||
|
||||
tabItemClick = (tab) => {
|
||||
this.props.tabItemClick(tab);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { headerText, bodyText, footerText } = this.props;
|
||||
const { headerText, bodyText, footerText, currentTab, generalNoticeListUnseen, discussionNoticeListUnseen } = this.props;
|
||||
return (
|
||||
<Popover
|
||||
className="notification-wrapper"
|
||||
@@ -64,12 +74,39 @@ export default class NotificationPopover extends React.Component {
|
||||
<span className="sf3-font sf3-font-x-01 notification-close-icon" onClick={this.props.onNotificationListToggle}></span>
|
||||
</div>
|
||||
<div className="notification-body">
|
||||
<div className="mark-notifications" onClick={this.props.onMarkAllNotifications}>{bodyText}</div>
|
||||
<div className="mark-notifications">
|
||||
<ul className="nav">
|
||||
<li className="nav-item" onClick={() => this.tabItemClick('general')}>
|
||||
<span className={`nav-link ${currentTab === 'general' ? 'active' : ''}`}>
|
||||
{gettext('General')}
|
||||
{generalNoticeListUnseen > 0 && <span>({generalNoticeListUnseen})</span>}
|
||||
</span>
|
||||
</li>
|
||||
<li className="nav-item" onClick={() => this.tabItemClick('discussion')}>
|
||||
<span className={`nav-link ${currentTab === 'discussion' ? 'active' : ''}`}>
|
||||
{gettext('Discussion')}
|
||||
{discussionNoticeListUnseen > 0 && <span>({discussionNoticeListUnseen})</span>}
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
<span className="mark-all-read" onClick={this.props.onMarkAllNotifications}>
|
||||
{bodyText}
|
||||
</span>
|
||||
</div>
|
||||
{currentTab === 'general' &&
|
||||
<div className="notification-list-container" onScroll={this.onHandleScroll} ref={ref => this.notificationListRef = ref}>
|
||||
<div ref={ref => this.notificationsWrapperRef = ref}>
|
||||
{this.props.children}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
{currentTab === 'discussion' &&
|
||||
<div className="notification-list-container" onScroll={this.onHandleScroll} ref={ref => this.notificationListRef = ref}>
|
||||
<div ref={ref => this.notificationsWrapperRef = ref}>
|
||||
{this.props.children}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div className="notification-footer" onClick={this.onNotificationDialogToggle}>{footerText}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -13,14 +13,17 @@ class Notification extends React.Component {
|
||||
this.state = {
|
||||
showNotice: false,
|
||||
unseenCount: 0,
|
||||
noticeList: [],
|
||||
generalNoticeList: [],
|
||||
discussionNoticeList: [],
|
||||
currentTab: 'general',
|
||||
isShowNotificationDialog: this.getInitDialogState(),
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
seafileAPI.getUnseenNotificationCount().then(res => {
|
||||
this.setState({ unseenCount: res.data.unseen_count });
|
||||
seafileAPI.listAllNotifications().then(res => {
|
||||
let unseen_count = res.data.general.unseen_count + res.data.discussion.unseen_count;
|
||||
this.setState({ unseenCount: unseen_count });
|
||||
});
|
||||
}
|
||||
|
||||
@@ -38,28 +41,57 @@ class Notification extends React.Component {
|
||||
}
|
||||
};
|
||||
|
||||
tabItemClick = (tab) => {
|
||||
const { currentTab } = this.state;
|
||||
if (currentTab === tab) return;
|
||||
this.setState({
|
||||
showNotice: true,
|
||||
currentTab: tab
|
||||
});
|
||||
};
|
||||
|
||||
loadNotices = () => {
|
||||
let page = 1;
|
||||
let perPage = 5;
|
||||
seafileAPI.listNotifications(page, perPage).then(res => {
|
||||
let noticeList = res.data.notification_list;
|
||||
this.setState({ noticeList: noticeList });
|
||||
seafileAPI.listAllNotifications(page, perPage).then(res => {
|
||||
let generalNoticeList = res.data.general.notification_list;
|
||||
let discussionNoticeList = res.data.discussion.notification_list;
|
||||
this.setState({
|
||||
generalNoticeList: generalNoticeList,
|
||||
discussionNoticeList: discussionNoticeList
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
onNoticeItemClick = (noticeItem) => {
|
||||
let noticeList = this.state.noticeList.map(item => {
|
||||
if (item.id === noticeItem.id) {
|
||||
item.seen = true;
|
||||
}
|
||||
return item;
|
||||
});
|
||||
seafileAPI.markNoticeAsRead(noticeItem.id);
|
||||
let unseenCount = this.state.unseenCount === 0 ? 0 : this.state.unseenCount - 1;
|
||||
this.setState({
|
||||
noticeList: noticeList,
|
||||
unseenCount: unseenCount,
|
||||
});
|
||||
if (this.state.currentTab === 'general') {
|
||||
let noticeList = this.state.generalNoticeList.map(item => {
|
||||
if (item.id === noticeItem.id) {
|
||||
item.seen = true;
|
||||
}
|
||||
return item;
|
||||
});
|
||||
let unseenCount = this.state.unseenCount === 0 ? 0 : this.state.unseenCount - 1;
|
||||
this.setState({
|
||||
generalNoticeList: noticeList,
|
||||
unseenCount: unseenCount,
|
||||
});
|
||||
seafileAPI.markNoticeAsRead(noticeItem.id);
|
||||
}
|
||||
if (this.state.currentTab === 'discussion') {
|
||||
let noticeList = this.state.discussionNoticeList.map(item => {
|
||||
if (item.id === noticeItem.id) {
|
||||
item.seen = true;
|
||||
}
|
||||
return item;
|
||||
});
|
||||
let unseenCount = this.state.unseenCount === 0 ? 0 : this.state.unseenCount - 1;
|
||||
this.setState({
|
||||
discussionNoticeList: noticeList,
|
||||
unseenCount: unseenCount,
|
||||
});
|
||||
seafileAPI.markSdocNoticeAsRead(noticeItem.id);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@@ -79,7 +111,7 @@ class Notification extends React.Component {
|
||||
};
|
||||
|
||||
onMarkAllNotifications = () => {
|
||||
seafileAPI.updateNotifications().then(() => {
|
||||
seafileAPI.updateAllNotifications().then(() => {
|
||||
this.setState({
|
||||
unseenCount: 0,
|
||||
});
|
||||
@@ -91,7 +123,9 @@ class Notification extends React.Component {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { unseenCount } = this.state;
|
||||
const { unseenCount, currentTab, generalNoticeList, discussionNoticeList } = this.state;
|
||||
const generalNoticeListUnseen = generalNoticeList.filter(item => !item.seen).length;
|
||||
const discussionNoticeListUnseen = discussionNoticeList.filter(item => !item.seen).length;
|
||||
return (
|
||||
<div id="notifications">
|
||||
<a href="#" onClick={this.onClick} className="no-deco" id="notice-icon" title={gettext('Notifications')} aria-label={gettext('Notifications')}>
|
||||
@@ -103,19 +137,40 @@ class Notification extends React.Component {
|
||||
headerText={gettext('Notification')}
|
||||
bodyText={gettext('Mark all as read')}
|
||||
footerText={gettext('View all notifications')}
|
||||
currentTab={currentTab}
|
||||
onNotificationListToggle={this.onNotificationListToggle}
|
||||
onNotificationDialogToggle={this.onNotificationDialogToggle}
|
||||
onMarkAllNotifications={this.onMarkAllNotifications}
|
||||
tabItemClick={this.tabItemClick}
|
||||
generalNoticeListUnseen={generalNoticeListUnseen}
|
||||
discussionNoticeListUnseen={discussionNoticeListUnseen}
|
||||
>
|
||||
<ul className="notice-list list-unstyled" id="notice-popover">
|
||||
{this.state.noticeList.map(item => {
|
||||
return (<NoticeItem key={item.id} noticeItem={item} onNoticeItemClick={this.onNoticeItemClick}/>);
|
||||
})}
|
||||
</ul>
|
||||
{currentTab === 'general' &&
|
||||
<ul className="notice-list list-unstyled" id="notice-popover">
|
||||
{generalNoticeList.map(item => {
|
||||
return (
|
||||
<NoticeItem key={item.id} noticeItem={item} onNoticeItemClick={this.onNoticeItemClick}/>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
}
|
||||
{currentTab === 'discussion' &&
|
||||
<ul className="notice-list list-unstyled" id="notice-popover">
|
||||
{discussionNoticeList.map(item => {
|
||||
return (
|
||||
<NoticeItem key={item.id} noticeItem={item} onNoticeItemClick={this.onNoticeItemClick}/>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
}
|
||||
</NotificationPopover>
|
||||
}
|
||||
{this.state.isShowNotificationDialog &&
|
||||
<UserNotificationsDialog onNotificationDialogToggle={this.onNotificationDialogToggle} />
|
||||
<UserNotificationsDialog
|
||||
onNotificationDialogToggle={this.onNotificationDialogToggle}
|
||||
generalNoticeListUnseen={generalNoticeListUnseen}
|
||||
discussionNoticeListUnseen={discussionNoticeListUnseen}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
|
Reference in New Issue
Block a user