1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-08-24 17:49:44 +00:00
seahub/frontend/src/user-notifications.js
awu0403 3899577631
optimize notification (#7521)
* update

* update

---------

Co-authored-by: 孙永强 <11704063+s-yongqiang@user.noreply.gitee.com>
2025-02-27 17:00:33 +08:00

334 lines
11 KiB
JavaScript

import React from 'react';
import PropTypes from 'prop-types';
import classname from 'classnames';
import { Modal, ModalHeader, ModalBody, Dropdown, DropdownToggle, DropdownMenu, DropdownItem, TabPane, Nav, NavItem, NavLink, TabContent } from 'reactstrap';
import { Utils } from './utils/utils';
import { gettext } from './utils/constants';
import { seafileAPI } from './utils/seafile-api';
import Loading from './components/loading';
import NoticeItem from './components/common/notice-item';
import './css/toolbar.css';
import './css/search.css';
import './css/user-notifications.css';
const PER_PAGE = 20;
class UserNotificationsDialog extends React.Component {
constructor(props) {
super(props);
this.state = {
isLoading: true,
errorMsg: '',
currentPage: 1,
hasNextPage: false,
items: [],
isItemMenuShow: false,
activeTab: 'general',
};
}
componentDidMount() {
let urlParams = (new URL(window.location)).searchParams;
const { currentPage } = this.state;
this.setState({
currentPage: parseInt(urlParams.get('page') || currentPage)
}, () => {
this.getItems(this.state.currentPage);
});
}
getItems = (page, is_scroll = false) => {
this.setState({ isLoading: true });
if (this.state.activeTab === 'general') {
seafileAPI.listNotifications(page, PER_PAGE).then((res) => {
if (is_scroll) {
this.setState({
isLoading: false,
items: [...this.state.items, ...res.data.notification_list],
currentPage: page,
hasNextPage: Utils.hasNextPage(page, PER_PAGE, res.data.count)
});
} else {
this.setState({
isLoading: false,
items: [...res.data.notification_list],
currentPage: page,
hasNextPage: Utils.hasNextPage(page, PER_PAGE, res.data.count)
});
}
}).catch((error) => {
this.setState({
isLoading: false,
errorMsg: Utils.getErrorMsg(error, true) // true: show login tip if 403
});
});
} else if (this.state.activeTab === 'discussion') {
seafileAPI.listSdocNotifications(page, PER_PAGE).then((res) => {
if (is_scroll) {
this.setState({
isLoading: false,
items: [...this.state.items, ...res.data.notification_list],
currentPage: page,
hasNextPage: Utils.hasNextPage(page, PER_PAGE, res.data.count)
});
} else {
this.setState({
isLoading: false,
items: [...res.data.notification_list],
currentPage: page,
hasNextPage: Utils.hasNextPage(page, PER_PAGE, res.data.count)
});
}
}).catch((error) => {
this.setState({
isLoading: false,
errorMsg: Utils.getErrorMsg(error, true) // true: show login tip if 403
});
});
}
};
markAllRead = () => {
if (this.state.activeTab === 'general') {
seafileAPI.updateNotifications().then((res) => {
this.setState({
items: this.state.items.map(item => {
item.seen = true;
return item;
})
});
this.props.updateTotalUnseenCount('general');
}).catch((error) => {
this.setState({
isLoading: false,
errorMsg: Utils.getErrorMsg(error, true) // true: show login tip if 403
});
});
} else if (this.state.activeTab === 'discussion') {
seafileAPI.updateSdocNotifications().then((res) => {
this.setState({
items: this.state.items.map(item => {
item.seen = true;
return item;
})
});
this.props.updateTotalUnseenCount('discussion');
}).catch((error) => {
this.setState({
isLoading: false,
errorMsg: Utils.getErrorMsg(error, true) // true: show login tip if 403
});
});
}
};
clearAll = () => {
if (this.state.activeTab === 'general') {
seafileAPI.deleteNotifications().then((res) => {
this.setState({
items: []
});
this.props.updateTotalUnseenCount('general');
}).catch((error) => {
this.setState({
isLoading: false,
errorMsg: Utils.getErrorMsg(error, true) // true: show login tip if 403
});
});
} else if (this.state.activeTab === 'discussion') {
seafileAPI.deleteSdocNotifications().then((res) => {
this.setState({
items: []
});
this.props.updateTotalUnseenCount('discussion');
}).catch((error) => {
this.setState({
isLoading: false,
errorMsg: Utils.getErrorMsg(error, true) // true: show login tip if 403
});
});
}
};
toggle = () => {
this.props.onNotificationDialogToggle();
};
tabItemClick = (tab) => {
if (tab === this.state.activeTab) return;
this.setState({
activeTab: tab,
currentPage: 1
}, () => {
this.getItems(this.state.currentPage);
});
};
toggleDropDownMenu = () => {
this.setState({ isItemMenuShow: !this.state.isItemMenuShow });
};
onHandleScroll = () => {
if (!this.state.hasNextPage || this.state.isLoading || !this.tableRef) {
return;
}
if (this.notificationTableRef.offsetHeight + this.notificationTableRef.scrollTop + 1 >= this.tableRef.offsetHeight) {
this.getItems(this.state.currentPage + 1, true);
}
};
renderHeaderRowBtn = () => {
return (
<div className="notification-header-close">
<Dropdown isOpen={this.state.isItemMenuShow} toggle={this.toggleDropDownMenu}>
<DropdownToggle
tag="span"
data-toggle="dropdown"
aria-expanded={this.state.isItemMenuShow}
className="notification-dropdown-toggle"
>
<button type="button" className="close seahub-modal-btn" aria-label={gettext('More')}>
<span className="seahub-modal-btn-inner">
<i className="sf3-font sf3-font-more" aria-hidden="true"></i>
</span>
</button>
</DropdownToggle>
<DropdownMenu className="dtable-dropdown-menu large">
<DropdownItem onClick={this.markAllRead}>{gettext('Mark all read')}</DropdownItem>
<DropdownItem onClick={this.clearAll}>{gettext('Clear')}</DropdownItem>
</DropdownMenu>
</Dropdown>
<button type="button" className="close seahub-modal-btn" aria-label={gettext('Close')} onClick={this.toggle}>
<span className="seahub-modal-btn-inner">
<i className="sf3-font sf3-font-x-01" aria-hidden="true"></i>
</span>
</button>
</div>
);
};
renderNoticeContent = (content) => {
const { generalNoticeListUnseen, discussionNoticeListUnseen } = this.props;
let activeTab = this.state.activeTab;
return (
<>
<div className="notice-dialog-side">
<Nav pills className="flex-column w-100">
<NavItem className="w-100" role="tab" aria-selected={activeTab === 'general'} aria-controls="general-notice-panel">
<NavLink
className={classname('w-100 mr-0', { 'active': activeTab === 'general' })}
onClick={() => this.tabItemClick('general')}
tabIndex="0"
value="general"
>
{gettext('General')}
{generalNoticeListUnseen > 0 && <span className="pl-1">({generalNoticeListUnseen})</span>}
</NavLink>
</NavItem>
<NavItem className="w-100" role="tab" aria-selected={activeTab === 'discussion'} aria-controls="discussion-notice-panel">
<NavLink
className={classname('w-100 mr-0', { 'active': activeTab === 'discussion' })}
onClick={() => this.tabItemClick('discussion')}
tabIndex="1"
value="discussion"
>
{gettext('Discussion')}
{discussionNoticeListUnseen > 0 && <span className="pl-1">({discussionNoticeListUnseen})</span>}
</NavLink>
</NavItem>
</Nav>
</div>
<div className="notice-dialog-main">
<TabContent activeTab={this.state.activeTab}>
{activeTab === 'general' &&
<TabPane tabId="general" role="tabpanel" id="general-notice-panel" className="h-100">
<div className="notification-dialog-body" ref={ref => this.notificationTableRef = ref} onScroll={this.onHandleScroll}>
{content}
</div>
</TabPane>
}
{activeTab === 'discussion' &&
<TabPane tabId="discussion" role="tabpanel" id="discussion-notice-panel" className="h-100">
<div className="notification-dialog-body" ref={ref => this.notificationTableRef = ref} onScroll={this.onHandleScroll}>
{content}
</div>
</TabPane>
}
</TabContent>
</div>
</>
);
};
render() {
const { isLoading, errorMsg, items } = this.state;
let content;
if (errorMsg) {
content = <p className="error mt-6 text-center">{errorMsg}</p>;
}
else {
const isDesktop = Utils.isDesktop();
const theadData = isDesktop ? [
{ width: '2%', text: '' },
{ width: '15%', text: gettext('User') },
{ width: '63%', text: gettext('Message') },
{ width: '20%', text: gettext('Update time') }
] : [
{ width: '2%', text: '' },
{ width: '13%', text: gettext('User') },
{ width: '52%', text: gettext('Message') },
{ width: '33%', text: gettext('Update time') }
];
content = (
<table className="table-hover" ref={ref => this.tableRef = ref}>
<thead>
<tr>
{theadData.map((item, index) => {
return <th key={index} width={item.width}>{item.text}</th>;
})}
</tr>
</thead>
<tbody>
{items.map((item, index) => {
return (
<NoticeItem key={index} noticeItem={item} tr={true} />
);
})}
</tbody>
</table>
);
if (isLoading) {
content = <>{content}<Loading /></>;
}
}
return (
<Modal
isOpen={true}
size={'lg'}
toggle={this.toggle}
className="notification-list-dialog"
contentClassName="notification-list-content"
zIndex={1046}
>
<ModalHeader close={this.renderHeaderRowBtn()} toggle={this.toggle}>{gettext('Notifications')}</ModalHeader>
<ModalBody className="notification-modal-body">
{this.renderNoticeContent(content)}
</ModalBody>
</Modal>
);
}
}
UserNotificationsDialog.propTypes = {
onNotificationDialogToggle: PropTypes.func.isRequired
};
export default UserNotificationsDialog;