mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-20 02:48:51 +00:00
change notification popover (#5629)
* change notification popover delete useless test * Update iconfont and user notification ui * change notification dialog detail * format css * update url of all notifications when send notice email --------- Co-authored-by: lian <imwhatiam123@gmail.com>
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import moment from 'moment';
|
||||
|
||||
import { gettext, siteRoot } from '../../utils/constants';
|
||||
import { Utils } from '../../utils/utils';
|
||||
|
||||
|
186
frontend/src/components/common/notification-popover/index.css
Normal file
186
frontend/src/components/common/notification-popover/index.css
Normal file
@@ -0,0 +1,186 @@
|
||||
.notification-wrapper .popover {
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.notification-container {
|
||||
position: absolute;
|
||||
background: #fff;
|
||||
width: 320px;
|
||||
right: -10px;
|
||||
top: -1px;
|
||||
border-radius: 3px;
|
||||
box-shadow: 0 0 5px #ccc;
|
||||
}
|
||||
|
||||
.notification-container .notification-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 50px;
|
||||
border-bottom: 1px solid #ededed;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.notification-container .notification-header .notification-close-icon {
|
||||
position: absolute;
|
||||
right: 14px;
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
color: #000;
|
||||
opacity: 0.5;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.notification-container .notification-header .notification-close-icon:hover {
|
||||
opacity: 0.75;
|
||||
}
|
||||
|
||||
.notification-container .notification-body {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.notification-container .notification-body .show-weixin-qrcode {
|
||||
cursor: pointer;
|
||||
border-bottom: 1px solid #ededed;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.show-weixin-qrcode .weixin-icon {
|
||||
color: #999;
|
||||
font-size: 20px;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.notification-container .notification-body .mark-notifications {
|
||||
color: #b4b4b4;
|
||||
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;
|
||||
}
|
||||
|
||||
.notification-list-container .notification-item {
|
||||
padding: 14px 16px 14px 10px;
|
||||
border-bottom: 1px solid #ededed;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.notification-list-container .notification-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.notification-list-container .notification-item:hover {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.notification-list-container .notification-item .notification-item-header {
|
||||
display: flex;
|
||||
align-items: center
|
||||
}
|
||||
|
||||
.notification-list-container .notification-item .notification-point {
|
||||
display: inline-block;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background: red;
|
||||
margin-right: 12px;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.notification-list-container .notification-item .notification-header-info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex: 1;
|
||||
margin-left: 20px;
|
||||
width: calc(100% - 20px);
|
||||
}
|
||||
|
||||
.notification-user-detail {
|
||||
display: flex;
|
||||
width: 65%;
|
||||
}
|
||||
|
||||
.notification-user-detail img {
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
.notification-user-name {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.notification-item .notification-header-info .notification-time {
|
||||
color: #b4b4b4;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.notification-list-container .notification-item .notification-content-wrapper {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.notification-item .notification-content-quotes {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
.notification-list-container .notification-item .notification-comment-content {
|
||||
max-width: calc(100% - 16px);
|
||||
}
|
||||
|
||||
.notification-list-container .notification-item .notification-comment-content p {
|
||||
display: inline-block;
|
||||
letter-spacing: 1px;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.notification-list-container .notification-item .notification-comment-content p img {
|
||||
max-width: 70%;
|
||||
height: auto;
|
||||
max-height: 60px;
|
||||
}
|
||||
|
||||
.notification-body .notification-footer {
|
||||
height: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #f9f9f9;
|
||||
cursor: pointer;
|
||||
border-bottom-right-radius: 3px;
|
||||
border-bottom-left-radius: 3px;
|
||||
border-top: 1px solid #ededed;
|
||||
}
|
||||
|
||||
.notification-body .notification-footer:hover {
|
||||
text-decoration: underline;
|
||||
}
|
83
frontend/src/components/common/notification-popover/index.js
Normal file
83
frontend/src/components/common/notification-popover/index.js
Normal file
@@ -0,0 +1,83 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Popover } from 'reactstrap';
|
||||
import './index.css';
|
||||
|
||||
export default class NotificationPopover extends React.Component {
|
||||
|
||||
static propTypes = {
|
||||
headerText: PropTypes.string.isRequired,
|
||||
bodyText: PropTypes.string.isRequired,
|
||||
footerText: PropTypes.string.isRequired,
|
||||
onNotificationListToggle: PropTypes.func,
|
||||
onNotificationDialogToggle: PropTypes.func,
|
||||
listNotifications: PropTypes.func,
|
||||
onMarkAllNotifications: PropTypes.func,
|
||||
children: PropTypes.any,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
headerText: '',
|
||||
bodyText: '',
|
||||
footerText: '',
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
document.addEventListener('mousedown', this.handleOutsideClick);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
document.removeEventListener('mousedown', this.handleOutsideClick);
|
||||
}
|
||||
|
||||
handleOutsideClick = (e) => {
|
||||
if (!this.notificationContainerRef.contains(e.target)) {
|
||||
document.removeEventListener('mousedown', this.handleOutsideClick);
|
||||
if (e.target.className === 'tool notification' || e.target.parentNode.className === 'tool notification') {
|
||||
return;
|
||||
}
|
||||
this.props.onNotificationListToggle();
|
||||
}
|
||||
}
|
||||
|
||||
onNotificationDialogToggle = () => {
|
||||
this.props.onNotificationDialogToggle();
|
||||
this.props.onNotificationListToggle();
|
||||
}
|
||||
|
||||
onHandleScroll = () => {
|
||||
if (this.notificationListRef.offsetHeight + this.notificationListRef.scrollTop + 1 >= this.notificationsWrapperRef.offsetHeight) {
|
||||
this.props.listNotifications && this.props.listNotifications();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { headerText, bodyText, footerText } = this.props;
|
||||
return (
|
||||
<Popover
|
||||
className="notification-wrapper"
|
||||
target="notification-popover"
|
||||
isOpen={true}
|
||||
fade={false}
|
||||
hideArrow={true}
|
||||
placement="bottom"
|
||||
>
|
||||
<div className="notification-container" ref={ref => this.notificationContainerRef = ref}>
|
||||
<div className="notification-header">
|
||||
{headerText}
|
||||
<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="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>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
}
|
126
frontend/src/components/common/notification.css
Normal file
126
frontend/src/components/common/notification.css
Normal file
@@ -0,0 +1,126 @@
|
||||
#notifications {
|
||||
position: relative;
|
||||
width: 32px;
|
||||
}
|
||||
|
||||
#notice-icon {
|
||||
position: relative;
|
||||
display: block;
|
||||
}
|
||||
|
||||
@media (max-width: 390px) {
|
||||
#notifications {
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
#notifications .title {
|
||||
line-height: 1.5;
|
||||
font-size: 1rem;
|
||||
color: #322;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
#notifications .sf2-icon-bell {
|
||||
font-size: 24px;
|
||||
line-height: 1;
|
||||
color: #999;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#notifications .num {
|
||||
position: absolute;
|
||||
top: -3px;
|
||||
left: 12px;
|
||||
padding: 0 2px;
|
||||
min-width: 16px;
|
||||
height: 16px;
|
||||
color: #fff;
|
||||
font-size: 9px;
|
||||
line-height: 16px;
|
||||
text-align: center;
|
||||
background: #fc6440;
|
||||
border-radius: 100%;
|
||||
}
|
||||
|
||||
#notice-popover {
|
||||
top: 38px;
|
||||
right: -12px;
|
||||
}
|
||||
|
||||
#notice-popover .outer-caret {
|
||||
right: 18px;
|
||||
}
|
||||
|
||||
#notice-popover a {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
#notice-popover li {
|
||||
padding: 9px 0 3px;
|
||||
border-bottom: 1px solid #dfdfe1;
|
||||
}
|
||||
|
||||
#notice-popover li.unread {
|
||||
padding-right: 10px;
|
||||
padding-left: 10px;
|
||||
border-left: 2px solid #feac74;
|
||||
}
|
||||
|
||||
#notice-popover li.read {
|
||||
padding-right: 10px;
|
||||
padding-left: 10px;
|
||||
border-left: 2px solid transparent;
|
||||
}
|
||||
|
||||
#notice-popover li:hover {
|
||||
background: #f5f5f7;
|
||||
}
|
||||
|
||||
#notice-popover li.read:hover {
|
||||
background: #f5f5f7;
|
||||
border-left: 2px solid #dfdfe1;
|
||||
}
|
||||
|
||||
#notice-popover .avatar {
|
||||
border-radius: 1000px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
#notice-popover .brief {
|
||||
margin-left: 40px;
|
||||
margin-bottom: 1rem;
|
||||
font-size: 0.8125rem;
|
||||
line-height: 1.5rem;
|
||||
}
|
||||
|
||||
#notice-popover .time {
|
||||
margin: 0;
|
||||
color: #999;
|
||||
text-align: right;
|
||||
font-size: 0.8125rem;
|
||||
line-height: 1.5rem;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
#notice-popover .view-all {
|
||||
display: block;
|
||||
padding: 7px 0;
|
||||
text-align: center;
|
||||
color: #a4a4a4;
|
||||
}
|
||||
|
||||
#notice-popover .sf-popover-close {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 17px;
|
||||
}
|
||||
|
||||
#notice-popover .sf-popover-hd {
|
||||
border-bottom: 1px solid #dfdfe1;
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
#notice-popover .sf-popover-con {
|
||||
max-height: 25rem;
|
||||
}
|
@@ -1,7 +1,11 @@
|
||||
import React from 'react';
|
||||
import NotificationPopover from './notification-popover';
|
||||
import { seafileAPI } from '../../utils/seafile-api';
|
||||
import { gettext, siteRoot } from '../../utils/constants';
|
||||
import { gettext } from '../../utils/constants';
|
||||
import NoticeItem from './notice-item';
|
||||
import UserNotificationsDialog from '../../user-notifications';
|
||||
import { Utils } from '../../utils/utils';
|
||||
import './notification.css';
|
||||
|
||||
class Notification extends React.Component {
|
||||
constructor(props) {
|
||||
@@ -10,6 +14,7 @@ class Notification extends React.Component {
|
||||
showNotice: false,
|
||||
unseenCount: 0,
|
||||
noticeList: [],
|
||||
isShowNotificationDialog: this.getInitDialogState(),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -58,29 +63,60 @@ class Notification extends React.Component {
|
||||
|
||||
}
|
||||
|
||||
render() {
|
||||
getInitDialogState = () => {
|
||||
const searchParams = Utils.getUrlSearches();
|
||||
return searchParams.notifications === 'all';
|
||||
}
|
||||
|
||||
onNotificationDialogToggle = () => {
|
||||
let newSearch = this.state.isShowNotificationDialog ? null : 'all';
|
||||
Utils.updateSearchParameter('notifications', newSearch);
|
||||
this.setState({isShowNotificationDialog: !this.state.isShowNotificationDialog});
|
||||
}
|
||||
|
||||
onNotificationListToggle = () => {
|
||||
this.setState({showNotice: false});
|
||||
}
|
||||
|
||||
onMarkAllNotifications = () => {
|
||||
seafileAPI.updateNotifications().then(() => {
|
||||
this.setState({
|
||||
unseenCount: 0,
|
||||
});
|
||||
}).catch((error) => {
|
||||
this.setState({
|
||||
errorMsg: Utils.getErrorMsg(error, true)
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const { unseenCount } = this.state;
|
||||
return (
|
||||
<div id="notifications">
|
||||
<a href="#" onClick={this.onClick} className="no-deco" id="notice-icon" title={gettext('Notifications')} aria-label={gettext('Notifications')}>
|
||||
<span className="sf2-icon-bell"></span>
|
||||
<span className={`num ${this.state.unseenCount ? '' : 'hide'}`}>{this.state.unseenCount}</span>
|
||||
<span className="sf2-icon-bell" id="notification-popover"></span>
|
||||
<span className={`num ${unseenCount ? '' : 'hide'}`}>{unseenCount}</span>
|
||||
</a>
|
||||
<div id="notice-popover" className={`sf-popover ${this.state.showNotice ? '': 'hide'}`}>
|
||||
<div className="outer-caret up-outer-caret"><div className="inner-caret"></div></div>
|
||||
<div className="sf-popover-hd h-7 d-flex align-items-center justify-content-center">
|
||||
<h3 className="sf-popover-title title m-0">{gettext('Notifications')}</h3>
|
||||
<a href="#" onClick={this.onClick} title={gettext('Close')} aria-label={gettext('Close')} className="sf-popover-close js-close sf2-icon-x1 action-icon m-0"></a>
|
||||
</div>
|
||||
<div className="sf-popover-con">
|
||||
<ul className="notice-list list-unstyled">
|
||||
{this.state.showNotice &&
|
||||
<NotificationPopover
|
||||
headerText={gettext('Notification')}
|
||||
bodyText={gettext('Mark all as read')}
|
||||
footerText={gettext('View all notifications')}
|
||||
onNotificationListToggle={this.onNotificationListToggle}
|
||||
onNotificationDialogToggle={this.onNotificationDialogToggle}
|
||||
onMarkAllNotifications={this.onMarkAllNotifications}
|
||||
>
|
||||
<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>
|
||||
<a href={siteRoot + 'notice/list/'} className="view-all">{gettext('See All Notifications')}</a>
|
||||
</div>
|
||||
</div>
|
||||
</NotificationPopover>
|
||||
}
|
||||
{this.state.isShowNotificationDialog &&
|
||||
<UserNotificationsDialog onNotificationDialogToggle={this.onNotificationDialogToggle} />
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
Reference in New Issue
Block a user