feat(messages): new message type of must read, if user has a not read message and this message is must read, so, user must read it otherwise can not do other operate

This commit is contained in:
OldHawk
2018-05-28 00:03:58 +08:00
parent efe1acc3ed
commit f3e4b86a3a
18 changed files with 269 additions and 61 deletions

View File

@@ -124,6 +124,8 @@ module.exports = {
* @debugAnnounceUser: setting of debug announce user, NOTE: enable this need @app.writeServerDebugLog must be true too
* @debugAll: if true, debug all announce user, else debug user in ids list below
* @ids: debug announce user id list
* @debugClientSideUser: setting of debug client side user
* @ids: debug user id list
*/
announce: {
url: 'http://localhost:3000/announce',
@@ -162,6 +164,11 @@ module.exports = {
'59227f9095602327ea1d96ba',
'592280c464be9e281a1ec56e'
]
},
debugClientSideUser: {
ids: [
'59227f9095602327ea1d96ba'
]
}
},

View File

@@ -1124,6 +1124,7 @@
MESSAGES_IS_LOADING: 'Loading messages, please wait ...',
MESSAGES_IS_EMPTY: 'Messages Box is empty!',
OFFICIAL_MESSAGES_TOOLTIP: '<h4>Tip:</h4>In order to maintain a clean list of official messages, please delete messages that have expired over time as they cannot be unilaterally cleared in the users message center.',
MARK_AS_ALREADY_READ: 'Mark as already read, do not show next time.',
MESSAGES_FIELD: {
TO: 'Send to:',
TYPE: 'Message type:',
@@ -1143,7 +1144,9 @@
INFO_SEND_AT: 'at',
LAST_REPLY_AT: 'Latest reply at',
NEW_MSG: 'New messages',
SERVER_SEND: 'Server send'
SERVER_SEND: 'Server send',
MUST_READ: 'Must read message',
MUST_READ_KEY: 'MustRead'
},
BUTTON_MESSAGE_DELETE: 'Delete',
BUTTON_MESSAGE_CLOSE: 'Close(esc)',

View File

@@ -1124,6 +1124,7 @@
MESSAGES_IS_LOADING: '正在讀取, 請稍候 ...',
MESSAGES_IS_EMPTY: '訊息列表為空!',
OFFICIAL_MESSAGES_TOOLTIP: '<h4>提示:</h4>為了維護一個乾淨的官方訊息列表,請及時將長時間過期的訊息刪除,因為這些訊息在用戶的訊息中心裡不能被單方面清理。',
MARK_AS_ALREADY_READ: '標記為已讀,下次不再提示。',
MESSAGES_FIELD: {
TO: '傳送至:',
TYPE: '訊息類別:',
@@ -1143,7 +1144,9 @@
INFO_SEND_AT: '於',
LAST_REPLY_AT: '最新回覆於',
NEW_MSG: '條未讀訊息',
SERVER_SEND: '伺服器傳送'
SERVER_SEND: '伺服器傳送',
MUST_READ: '必讀消息',
MUST_READ_KEY: '必讀'
},
BUTTON_MESSAGE_DELETE: '刪除',
BUTTON_MESSAGE_CLOSE: '關閉(esc)',

View File

@@ -1124,6 +1124,7 @@
MESSAGES_IS_LOADING: '正在装载, 请稍候 ...',
MESSAGES_IS_EMPTY: '消息列表为空!',
OFFICIAL_MESSAGES_TOOLTIP: '<h4>提示:</h4>为了维护一个干净的官方消息列表,请及时将长时间过期的消息删除,因为这些消息在用户的消息中心里不能被单方面清理。',
MARK_AS_ALREADY_READ: '标记为已读,下次不再提示。',
MESSAGES_FIELD: {
TO: '发送至:',
TYPE: '消息类型:',
@@ -1143,7 +1144,9 @@
INFO_SEND_AT: '于',
LAST_REPLY_AT: '最新回复于',
NEW_MSG: '条未读消息',
SERVER_SEND: '服务器发送'
SERVER_SEND: '服务器发送',
MUST_READ: '必读消息',
MUST_READ_KEY: '必读'
},
BUTTON_MESSAGE_DELETE: '删除',
BUTTON_MESSAGE_CLOSE: '关闭(esc)',

View File

@@ -14,7 +14,6 @@
function stateChangeStart(event, toState, toParams, fromState, fromParams) {
$('.side-background').remove();
$('.textcomplete-dropdown').remove();
$('.popup_wrapper').remove();
// Check authentication before changing state
if (toState.data && toState.data.roles && toState.data.roles.length > 0) {
var allowed = false;

View File

@@ -0,0 +1,78 @@
(function () {
'use strict';
angular
.module('core')
.controller('FooterController', FooterController);
FooterController.$inject = ['$scope', '$state', '$timeout', '$translate', 'Authentication', 'MeanTorrentConfig', 'localStorageService',
'getStorageLangService', 'TorrentsService'];
function FooterController($scope, $state, $timeout, $translate, Authentication, MeanTorrentConfig, localStorageService,
getStorageLangService, TorrentsService) {
var vm = this;
vm.user = Authentication.user;
vm.langService = getStorageLangService;
vm.language = MeanTorrentConfig.meanTorrentConfig.language;
vm.appConfig = MeanTorrentConfig.meanTorrentConfig.app;
vm.signConfig = MeanTorrentConfig.meanTorrentConfig.sign;
/**
* document.ready()
*/
$(document).ready(function () {
$('#warning_popup').popup({
outline: false,
focusdelay: 400,
vertical: 'top',
autoopen: false,
opacity: 0.6,
closetransitionend: function () {
$('#warning_popup_wrapper').remove();
$('#warning_popup_background').remove();
}
});
});
/**
* getWarningInfo
*/
vm.getWarningInfo = function () {
var sw = localStorageService.get('showed_warning');
if (!vm.user && vm.appConfig.showDemoWarningPopup && !sw) {
$timeout(function () {
$('#warning_popup').popup('show');
}, 10);
localStorageService.set('showed_warning', true);
}
if (sw) {
$('#warning_popup_wrapper').remove();
$('#warning_popup_background').remove();
}
};
/**
* changeLanguage
* @param langKey
*/
vm.changeLanguage = function (langKey) {
var lang = localStorageService.get('storage_user_lang');
if (lang !== langKey) {
localStorageService.set('storage_user_lang', langKey);
$translate.use(langKey);
$state.reload();
}
};
/**
* getSiteInfo
*/
vm.getSiteInfo = function () {
TorrentsService.siteInfo(function (data) {
vm.siteInfo = data;
});
};
}
}());

View File

@@ -5,11 +5,13 @@
.module('core')
.controller('HeaderController', HeaderController);
HeaderController.$inject = ['$scope', '$state', '$timeout', '$translate', 'Authentication', 'menuService', 'MeanTorrentConfig', 'localStorageService',
'ScoreLevelService', 'InvitationsService', '$interval', 'MessagesService', 'TorrentsService', 'UsersService', 'DebugConsoleService', 'getStorageLangService'];
HeaderController.$inject = ['$scope', '$rootScope', '$state', '$timeout', '$translate', 'Authentication', 'menuService', 'MeanTorrentConfig', 'localStorageService',
'ScoreLevelService', 'InvitationsService', '$interval', 'MessagesService', 'marked', 'UsersService', 'DebugConsoleService', 'getStorageLangService',
'AdminMessagesService'];
function HeaderController($scope, $state, $timeout, $translate, Authentication, menuService, MeanTorrentConfig, localStorageService, ScoreLevelService,
InvitationsService, $interval, MessagesService, TorrentsService, UsersService, mtDebug, getStorageLangService) {
function HeaderController($scope, $rootScope, $state, $timeout, $translate, Authentication, menuService, MeanTorrentConfig, localStorageService, ScoreLevelService,
InvitationsService, $interval, MessagesService, marked, UsersService, mtDebug, getStorageLangService,
AdminMessagesService) {
$scope.$state = $state;
var vm = this;
vm.user = Authentication.user;
@@ -37,15 +39,26 @@
/**
* document.ready()
*/
$(document).ready(function () {
$('#warning_popup').popup({
angular.element(document).ready(function () {
$('#must_read_popup').popup({
outline: false,
focusdelay: 400,
vertical: 'top',
autoopen: false,
opacity: 0.6,
blur: false,
escape: false,
closetransitionend: function () {
$('.popup_wrapper').remove();
if ($scope.mustReadMessage.markReadMessage) {
var mrMsg = new AdminMessagesService({
_adminMessageId: $scope.mustReadMessage._id
});
mrMsg.$update(function (res) {
$timeout(function () {
vm.getCountUnread();
}, 10);
});
}
}
});
});
@@ -190,23 +203,6 @@
}
};
/**
* getWarningInfo
*/
vm.getWarningInfo = function () {
var sw = localStorageService.get('showed_warning');
if (vm.appConfig.showDemoWarningPopup && !sw) {
$timeout(function () {
$('#warning_popup').popup('show');
}, 10);
localStorageService.set('showed_warning', true);
}
if (sw) {
$('.popup_wrapper').remove();
}
};
/**
* checkMessageUnread
*/
@@ -219,10 +215,30 @@
if (Authentication.user) {
MessagesService.countUnread(function (data) {
vm.unreadCount = data.countAll;
if (data.mustRead.length > 0) {
$rootScope.mustReadMessage = data.mustRead[0];
$rootScope.mustReadMessage.markReadMessage = false;
$timeout(function () {
$('#must_read_popup').popup('show');
}, 10);
}
});
}
};
/**
* getMustReadMessageContentMarked
* @param m
* @returns {*}
*/
$rootScope.getMustReadMessageContentMarked = function (m) {
if (m) {
return marked(m.content, {sanitize: true});
}
};
/**
* checkHnRWarning
*/
@@ -261,20 +277,7 @@
$translate.use(langKey);
$state.reload();
//$state.transitionTo($state.current, $stateParams, {
// reload: true, inherit: false, notify: false
//});
}
};
/**
* getSiteInfo
*/
vm.getSiteInfo = function () {
TorrentsService.siteInfo(function (data) {
vm.siteInfo = data;
mtDebug.info(data);
});
};
}
}());

View File

@@ -539,3 +539,56 @@
padding-top: 30px;
border-top: solid 1px #1a1a1a;
}
//popup overlay----------------------------------------------
/* csslint ignore:start */
.must_read_popup {
box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
display: none;
}
#must_read_popup_background {
transition: all 0.3s 0.05s;
}
#must_read_popup,
#must_read_popup_wrapper {
transition: all 0.3s ease-out;
}
#must_read_popup {
transform: translateX(0) translateY(-40%);
}
.popup_visible #must_read_popup {
transform: translateX(0) translateY(0);
}
#must_read_popup,
#must_read_popup_wrapper {
.popup_content {
background-color: #fff;
opacity: 0.8 !important;
width: 80%;
top: 20%;
border-radius: 4px;
h4,
.h4 {
line-height: 1.6 !important;
}
.msg-content {
font-size: 16px;
}
.checkbox {
font-size: 16px;
font-weight: bold;
color: @brand-danger;
}
}
.downloading-filename {
word-break: break-all;
}
}
/* csslint ignore:end */
//popup overlay end -------------------------------------------

View File

@@ -6,10 +6,12 @@
.module('core')
.factory('DebugConsoleService', DebugConsoleService);
DebugConsoleService.$inject = ['MeanTorrentConfig'];
DebugConsoleService.$inject = ['MeanTorrentConfig', 'Authentication'];
function DebugConsoleService(MeanTorrentConfig) {
function DebugConsoleService(MeanTorrentConfig, Authentication) {
var appConfig = MeanTorrentConfig.meanTorrentConfig.app;
var announceConfig = MeanTorrentConfig.meanTorrentConfig.announce;
var user = Authentication.user;
var service = {
info: debugInfo
@@ -18,7 +20,7 @@
return service;
function debugInfo(obj) {
if (appConfig.showClientDebugLog) {
if (appConfig.showClientDebugLog && announceConfig.debugClientSideUser.ids.includes(user._id)) {
console.log(obj);
}
}

View File

@@ -1,8 +1,8 @@
<div ng-controller="HeaderController as vm" ng-init="vm.getWarningInfo(); vm.getSiteInfo();">
<div ng-controller="FooterController as vm" ng-init="vm.getWarningInfo(); vm.getSiteInfo();">
<div class="filter-footer">
<div class="container">
<div class="row">
<div class="col-md-6 col-md-offset-0 site-info" ng-hide="!vm.authentication.user && !vm.signConfig.showFooterCountInfoForGuest">
<div class="col-md-6 col-md-offset-0 site-info" ng-hide="!vm.user && !vm.signConfig.showFooterCountInfoForGuest">
<div class="row">
<div class="info-item col-xs-6 col-xs-offset-0 col-sm-5 col-sm-offset-1 col-md-6 col-md-offset-0 col-md-offset-0">{{'TOTAL_USERS' | translate}}: <span
class="item-data">{{vm.siteInfo.totalUsers}}</span></div>
@@ -65,4 +65,3 @@
</div>
</div>
</div>

View File

@@ -114,3 +114,25 @@
</ul>
</nav>
</div>
<div id="must_read_popup" style="display: none;">
<div class="row padding-top-30 padding-bottom-30">
<div class="col-xs-10 col-xs-offset-1 col-sm-8 col-sm-offset-2 text-center">
<h3>{{'MESSAGE_TYPE_' + mustReadMessage.type.toUpperCase() | translate}}</h3>
</div>
<div class="col-xs-10 col-xs-offset-1 col-sm-8 col-sm-offset-2">
<p class="h4 msg-title">{{mustReadMessage.title}}</p>
<p class="msg-content" ng-bind-html="getMustReadMessageContentMarked(mustReadMessage);"></p>
</div>
<div class="col-xs-10 col-xs-offset-1 col-sm-8 col-sm-offset-2 text-center">
<div class="checkbox">
<label>
<input type="checkbox" ng-model="mustReadMessage.markReadMessage">{{'MARK_AS_ALREADY_READ' | translate}}
</label>
</div>
</div>
<div class="col-xs-10 col-xs-offset-1 col-sm-8 col-sm-offset-2 text-center margin-top-10">
<button class="must_read_popup_close btn btn-success btn-width-100">{{'BUTTON_UPLOADED_POPUP_CLOSE' | translate}}</button>
</div>
</div>
</div>

View File

@@ -6,10 +6,10 @@
.controller('AdminMessageController', AdminMessageController);
AdminMessageController.$inject = ['$scope', '$state', '$translate', '$timeout', 'Authentication', '$filter', 'NotifycationService', 'AdminMessagesService',
'MeanTorrentConfig', 'ModalConfirmService', 'marked', '$rootScope'];
'MeanTorrentConfig', 'ModalConfirmService', 'marked', '$rootScope', 'DebugConsoleService'];
function AdminMessageController($scope, $state, $translate, $timeout, Authentication, $filter, NotifycationService, AdminMessagesService,
MeanTorrentConfig, ModalConfirmService, marked, $rootScope) {
MeanTorrentConfig, ModalConfirmService, marked, $rootScope, mtDebug) {
var vm = this;
vm.messageConfig = MeanTorrentConfig.meanTorrentConfig.messages;
vm.inputLengthConfig = MeanTorrentConfig.meanTorrentConfig.inputLength;
@@ -29,6 +29,8 @@
var msg = new AdminMessagesService(vm.messageFields);
msg.type = vm.messageType;
console.log(msg);
msg.$save(function (response) {
successCallback(response);
}, function (errorResponse) {
@@ -54,6 +56,7 @@
vm.getAdminMessages = function () {
AdminMessagesService.query(function (data) {
vm.adminMessages = data;
mtDebug.info(data);
});
};

View File

@@ -8,9 +8,9 @@
AdminMessagesService.$inject = ['$resource', 'CacheFactory'];
function AdminMessagesService($resource, CacheFactory) {
var adminMessagesCache = CacheFactory.get('adminMessagesCache') || CacheFactory.createCache('adminMessagesCache');
var messagesCache = CacheFactory.get('messagesCache') || CacheFactory.createCache('messagesCache');
var removeCache = function (res) {
adminMessagesCache.removeAll();
messagesCache.removeAll();
return res.resource;
};
@@ -19,12 +19,12 @@
}, {
get: {
method: 'GET',
cache: adminMessagesCache
cache: messagesCache
},
query: {
method: 'GET',
isArray: true,
cache: adminMessagesCache
cache: messagesCache
},
update: {
method: 'PUT',

View File

@@ -61,6 +61,15 @@
</dd>
</div>
<div>
<dt class="h-line"></dt>
<dd class="h-line">
<label style="font-weight: normal">
<input type="checkbox" ng-model="vm.messageFields.mustRead"> {{'MESSAGES_FIELD.MUST_READ' | translate}}
</label>
</dd>
</div>
<div>
<dt class="h-line"></dt>
<dd class="h-line">
@@ -100,7 +109,12 @@
<tbody>
<tr class="message-item" ng-repeat="m in vm.adminMessages">
<td class="col-md-8 width-400">
<div class="message-title-admin text-long"><h4 ng-bind="m.title"></h4></div>
<div class="message-title-admin text-long">
<span class="h4" ng-bind="m.title"></span>
<span class="badge badge_danger" ng-if="m.mustRead">
{{'MESSAGES_FIELD.MUST_READ_KEY' | translate}}
</span>
</div>
<p class="message-content-admin" ng-bind-html="vm.getContentMarked(m);"></p>

View File

@@ -76,6 +76,7 @@
<i class="fa fa-envelope message-avatar server-message-avatar" ng-if="m.type!='user' && m.type!='server'"></i>
<span class="message-title" ng-class="vm.isUnread(m)? 'unread' : 'read'" ng-bind="m.title"></span>
<span class="badge badge_danger" ng-if="m.mustRead">{{'MESSAGES_FIELD.MUST_READ_KEY' | translate}}</span>
<p class="message-info" ng-if="m.type=='user'">
<span user-info="m.from_user"

View File

@@ -242,6 +242,7 @@ exports.countUnread = function (req, res) {
}
});
};
var countTo = function (callback) {
Message.count({
type: 'user',
@@ -255,6 +256,7 @@ exports.countUnread = function (req, res) {
}
});
};
var countServer = function (callback) {
Message.count({
type: 'server',
@@ -268,12 +270,12 @@ exports.countUnread = function (req, res) {
}
});
};
var countAdminSystemMessage = function (callback) {
AdminMessage.count({
type: 'system',
createdat: {$gt: req.user.created},
_readers: {$not: {$in: [req.user._id]}}
}, function (err, count) {
if (err) {
callback(err, null);
@@ -282,12 +284,12 @@ exports.countUnread = function (req, res) {
}
});
};
var countAdminAdvertMessage = function (callback) {
AdminMessage.count({
type: 'advert',
createdat: {$gt: req.user.created},
_readers: {$not: {$in: [req.user._id]}}
}, function (err, count) {
if (err) {
callback(err, null);
@@ -297,7 +299,21 @@ exports.countUnread = function (req, res) {
});
};
async.parallel([countFrom, countTo, countServer, countAdminSystemMessage, countAdminAdvertMessage], function (err, results) {
var getMustReadMessage = function (callback) {
AdminMessage.find({
mustRead: true,
_readers: {$not: {$in: [req.user._id]}}
}, function (err, msgs) {
if (err) {
callback(err, null);
} else {
callback(null, msgs);
}
});
};
async.parallel([countFrom, countTo, countServer, countAdminSystemMessage, countAdminAdvertMessage, getMustReadMessage], function (err, results) {
if (err) {
return res.status(422).send(err);
} else {
@@ -307,7 +323,8 @@ exports.countUnread = function (req, res) {
countTo: results[1],
countServer: results[2],
countSystem: results[3],
countAdvert: results[4]
countAdvert: results[4],
mustRead: results[5]
});
}
});

View File

@@ -28,6 +28,10 @@ var AdminMessageSchema = new Schema({
type: String,
default: 'system'
},
mustRead: {
type: Boolean,
default: false
},
_readers: [{
type: Schema.Types.ObjectId,
ref: 'User'

View File

@@ -819,9 +819,6 @@
<div class="col-xs-10 col-xs-offset-1 col-sm-8 col-sm-offset-2">
<h4 translate="POPUP_UPLOADED_TOOLTIP"></h4>
</div>
<!--<div class="col-xs-10 col-xs-offset-1 col-sm-8 col-sm-offset-2">-->
<!--<h6 class="text-muted downloading-filename margin-left-30">{{'TORRENT_DOWNLOADING_FILENAME' | translate}} {{announceConfig.announcePrefix}}{{downloadingTorrent.torrent_filename}}</h6>-->
<!--</div>-->
<div class="col-xs-10 col-xs-offset-1 col-sm-8 col-sm-offset-2 text-center">
<div class="checkbox text-muted">
<label>