1
0
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:
Michael An
2023-09-08 13:54:05 +08:00
committed by GitHub
parent cd3ede438f
commit b46a60268f
24 changed files with 710 additions and 768 deletions

View File

@@ -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';

View 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;
}

View 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>
);
}
}

View 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;
}

View File

@@ -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>
);
}