1
0
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:
孙永强 2024-11-21 10:59:13 +08:00
parent 175803baba
commit 27dcce694b
9 changed files with 228 additions and 18 deletions

View File

@ -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) {
// }

View File

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

View File

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

View File

@ -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 => {

View File

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

View File

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

View File

@ -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('/')

View File

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

View File

@ -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()),