ui: 收到站内消息是增加提醒 (#86)

This commit is contained in:
undefined committed 2021-04-05 02:32:04 +08:00
1 parent fb47598c57
commit 6cae5cb7d1
10 files changed
+62 -67

No files matched your search

+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "hydrooj", "name": "hydrooj",
"version": "2.21.5", "version": "2.21.6",
"bin": "bin/hydrooj.js", "bin": "bin/hydrooj.js",
"main": "dist/loader.js", "main": "dist/loader.js",
"typings": "dist/loader.d.ts", "typings": "dist/loader.d.ts",
+3 -5
View File
@@ -1,6 +1,6 @@
/* eslint-disable no-await-in-loop */ /* eslint-disable no-await-in-loop */
/* eslint-disable no-eval */ /* eslint-disable no-eval */
import fs from 'fs'; import fs from 'fs-extra';
import os from 'os'; import os from 'os';
import path from 'path'; import path from 'path';
import yaml from 'js-yaml'; import yaml from 'js-yaml';
@@ -130,9 +130,7 @@ export async function template(pending: string[], fail: string[]) {
try { try {
const files = getFiles(p); const files = getFiles(p);
for (const file of files) { for (const file of files) {
global.Hydro.ui.template[file] = fs.readFileSync( global.Hydro.ui.template[file] = await fs.readFile(path.resolve(p, file), 'utf-8');
path.resolve(p, file),
).toString();
} }
logger.info('Template init: %s', i); logger.info('Template init: %s', i);
} catch (e) { } catch (e) {
@@ -216,7 +214,7 @@ export async function service(pending: string[], fail: string[]) {
export async function script(pending: string[], fail: string[], active: string[]) { export async function script(pending: string[], fail: string[], active: string[]) {
for (const i of pending) { for (const i of pending) {
const p = path.resolve(i, 'script.js'); const p = path.resolve(i, 'script.js');
if (fs.existsSync(p) && !fail.includes(i)) { if (await fs.pathExists(p) && !fail.includes(i)) {
try { try {
logger.info('Script init: %s', i); logger.info('Script init: %s', i);
eval('require')(p); eval('require')(p);
+6 -2
View File
@@ -3,6 +3,7 @@ import {
VerifyPasswordError, UserAlreadyExistError, InvalidTokenError, VerifyPasswordError, UserAlreadyExistError, InvalidTokenError,
NotFoundError, UserNotFoundError, PermissionError, DomainAlreadyExistsError, NotFoundError, UserNotFoundError, PermissionError, DomainAlreadyExistsError,
} from '../error'; } from '../error';
import { Mdoc } from '../interface';
import * as bus from '../service/bus'; import * as bus from '../service/bus';
import { import {
Route, Connection, Handler, ConnectionHandler, param, Types, Route, Connection, Handler, ConnectionHandler, param, Types,
@@ -328,6 +329,7 @@ class HomeMessagesHandler extends Handler {
['Hydro', 'homepage'], ['Hydro', 'homepage'],
['home_messages', null], ['home_messages', null],
]; ];
await user.setById(this.user._id, { unreadMsg: 0 });
this.response.body = { messages: parsed, path }; this.response.body = { messages: parsed, path };
this.response.template = 'home_messages.html'; this.response.template = 'home_messages.html';
} }
@@ -367,9 +369,11 @@ class HomeMessagesConnectionHandler extends ConnectionHandler {
this.dispose = bus.on('user/message', this.onMessageReceived.bind(this)); this.dispose = bus.on('user/message', this.onMessageReceived.bind(this));
} }
async onMessageReceived(uid: number, e: any) { async onMessageReceived(uid: number, mdoc: Mdoc) {
if (uid !== this.user._id) return; if (uid !== this.user._id) return;
this.send(e); const udoc = await user.getById(this.domainId, mdoc.from);
udoc.gravatar_url = misc.gravatar(udoc.gravatar, 64);
this.send({ udoc, mdoc });
} }
async cleanup() { async cleanup() {
+1
View File
@@ -27,6 +27,7 @@ class MessageModel {
const udoc = await user.getById('system', to); const udoc = await user.getById('system', to);
bus.boardcast('user/message', to, mdoc, udoc); bus.boardcast('user/message', to, mdoc, udoc);
} }
await user.inc(to, 'unreadMsg', 1);
return mdoc; return mdoc;
} }
+1 -1
View File
@@ -114,7 +114,7 @@ AccountSetting(
Setting('setting_customize', 'backgroundImage', Setting('setting_customize', 'backgroundImage',
'/components/profile/backgrounds/1.jpg', 'text', 'Profile Background Image', '/components/profile/backgrounds/1.jpg', 'text', 'Profile Background Image',
'Choose the background image in your profile page.'), 'Choose the background image in your profile page.'),
Setting('setting_storage', 'usage', 0, 'number', 'Userfile Usage', null, FLAG_DISABLED | FLAG_HIDDEN), Setting('setting_storage', 'unreadMsg', 0, 'number', 'Unread Message Count', null, FLAG_DISABLED | FLAG_HIDDEN),
Setting('setting_storage', 'checkincnt', 0, 'number', 'Check In Counter', null, FLAG_DISABLED | FLAG_HIDDEN), Setting('setting_storage', 'checkincnt', 0, 'number', 'Check In Counter', null, FLAG_DISABLED | FLAG_HIDDEN),
Setting('setting_storage', 'lastcheckin', 0, 'number', 'Last checkin time', null, FLAG_DISABLED | FLAG_HIDDEN), Setting('setting_storage', 'lastcheckin', 0, 'number', 'Last checkin time', null, FLAG_DISABLED | FLAG_HIDDEN),
); );
+1
View File
@@ -146,6 +146,7 @@ export async function parallel<K extends keyof EventMap>(name: K, ...args: Param
const tasks: Promise<any>[] = []; const tasks: Promise<any>[] = [];
if (argv.showBus) logger.debug('parallel: %s %o', name, args); if (argv.showBus) logger.debug('parallel: %s %o', name, args);
for (const callback of _hooks[name] || []) { for (const callback of _hooks[name] || []) {
if (argv.busDetail) logger.debug(callback.toString());
tasks.push(callback.apply(this, args)); tasks.push(callback.apply(this, args));
} }
await Promise.all(tasks); await Promise.all(tasks);
@@ -7,27 +7,38 @@ import tpl from 'vj/utils/tpl';
const messagePage = new AutoloadPage('messagePage', () => { const messagePage = new AutoloadPage('messagePage', () => {
async function init() { async function init() {
if (UserContext.unreadMsg) {
new Notification({
type: 'info',
message: i18n('You have mew message.'),
action: () => window.open('/home/messages', '_blank'),
}).show();
}
const { default: SockJs } = await import('../socket'); const { default: SockJs } = await import('../socket');
const sock = new SockJs('/home/messages-conn'); const sock = new SockJs('/home/messages-conn');
sock.onmessage = (message) => { sock.onmessage = (message) => {
const msg = JSON.parse(message.data); const msg = JSON.parse(message.data);
if (msg.mdoc.flag | FLAG_ALERT) { console.log('Received message', msg);
if (msg.mdoc.flag & FLAG_ALERT) {
// Is alert // Is alert
const dialog = new InfoDialog({ new InfoDialog({
$body: tpl` $body: tpl`
<div class="typo"> <div class="typo">
<p>${i18n(msg.udoc.content)}</p> <p>${i18n('Message from {0}:', msg.mdoc.from)}</p>
<p>${i18n(msg.mdoc.content)}</p>
</div>`, </div>`,
}); }).open();
dialog.open();
} else { } else {
// Is message // Is message
Notification.show({ new Notification({
title: msg.udoc.uname, title: msg.udoc.uname,
avatar: msg.udoc.gravatar, avatar: msg.udoc.gravatar_url,
message: msg.mdoc.content, message: msg.mdoc.content,
}); duration: 15000,
action: () => window.open(`/home/messages?uid=${msg.udoc._id}`, '_blank'),
}).show();
} }
}; };
} }
@@ -1,9 +1,6 @@
import tpl from 'vj/utils/tpl'; import tpl from 'vj/utils/tpl';
import zIndexManager from 'vj/utils/zIndexManager'; import zIndexManager from 'vj/utils/zIndexManager';
let lastNotification = null;
let autoHideTimer = null;
export default class Notification { export default class Notification {
constructor({ constructor({
avatar, title, message, type = '', duration = 3000, action, avatar, title, message, type = '', duration = 3000, action,
@@ -11,78 +8,48 @@ export default class Notification {
this.type = type; this.type = type;
if (avatar) this.type += ' avatar'; if (avatar) this.type += ' avatar';
if (title) this.type += ' title'; if (title) this.type += ' title';
this.action = (() => { }) || action; this.action = action || (() => { });
this.$dom = $(tpl`<div class="notification ${type} hide"></div>`); this.$dom = $(tpl`<div class="notification ${type} hide"></div>`);
if (avatar) $(tpl`<img width="64" height="64" class="avatar" src="${avatar}"></img>`).appendTo(this.$dom); if (avatar) $(tpl`<img width="64" height="64" class="avatar" src="${avatar}"></img>`).appendTo(this.$dom);
if (title) $(tpl`<h2>${title}</h2>`).appendTo(this.$dom); if (title) {
$(tpl`<p>${message}</p>`).appendTo(this.$dom); $(tpl`<div class="notification-content"><h2>${title}</h2><p>${message}</p></div>`).appendTo(this.$dom);
this.$dom.on('clink', this.handleClick.bind(this)); } else $(tpl`<p>${message}</p>`).appendTo(this.$dom);
this.$dom.on('click', this.handleClick.bind(this));
this.$n = this.$dom this.$n = this.$dom
.css('z-index', zIndexManager.getNext()) .css('z-index', zIndexManager.getNext())
.appendTo('body'); .appendTo(document.body);
this.$n.width(); // force reflow this.$n.width(); // force reflow
this.$n.removeClass('hide'); this.duration = duration;
if (duration) this.autoHideTimer = setTimeout(this.hide.bind(this), duration);
} }
handleClick() { handleClick() {
this.action(); this.action();
} }
show(autohide = true) {
this.$n.removeClass('hide');
if (this.duration && autohide) this.autoHideTimer = setTimeout(this.hide.bind(this), this.duration);
}
hide() { hide() {
this.$n.addClass('hide'); this.$n.addClass('hide');
setTimeout(() => this.$n.remove(), 200); setTimeout(() => this.$n.remove(), 200);
} }
static success(message, duration) { static success(message, duration) {
return Notification.show({ type: 'success', message, duration }); return new Notification({ type: 'success', message, duration }).show();
} }
static info(message, duration) { static info(message, duration) {
return Notification.show({ type: 'info', message, duration }); return new Notification({ type: 'info', message, duration }).show();
} }
static warn(message, duration) { static warn(message, duration) {
return Notification.show({ type: 'warn', message, duration }); return new Notification({ type: 'warn', message, duration }).show();
} }
static error(message, duration) { static error(message, duration) {
return Notification.show({ type: 'error', message, duration }); return new Notification({ type: 'error', message, duration }).show();
}
static show({
avatar, title, message, type = '', duration = 3000, action,
}) {
if (lastNotification) Notification.hide();
if (avatar) type += ' avatar';
if (title) type += ' title';
const $dom = $(tpl`<div class="notification ${type} hide"></div>`);
if (avatar) $(tpl`<img width="64" height="64" class="avatar" src="${avatar}"></img>`).appendTo($dom);
if (title) $(tpl`<h2>${title}</h2>`).appendTo($dom);
$(tpl`<p>${message}</p>`).appendTo($dom);
$dom.on('clink', action);
const $n = $dom
.css('z-index', zIndexManager.getNext())
.appendTo('body');
$n.width(); // force reflow
$n.removeClass('hide');
lastNotification = $n;
autoHideTimer = setTimeout(Notification.hide, duration);
return $n;
}
static hide() {
if (!lastNotification) {
return;
}
if (autoHideTimer) {
clearTimeout(autoHideTimer);
autoHideTimer = null;
}
const $n = lastNotification;
$n.addClass('hide');
setTimeout(() => $n.remove(), 200);
lastNotification = null;
} }
} }
@@ -47,3 +47,16 @@
width: 100% width: 100%
min-width: auto min-width: auto
bottom: 0 bottom: 0
.notification-content
float: right
margin-top: rem(7px)
margin-left: rem(10px)
margin-right: rem(15px)
h2
color: #FFFFFF
p
margin-top: rem(7px);
font-size: larger
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@hydrooj/ui-default", "name": "@hydrooj/ui-default",
"version": "4.5.4", "version": "4.5.5",
"author": "undefined <i@undefined.moe>", "author": "undefined <i@undefined.moe>",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"main": "hydro.js", "main": "hydro.js",