mirror of
https://github.com/haiwen/seahub.git
synced 2025-04-28 03:10:45 +00:00
Update notifications.py
This commit is contained in:
parent
175803baba
commit
27dcce694b
@ -24,6 +24,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);
|
||||
|
||||
@ -33,7 +35,7 @@ class NoticeItem extends React.Component {
|
||||
let noticeItem = this.props.noticeItem;
|
||||
let noticeType = noticeItem.type;
|
||||
let detail = noticeItem.detail;
|
||||
|
||||
console.log(detail, noticeItem, noticeType)
|
||||
if (noticeType === MSG_TYPE_ADD_USER_TO_GROUP) {
|
||||
|
||||
let avatar_url = detail.group_staff_avatar_url;
|
||||
@ -375,6 +377,22 @@ class NoticeItem extends React.Component {
|
||||
return { avatar_url: null, notice };
|
||||
}
|
||||
|
||||
if (noticeType === MSG_TYPE_SEADOC_COMMENT) {
|
||||
let avatar_url = detail.share_from_user_avatar_url;
|
||||
let notice = '';
|
||||
console.log(111)
|
||||
notice = Utils.HTMLescape(notice);
|
||||
return { avatar_url, notice };
|
||||
}
|
||||
|
||||
if (noticeType === MSG_TYPE_SEADOC_REPLY) {
|
||||
let avatar_url = detail.share_from_user_avatar_url;
|
||||
let notice = detail.reply;
|
||||
notice = Utils.HTMLescape(notice);
|
||||
console.log(notice)
|
||||
return { avatar_url, notice };
|
||||
}
|
||||
|
||||
// if (noticeType === MSG_TYPE_GUEST_INVITATION_ACCEPTED) {
|
||||
|
||||
// }
|
||||
|
@ -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 {
|
||||
.notification-container .mark-all-read {
|
||||
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;
|
||||
@ -190,3 +184,22 @@
|
||||
.notification-body .notification-footer:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.notification-container .notification-body .mark-notifications {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.notification-container .notification-body .nav .nav-item .nav-link.active {
|
||||
color: #ED7109 !important;
|
||||
}
|
@ -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,9 @@ 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,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
@ -47,8 +51,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 } = this.props;
|
||||
return (
|
||||
<Popover
|
||||
className="notification-wrapper"
|
||||
@ -64,12 +72,55 @@ 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')}
|
||||
</span>
|
||||
</li>
|
||||
<li className="nav-item" onClick={() => this.tabItemClick('discussion')}>
|
||||
<span className={`nav-link ${currentTab === 'discussion' ? 'active' : ''}`}>
|
||||
{gettext('Discussion')}
|
||||
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
<span className="mark-all-read" onClick={this.onMarkAllNotifications}>
|
||||
{gettext('Mark all as read')}
|
||||
</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="mark-notifications" onClick={this.props.onMarkAllNotifications}>
|
||||
<ul className="nav dtable-external-links-tab">
|
||||
<li className="nav-item">
|
||||
<span className="nav-link">General</span>
|
||||
</li>
|
||||
<li className="nav-item">
|
||||
<span className="nav-link">Discussion</span>
|
||||
</li>
|
||||
</ul>
|
||||
<span className="mark-all-read">{bodyText}</span>
|
||||
</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>
|
||||
|
@ -14,6 +14,7 @@ class Notification extends React.Component {
|
||||
showNotice: false,
|
||||
unseenCount: 0,
|
||||
noticeList: [],
|
||||
currentTab: 'general',
|
||||
isShowNotificationDialog: this.getInitDialogState(),
|
||||
};
|
||||
}
|
||||
@ -37,14 +38,35 @@ class Notification extends React.Component {
|
||||
this.setState({ showNotice: true });
|
||||
}
|
||||
};
|
||||
|
||||
tabItemClick = (tab) => {
|
||||
const { currentTab } = this.state;
|
||||
if (currentTab === tab) return;
|
||||
this.setState({
|
||||
showNotice: true,
|
||||
currentTab: tab
|
||||
}, () => {
|
||||
this.loadNotices();
|
||||
});
|
||||
};
|
||||
|
||||
loadNotices = () => {
|
||||
let page = 1;
|
||||
let perPage = 5;
|
||||
seafileAPI.listNotifications(page, perPage).then(res => {
|
||||
let noticeList = res.data.notification_list;
|
||||
this.setState({ noticeList: noticeList });
|
||||
});
|
||||
if (this.state.currentTab === 'general') {
|
||||
seafileAPI.listNotifications(page, perPage).then(res => {
|
||||
let noticeList = res.data.notification_list;
|
||||
this.setState({ noticeList: noticeList });
|
||||
});
|
||||
}
|
||||
if (this.state.currentTab === 'discussion') {
|
||||
seafileAPI.listSdocNotifications(page, perPage).then(res => {
|
||||
let noticeList = res.data.notification_list;
|
||||
console.log(noticeList)
|
||||
this.setState({ noticeList: noticeList });
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
onNoticeItemClick = (noticeItem) => {
|
||||
@ -91,7 +113,7 @@ class Notification extends React.Component {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { unseenCount } = this.state;
|
||||
const { unseenCount, currentTab } = this.state;
|
||||
return (
|
||||
<div id="notifications">
|
||||
<a href="#" onClick={this.onClick} className="no-deco" id="notice-icon" title={gettext('Notifications')} aria-label={gettext('Notifications')}>
|
||||
@ -103,9 +125,11 @@ 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}
|
||||
>
|
||||
<ul className="notice-list list-unstyled" id="notice-popover">
|
||||
{this.state.noticeList.map(item => {
|
||||
|
@ -1460,6 +1460,16 @@ class SeafileAPI {
|
||||
return this.req.get(url, { params: params });
|
||||
}
|
||||
|
||||
|
||||
listSdocNotifications(page, perPage) {
|
||||
const url = this.server + '/api/v2.1/sdoc-notifications/';
|
||||
let params = {
|
||||
page: page,
|
||||
per_page: perPage
|
||||
};
|
||||
return this.req.get(url, { params: params });
|
||||
}
|
||||
|
||||
updateNotifications() {
|
||||
const url = this.server + '/api/v2.1/notifications/';
|
||||
return this.req.put(url);
|
||||
|
@ -14,13 +14,16 @@ from seahub.api2.throttling import UserRateThrottle
|
||||
from seahub.notifications.models import UserNotification
|
||||
|
||||
from seahub.notifications.models import get_cache_key_of_unseen_notifications
|
||||
from seahub.notifications.utils import update_notice_detail
|
||||
from seahub.notifications.utils import update_notice_detail, update_sdoc_notice_detail
|
||||
from seahub.api2.utils import api_error
|
||||
from seahub.seadoc.models import SeadocCommentReply, SeadocNotification
|
||||
from seahub.utils.timeutils import datetime_to_isoformat_timestr
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
json_content_type = 'application/json; charset=utf-8'
|
||||
|
||||
NOTIF_TYPE = ['general', 'discussion']
|
||||
|
||||
|
||||
class NotificationsView(APIView):
|
||||
|
||||
@ -161,3 +164,61 @@ class NotificationView(APIView):
|
||||
cache.delete(cache_key)
|
||||
|
||||
return Response({'success': True})
|
||||
|
||||
|
||||
class SdocNotificationView(APIView):
|
||||
def get(self, request):
|
||||
""" used for get sdoc notifications
|
||||
|
||||
Permission checking:
|
||||
1. login user.
|
||||
"""
|
||||
notice_type = request.GET.get('type', 'general')
|
||||
if notice_type not in NOTIF_TYPE:
|
||||
error_msg = 'notice_type invalid.'
|
||||
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||
|
||||
result = {}
|
||||
|
||||
username = request.user.username
|
||||
|
||||
try:
|
||||
per_page = int(request.GET.get('per_page', ''))
|
||||
page = int(request.GET.get('page', ''))
|
||||
except ValueError:
|
||||
per_page = 25
|
||||
page = 1
|
||||
|
||||
start = (page - 1) * per_page
|
||||
end = page * per_page
|
||||
|
||||
notice_list = SeadocNotification.objects.list_all_by_user(username, start, end)
|
||||
result_notices = update_sdoc_notice_detail(request, notice_list)
|
||||
notification_list = []
|
||||
for i in result_notices:
|
||||
if i.detail is not None:
|
||||
notice = {}
|
||||
notice['id'] = i.id
|
||||
notice['type'] = i.msg_type
|
||||
notice['detail'] = i.detail
|
||||
notice['time'] = datetime_to_isoformat_timestr(i.created_at)
|
||||
notice['seen'] = i.seen
|
||||
|
||||
notification_list.append(notice)
|
||||
cache_key = get_cache_key_of_unseen_notifications(username)
|
||||
unseen_count_from_cache = cache.get(cache_key, None)
|
||||
|
||||
# for case of count value is `0`
|
||||
if unseen_count_from_cache is not None:
|
||||
result['unseen_count'] = unseen_count_from_cache
|
||||
else:
|
||||
unseen_count = SeadocNotification.objects.filter(username=username, seen=False).count()
|
||||
result['unseen_count'] = unseen_count
|
||||
cache.set(cache_key, unseen_count)
|
||||
|
||||
total_count = SeadocNotification.objects.filter(username=username).count()
|
||||
|
||||
result['notification_list'] = notification_list
|
||||
result['count'] = total_count
|
||||
|
||||
return Response(result)
|
||||
|
@ -402,6 +402,24 @@ def update_notice_detail(request, notices):
|
||||
return notices
|
||||
|
||||
|
||||
def update_sdoc_notice_detail(request, notices):
|
||||
repo_dict = {}
|
||||
for notice in notices:
|
||||
if notice.is_comment():
|
||||
try:
|
||||
d = json.loads(notice.detail)
|
||||
notice.detail = d
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
elif notice.is_reply():
|
||||
try:
|
||||
d = json.loads(notice.detail)
|
||||
notice.detail = d
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
return notices
|
||||
|
||||
|
||||
def gen_sdoc_smart_link(doc_uuid, with_service_url=True):
|
||||
service_url = get_service_url()
|
||||
service_url = service_url.rstrip('/')
|
||||
|
@ -247,6 +247,11 @@ class SeadocCommentReply(models.Model):
|
||||
}
|
||||
|
||||
|
||||
|
||||
### sdoc notification
|
||||
MSG_TYPE_REPLY = 'reply'
|
||||
MSG_TYPE_COMMENT = 'comment'
|
||||
|
||||
class SeadocNotificationManager(models.Manager):
|
||||
def total_count(self, doc_uuid, username):
|
||||
return self.filter(doc_uuid=doc_uuid, username=username).count()
|
||||
@ -259,6 +264,9 @@ class SeadocNotificationManager(models.Manager):
|
||||
|
||||
def delete_by_ids(self, doc_uuid, username, ids):
|
||||
return self.filter(doc_uuid=doc_uuid, username=username, id__in=ids).delete()
|
||||
|
||||
def list_all_by_user(self, username, start, end):
|
||||
return self.filter(username=username).order_by('-created_at')[start: end]
|
||||
|
||||
|
||||
class SeadocNotification(models.Model):
|
||||
@ -285,3 +293,9 @@ class SeadocNotification(models.Model):
|
||||
'detail': json.loads(self.detail),
|
||||
'seen': self.seen,
|
||||
}
|
||||
|
||||
def is_comment(self):
|
||||
return self.msg_type == MSG_TYPE_COMMENT
|
||||
|
||||
def is_reply(self):
|
||||
return self.msg_type == MSG_TYPE_REPLY
|
||||
|
@ -91,7 +91,7 @@ from seahub.api2.endpoints.invitations import InvitationsView, InvitationsBatchV
|
||||
from seahub.api2.endpoints.invitation import InvitationView, InvitationRevokeView
|
||||
from seahub.api2.endpoints.repo_share_invitations import RepoShareInvitationsView, RepoShareInvitationsBatchView
|
||||
from seahub.api2.endpoints.repo_share_invitation import RepoShareInvitationView
|
||||
from seahub.api2.endpoints.notifications import NotificationsView, NotificationView
|
||||
from seahub.api2.endpoints.notifications import NotificationsView, NotificationView, SdocNotificationView
|
||||
from seahub.api2.endpoints.repo_file_uploaded_bytes import RepoFileUploadedBytesView
|
||||
from seahub.api2.endpoints.user_avatar import UserAvatarView
|
||||
from seahub.api2.endpoints.wikis import WikisView, WikiView
|
||||
@ -522,6 +522,7 @@ urlpatterns = [
|
||||
|
||||
re_path(r'^api/v2.1/notifications/$', NotificationsView.as_view(), name='api-v2.1-notifications'),
|
||||
re_path(r'^api/v2.1/notification/$', NotificationView.as_view(), name='api-v2.1-notification'),
|
||||
re_path(r'^api/v2.1/sdoc-notifications/$', SdocNotificationView.as_view(), name='api-v2.1-sdoc-notifications'),
|
||||
|
||||
## user::invitations
|
||||
re_path(r'^api/v2.1/invitations/$', InvitationsView.as_view()),
|
||||
|
Loading…
Reference in New Issue
Block a user