2024-11-22 03:18:42 +00:00
|
|
|
import React, { Fragment } from 'react';
|
2023-09-08 05:54:05 +00:00
|
|
|
import PropTypes from 'prop-types';
|
2024-11-22 03:18:42 +00:00
|
|
|
import {
|
|
|
|
Modal,
|
|
|
|
ModalHeader,
|
|
|
|
ModalBody,
|
|
|
|
Dropdown,
|
|
|
|
DropdownToggle,
|
|
|
|
DropdownMenu,
|
|
|
|
DropdownItem,
|
|
|
|
TabPane,
|
|
|
|
Nav, NavItem, NavLink, TabContent
|
|
|
|
} from 'reactstrap';
|
2020-03-24 07:24:47 +00:00
|
|
|
import { Utils } from './utils/utils';
|
2023-09-08 05:54:05 +00:00
|
|
|
import { gettext } from './utils/constants';
|
2020-03-24 07:24:47 +00:00
|
|
|
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';
|
|
|
|
|
2023-09-08 05:54:05 +00:00
|
|
|
const PER_PAGE = 20;
|
|
|
|
|
|
|
|
class UserNotificationsDialog extends React.Component {
|
2020-03-24 07:24:47 +00:00
|
|
|
|
|
|
|
constructor(props) {
|
|
|
|
super(props);
|
|
|
|
this.state = {
|
|
|
|
isLoading: true,
|
|
|
|
errorMsg: '',
|
|
|
|
currentPage: 1,
|
|
|
|
hasNextPage: false,
|
2023-09-08 05:54:05 +00:00
|
|
|
items: [],
|
|
|
|
isItemMenuShow: false,
|
2024-11-22 03:18:42 +00:00
|
|
|
activeTab: 'general',
|
2020-03-24 07:24:47 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
componentDidMount() {
|
|
|
|
let urlParams = (new URL(window.location)).searchParams;
|
2023-09-08 05:54:05 +00:00
|
|
|
const { currentPage } = this.state;
|
2020-03-24 07:24:47 +00:00
|
|
|
this.setState({
|
|
|
|
currentPage: parseInt(urlParams.get('page') || currentPage)
|
|
|
|
}, () => {
|
|
|
|
this.getItems(this.state.currentPage);
|
2020-11-02 05:56:35 +00:00
|
|
|
});
|
2020-03-24 07:24:47 +00:00
|
|
|
}
|
|
|
|
|
2024-11-22 03:18:42 +00:00
|
|
|
getItems = (page, is_scroll = false) => {
|
2023-09-13 00:40:50 +00:00
|
|
|
this.setState({ isLoading: true });
|
2024-11-22 03:18:42 +00:00
|
|
|
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
|
|
|
|
});
|
2020-03-24 07:24:47 +00:00
|
|
|
});
|
2024-11-22 03:18:42 +00:00
|
|
|
} 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
|
|
|
|
});
|
2020-11-02 05:56:35 +00:00
|
|
|
});
|
2024-11-22 03:18:42 +00:00
|
|
|
}
|
|
|
|
|
2023-09-08 05:54:05 +00:00
|
|
|
};
|
2020-03-24 07:24:47 +00:00
|
|
|
|
|
|
|
markAllRead = () => {
|
2024-11-22 03:18:42 +00:00
|
|
|
if (this.state.activeTab === 'general') {
|
|
|
|
seafileAPI.updateNotifications().then((res) => {
|
|
|
|
this.setState({
|
|
|
|
items: this.state.items.map(item => {
|
|
|
|
item.seen = true;
|
|
|
|
return item;
|
|
|
|
})
|
|
|
|
});
|
|
|
|
}).catch((error) => {
|
|
|
|
this.setState({
|
|
|
|
isLoading: false,
|
|
|
|
errorMsg: Utils.getErrorMsg(error, true) // true: show login tip if 403
|
|
|
|
});
|
2020-03-24 07:24:47 +00:00
|
|
|
});
|
2024-11-22 03:18:42 +00:00
|
|
|
} else if (this.state.activeTab === 'discussion') {
|
|
|
|
seafileAPI.updateSdocNotifications().then((res) => {
|
|
|
|
this.setState({
|
|
|
|
items: this.state.items.map(item => {
|
|
|
|
item.seen = true;
|
|
|
|
return item;
|
|
|
|
})
|
|
|
|
});
|
|
|
|
}).catch((error) => {
|
|
|
|
this.setState({
|
|
|
|
isLoading: false,
|
|
|
|
errorMsg: Utils.getErrorMsg(error, true) // true: show login tip if 403
|
|
|
|
});
|
2020-03-24 07:24:47 +00:00
|
|
|
});
|
2024-11-22 03:18:42 +00:00
|
|
|
}
|
|
|
|
|
2023-09-08 05:54:05 +00:00
|
|
|
};
|
2020-03-24 07:24:47 +00:00
|
|
|
|
|
|
|
clearAll = () => {
|
2024-11-22 03:18:42 +00:00
|
|
|
if (this.state.activeTab === 'general') {
|
|
|
|
seafileAPI.deleteNotifications().then((res) => {
|
|
|
|
this.setState({
|
|
|
|
items: []
|
|
|
|
});
|
|
|
|
}).catch((error) => {
|
|
|
|
this.setState({
|
|
|
|
isLoading: false,
|
|
|
|
errorMsg: Utils.getErrorMsg(error, true) // true: show login tip if 403
|
|
|
|
});
|
2020-03-24 07:24:47 +00:00
|
|
|
});
|
2024-11-22 03:18:42 +00:00
|
|
|
} else if (this.state.activeTab === 'discussion') {
|
|
|
|
seafileAPI.deleteSdocNotifications().then((res) => {
|
|
|
|
this.setState({
|
|
|
|
items: []
|
|
|
|
});
|
|
|
|
}).catch((error) => {
|
|
|
|
this.setState({
|
|
|
|
isLoading: false,
|
|
|
|
errorMsg: Utils.getErrorMsg(error, true) // true: show login tip if 403
|
|
|
|
});
|
2020-03-24 07:24:47 +00:00
|
|
|
});
|
2024-11-22 03:18:42 +00:00
|
|
|
}
|
2023-09-08 05:54:05 +00:00
|
|
|
};
|
2020-03-24 07:24:47 +00:00
|
|
|
|
2023-09-08 05:54:05 +00:00
|
|
|
toggle = () => {
|
|
|
|
this.props.onNotificationDialogToggle();
|
|
|
|
};
|
2020-03-24 07:24:47 +00:00
|
|
|
|
2024-11-22 03:18:42 +00:00
|
|
|
tabItemClick = (e) => {
|
|
|
|
let tab = e.target.getAttribute('value');
|
|
|
|
this.setState({
|
|
|
|
activeTab: tab,
|
|
|
|
currentPage: 1
|
|
|
|
}, () => {
|
|
|
|
this.getItems(this.state.currentPage);
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2023-09-08 05:54:05 +00:00
|
|
|
toggleDropDownMenu = () => {
|
2024-07-18 03:58:42 +00:00
|
|
|
this.setState({ isItemMenuShow: !this.state.isItemMenuShow });
|
2023-09-08 05:54:05 +00:00
|
|
|
};
|
2020-03-24 07:24:47 +00:00
|
|
|
|
2023-09-08 05:54:05 +00:00
|
|
|
onHandleScroll = () => {
|
2024-07-18 03:58:42 +00:00
|
|
|
if (!this.state.hasNextPage || this.state.isLoading || !this.tableRef) {
|
2023-09-08 05:54:05 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (this.notificationTableRef.offsetHeight + this.notificationTableRef.scrollTop + 1 >= this.tableRef.offsetHeight) {
|
2024-11-22 03:18:42 +00:00
|
|
|
this.getItems(this.state.currentPage + 1, true);
|
2023-09-08 05:54:05 +00:00
|
|
|
}
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2020-03-24 07:24:47 +00:00
|
|
|
|
2023-09-08 05:54:05 +00:00
|
|
|
renderHeaderRowBtn = () => {
|
|
|
|
return (
|
|
|
|
<div className="notification-header-close">
|
|
|
|
<Dropdown isOpen={this.state.isItemMenuShow} toggle={this.toggleDropDownMenu}>
|
2024-08-14 04:20:57 +00:00
|
|
|
<DropdownToggle
|
|
|
|
tag="span"
|
|
|
|
data-toggle="dropdown"
|
|
|
|
aria-expanded={this.state.isItemMenuShow}
|
|
|
|
className="notification-dropdown-toggle"
|
|
|
|
>
|
2024-12-27 09:44:13 +00:00
|
|
|
<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>
|
2023-09-08 05:54:05 +00:00
|
|
|
</DropdownToggle>
|
2025-02-14 06:04:25 +00:00
|
|
|
<DropdownMenu className="dtable-dropdown-menu large">
|
2023-09-08 05:54:05 +00:00
|
|
|
<DropdownItem onClick={this.markAllRead}>{gettext('Mark all read')}</DropdownItem>
|
|
|
|
<DropdownItem onClick={this.clearAll}>{gettext('Clear')}</DropdownItem>
|
|
|
|
</DropdownMenu>
|
|
|
|
</Dropdown>
|
2024-12-27 09:44:13 +00:00
|
|
|
<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>
|
2023-09-08 05:54:05 +00:00
|
|
|
</div>
|
|
|
|
);
|
2023-09-13 00:40:50 +00:00
|
|
|
};
|
2020-03-24 07:24:47 +00:00
|
|
|
|
2024-11-22 03:18:42 +00:00
|
|
|
|
|
|
|
renderNoticeContent = (content) => {
|
|
|
|
let activeTab = this.state.activeTab;
|
|
|
|
return (
|
|
|
|
<Fragment>
|
|
|
|
<div className="notice-dialog-side">
|
|
|
|
<Nav pills>
|
|
|
|
<NavItem role="tab" aria-selected={activeTab === 'general'} aria-controls="general-notice-panel">
|
|
|
|
<NavLink className={activeTab === 'general' ? 'active' : ''} onClick={this.tabItemClick} tabIndex="0" value="general">
|
|
|
|
{gettext('General')}
|
|
|
|
</NavLink>
|
|
|
|
</NavItem>
|
|
|
|
<NavItem role="tab" aria-selected={activeTab === 'discussion'} aria-controls="discussion-notice-panel">
|
|
|
|
<NavLink className={activeTab === 'discussion' ? 'active' : ''} onClick={this.tabItemClick} tabIndex="1" value="discussion">
|
|
|
|
{gettext('Discussion')}
|
|
|
|
</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">
|
|
|
|
<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">
|
|
|
|
<div className="notification-dialog-body" ref={ref => this.notificationTableRef = ref}
|
|
|
|
onScroll={this.onHandleScroll}>
|
|
|
|
{content}
|
|
|
|
</div>
|
|
|
|
</TabPane>
|
|
|
|
}
|
|
|
|
</TabContent>
|
|
|
|
</div>
|
|
|
|
</Fragment>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
2020-03-24 07:24:47 +00:00
|
|
|
render() {
|
2023-09-08 05:54:05 +00:00
|
|
|
const { isLoading, errorMsg, items } = this.state;
|
|
|
|
let content;
|
2020-03-24 07:24:47 +00:00
|
|
|
if (errorMsg) {
|
2023-09-08 05:54:05 +00:00
|
|
|
content = <p className="error mt-6 text-center">{errorMsg}</p>;
|
2020-03-24 07:24:47 +00:00
|
|
|
}
|
2023-09-08 05:54:05 +00:00
|
|
|
else {
|
|
|
|
const isDesktop = Utils.isDesktop();
|
|
|
|
const theadData = isDesktop ? [
|
2024-07-18 03:58:42 +00:00
|
|
|
{ width: '7%', text: '' },
|
|
|
|
{ width: '73%', text: gettext('Message') },
|
|
|
|
{ width: '20%', text: gettext('Time') }
|
2023-09-08 05:54:05 +00:00
|
|
|
] : [
|
2024-07-18 03:58:42 +00:00
|
|
|
{ width: '15%', text: '' },
|
|
|
|
{ width: '52%', text: gettext('Message') },
|
|
|
|
{ width: '33%', text: gettext('Time') }
|
2023-09-08 05:54:05 +00:00
|
|
|
];
|
|
|
|
content = (
|
|
|
|
<table className="table-hover" ref={ref => this.tableRef = ref}>
|
2020-03-24 07:24:47 +00:00
|
|
|
<thead>
|
|
|
|
<tr>
|
2020-03-30 13:27:45 +00:00
|
|
|
{theadData.map((item, index) => {
|
2020-03-24 07:24:47 +00:00
|
|
|
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>
|
2023-09-08 05:54:05 +00:00
|
|
|
);
|
|
|
|
if (isLoading) {
|
|
|
|
content = <>{content}<Loading /></>;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
|
|
|
<Modal isOpen={true} 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">
|
2024-11-22 03:18:42 +00:00
|
|
|
{this.renderNoticeContent(content)}
|
2023-09-08 05:54:05 +00:00
|
|
|
</ModalBody>
|
|
|
|
</Modal>
|
2020-03-24 07:24:47 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-08 05:54:05 +00:00
|
|
|
UserNotificationsDialog.propTypes = {
|
|
|
|
onNotificationDialogToggle: PropTypes.func.isRequired,
|
2024-11-22 03:18:42 +00:00
|
|
|
tabItemClick: PropTypes.func.isRequired,
|
2023-09-08 05:54:05 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
export default UserNotificationsDialog;
|