mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-20 01:38:19 +08:00
ui: simplify user_detail page
This commit is contained in:
1 parent
62ec797652
commit
dd037e36d7
7 files changed
+27
-110
No files matched your search
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "hydrooj",
|
"name": "hydrooj",
|
||||||
"version": "2.36.12",
|
"version": "2.36.13",
|
||||||
"bin": "bin/hydrooj.js",
|
"bin": "bin/hydrooj.js",
|
||||||
"main": "src/loader",
|
"main": "src/loader",
|
||||||
"module": "src/loader",
|
"module": "src/loader",
|
||||||
|
|||||||
@@ -2,18 +2,15 @@ import moment from 'moment-timezone';
|
|||||||
import {
|
import {
|
||||||
BlacklistedError, InvalidTokenError, LoginError,
|
BlacklistedError, InvalidTokenError, LoginError,
|
||||||
SystemError, UserAlreadyExistError, UserFacingError,
|
SystemError, UserAlreadyExistError, UserFacingError,
|
||||||
UserNotFoundError, VerifyPasswordError,
|
UserNotFoundError, ValidationError, VerifyPasswordError,
|
||||||
} from '../error';
|
} from '../error';
|
||||||
import { User } from '../interface';
|
import { User } from '../interface';
|
||||||
import avatar from '../lib/avatar';
|
import avatar from '../lib/avatar';
|
||||||
import { sendMail } from '../lib/mail';
|
import { sendMail } from '../lib/mail';
|
||||||
import paginate from '../lib/paginate';
|
|
||||||
import { isEmail, isPassword, isUname } from '../lib/validator';
|
import { isEmail, isPassword, isUname } from '../lib/validator';
|
||||||
import BlackListModel from '../model/blacklist';
|
import BlackListModel from '../model/blacklist';
|
||||||
import { PERM, PRIV } from '../model/builtin';
|
import { PERM, PRIV } from '../model/builtin';
|
||||||
import oauth from '../model/oauth';
|
import oauth from '../model/oauth';
|
||||||
import problem, { ProblemDoc } from '../model/problem';
|
|
||||||
import record from '../model/record';
|
|
||||||
import * as system from '../model/system';
|
import * as system from '../model/system';
|
||||||
import task from '../model/task';
|
import task from '../model/task';
|
||||||
import token from '../model/token';
|
import token from '../model/token';
|
||||||
@@ -136,6 +133,7 @@ export class UserRegisterHandler extends Handler {
|
|||||||
});
|
});
|
||||||
await sendMail(mail, 'Sign Up', 'user_register_mail', m);
|
await sendMail(mail, 'Sign Up', 'user_register_mail', m);
|
||||||
this.response.template = 'user_register_mail_sent.html';
|
this.response.template = 'user_register_mail_sent.html';
|
||||||
|
this.response.body = { mail };
|
||||||
} else this.response.redirect = this.url('user_register_with_code', { code: t[0] });
|
} else this.response.redirect = this.url('user_register_with_code', { code: t[0] });
|
||||||
} else if (phoneNumber) {
|
} else if (phoneNumber) {
|
||||||
if (!global.Hydro.lib.sendSms) throw new SystemError('Cannot send sms');
|
if (!global.Hydro.lib.sendSms) throw new SystemError('Cannot send sms');
|
||||||
@@ -148,7 +146,7 @@ export class UserRegisterHandler extends Handler {
|
|||||||
);
|
);
|
||||||
await global.Hydro.lib.sendSms(phoneNumber, 'register', t[0]);
|
await global.Hydro.lib.sendSms(phoneNumber, 'register', t[0]);
|
||||||
this.response.template = 'user_register_sms.html';
|
this.response.template = 'user_register_sms.html';
|
||||||
}
|
} else throw new ValidationError('mail');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,41 +235,19 @@ class UserDetailHandler extends Handler {
|
|||||||
async get(domainId: string, uid: number) {
|
async get(domainId: string, uid: number) {
|
||||||
if (uid === 0) throw new UserNotFoundError(0);
|
if (uid === 0) throw new UserNotFoundError(0);
|
||||||
const isSelfProfile = this.user._id === uid;
|
const isSelfProfile = this.user._id === uid;
|
||||||
const udoc = await user.getById(domainId, uid);
|
const [udoc, sdoc] = await Promise.all([
|
||||||
if (!udoc) throw new UserNotFoundError(uid);
|
user.getById(domainId, uid),
|
||||||
const [sdoc, rdocs, [pdocs, pcount]] = await Promise.all([
|
|
||||||
token.getMostRecentSessionByUid(uid),
|
token.getMostRecentSessionByUid(uid),
|
||||||
record.getByUid(domainId, uid, 30),
|
|
||||||
this.user.hasPerm(PERM.PERM_VIEW_PROBLEM)
|
|
||||||
? paginate(
|
|
||||||
problem.getMulti(domainId, { owner: uid }),
|
|
||||||
1,
|
|
||||||
100,
|
|
||||||
)
|
|
||||||
: [[], 0, 0] as [ProblemDoc[], number, number],
|
|
||||||
]);
|
]);
|
||||||
const pdict = this.user.hasPerm(PERM.PERM_VIEW_PROBLEM)
|
if (!udoc) throw new UserNotFoundError(uid);
|
||||||
? await problem.getList(
|
|
||||||
domainId, rdocs.map((rdoc) => rdoc.pid),
|
|
||||||
this.user.hasPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN) || this.user._id, false,
|
|
||||||
)
|
|
||||||
: Object.fromEntries(rdocs.map(
|
|
||||||
(rdoc) => [rdoc.pid, { ...problem.default, pid: rdoc.pid }],
|
|
||||||
));
|
|
||||||
// Remove sensitive data
|
// Remove sensitive data
|
||||||
if (!isSelfProfile && sdoc) {
|
if (!isSelfProfile && sdoc) {
|
||||||
sdoc.createIp = '';
|
sdoc.createIp = '';
|
||||||
sdoc.updateIp = '';
|
sdoc.updateIp = '';
|
||||||
sdoc._id = '';
|
sdoc._id = '';
|
||||||
}
|
}
|
||||||
const path = [
|
|
||||||
['Hydro', 'homepage'],
|
|
||||||
['user_detail', 'user_detail', { uid }],
|
|
||||||
];
|
|
||||||
this.response.template = 'user_detail.html';
|
this.response.template = 'user_detail.html';
|
||||||
this.response.body = {
|
this.response.body = { isSelfProfile, udoc, sdoc };
|
||||||
isSelfProfile, udoc, sdoc, rdocs, pdocs, pcount, pdict, path,
|
|
||||||
};
|
|
||||||
this.extraTitleContent = udoc.uname;
|
this.extraTitleContent = udoc.uname;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -343,7 +319,6 @@ export async function apply() {
|
|||||||
Route('user_oauth', '/oauth/:type', OauthHandler);
|
Route('user_oauth', '/oauth/:type', OauthHandler);
|
||||||
Route('user_oauth_callback', '/oauth/:type/callback', OauthCallbackHandler);
|
Route('user_oauth_callback', '/oauth/:type/callback', OauthCallbackHandler);
|
||||||
Route('user_register', '/register', UserRegisterHandler, PRIV.PRIV_REGISTER_USER);
|
Route('user_register', '/register', UserRegisterHandler, PRIV.PRIV_REGISTER_USER);
|
||||||
Route('user_register_without_code', '/register/code', UserRegisterWithCodeHandler, PRIV.PRIV_REGISTER_USER);
|
|
||||||
Route('user_register_with_code', '/register/:code', UserRegisterWithCodeHandler, PRIV.PRIV_REGISTER_USER);
|
Route('user_register_with_code', '/register/:code', UserRegisterWithCodeHandler, PRIV.PRIV_REGISTER_USER);
|
||||||
Route('user_logout', '/logout', UserLogoutHandler, PRIV.PRIV_USER_PROFILE);
|
Route('user_logout', '/logout', UserLogoutHandler, PRIV.PRIV_USER_PROFILE);
|
||||||
Route('user_lostpass', '/lostpass', UserLostPassHandler);
|
Route('user_lostpass', '/lostpass', UserLostPassHandler);
|
||||||
|
|||||||
@@ -219,13 +219,6 @@ class RecordModel {
|
|||||||
}
|
}
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ArgMethod
|
|
||||||
static getByUid(domainId: string, uid: number, limit: number): Promise<RecordDoc[]> {
|
|
||||||
return RecordModel.coll.find({
|
|
||||||
domainId, 'contest.tid': null, hidden: false, uid,
|
|
||||||
}).sort({ _id: -1 }).limit(limit).toArray();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark problem as deleted
|
// Mark problem as deleted
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ import { FLAG_ALERT } from 'vj/constant/message';
|
|||||||
import i18n from 'vj/utils/i18n';
|
import i18n from 'vj/utils/i18n';
|
||||||
import tpl from 'vj/utils/tpl';
|
import tpl from 'vj/utils/tpl';
|
||||||
|
|
||||||
const messagePage = new AutoloadPage('messagePage', () => {
|
const messagePage = new AutoloadPage('messagePage', (pagename) => {
|
||||||
|
if (pagename === 'home_messages') return;
|
||||||
let visible = true;
|
let visible = true;
|
||||||
document.addEventListener('visibilitychange', () => {
|
document.addEventListener('visibilitychange', () => {
|
||||||
const state = document.visibilityState;
|
const state = document.visibilityState;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@hydrooj/ui-default",
|
"name": "@hydrooj/ui-default",
|
||||||
"version": "4.21.9",
|
"version": "4.21.10",
|
||||||
"author": "undefined <i@undefined.moe>",
|
"author": "undefined <i@undefined.moe>",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"main": "hydro.js",
|
"main": "hydro.js",
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { NamedPage } from 'vj/misc/Page';
|
|||||||
import api, { gql } from 'vj/utils/api';
|
import api, { gql } from 'vj/utils/api';
|
||||||
import loadReactRedux from 'vj/utils/loadReactRedux';
|
import loadReactRedux from 'vj/utils/loadReactRedux';
|
||||||
import parseQueryString from 'vj/utils/parseQueryString';
|
import parseQueryString from 'vj/utils/parseQueryString';
|
||||||
|
import VjNotification from 'vj/components/notification';
|
||||||
import { ActionDialog } from 'vj/components/dialog';
|
import { ActionDialog } from 'vj/components/dialog';
|
||||||
import UserSelectAutoComplete from 'vj/components/autocomplete/UserSelectAutoComplete';
|
import UserSelectAutoComplete from 'vj/components/autocomplete/UserSelectAutoComplete';
|
||||||
|
|
||||||
@@ -39,6 +39,13 @@ const page = new NamedPage('home_messages', () => {
|
|||||||
type: 'DIALOGUES_MESSAGE_PUSH',
|
type: 'DIALOGUES_MESSAGE_PUSH',
|
||||||
payload: msg,
|
payload: msg,
|
||||||
});
|
});
|
||||||
|
new VjNotification({
|
||||||
|
title: msg.udoc.uname,
|
||||||
|
avatar: msg.udoc.avatarUrl,
|
||||||
|
message: msg.mdoc.content,
|
||||||
|
duration: 15000,
|
||||||
|
action: () => createDialog(msg.udoc),
|
||||||
|
}).show();
|
||||||
};
|
};
|
||||||
|
|
||||||
const userSelector = UserSelectAutoComplete.getOrConstruct($('.dialog__body--user-select [name="user"]'));
|
const userSelector = UserSelectAutoComplete.getOrConstruct($('.dialog__body--user-select [name="user"]'));
|
||||||
|
|||||||
@@ -46,12 +46,12 @@
|
|||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if udoc['qq'] %}
|
{% if udoc['qq'] %}
|
||||||
<a class="profile-header__contact-item" href="javascript:;" name="profile_contact_copy" data-content="{{ udoc['qq'] | base64_encode }}" data-tooltip="{{ _('Copy QQ Number') }}">
|
<a class="profile-header__contact-item" href="javascript:;" name="profile_qq_copy" data-content="{{ udoc['qq'] | base64_encode }}" data-tooltip="{{ _('Copy QQ Number') }}">
|
||||||
<span class="icon icon-qq"></span>
|
<span class="icon icon-qq"></span>
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if udoc['wechat'] %}
|
{% if udoc['wechat'] %}
|
||||||
<a class="profile-header__contact-item" href="javascript:;" name="profile_contact_copy" data-content="{{ udoc['wechat'] | base64_encode }}" data-tooltip="{{ _('Copy WeChat Account') }}">
|
<a class="profile-header__contact-item" href="javascript:;" name="profile_wechat_copy" data-content="{{ udoc['wechat'] | base64_encode }}" data-tooltip="{{ _('Copy WeChat Account') }}">
|
||||||
<span class="icon icon-wechat"></span>
|
<span class="icon icon-wechat"></span>
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -98,65 +98,6 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<li class="section__tab-item">
|
|
||||||
<h1 class="section__tab-title">{{ _('Contributions') }} ({{ pcount }})</h1>
|
|
||||||
<div class="section__tab-main">
|
|
||||||
{% if not pdocs %}
|
|
||||||
{{ nothing.render("Oh, the user doesn't have any contributions!") }}
|
|
||||||
{% else %}
|
|
||||||
<div class="section__header">
|
|
||||||
<h1 class="section__title">{{ _('Contributed Problems') }}</h1>
|
|
||||||
</div>
|
|
||||||
<div class="section__body">
|
|
||||||
{%- for pdoc in pdocs -%}
|
|
||||||
<p><a href="{{ url('problem_detail', pid=pdoc.pid|default(pdoc.docId)) }}">{{ pdoc['title'] }}</a></p>
|
|
||||||
{%- endfor -%}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
<li class="section__tab-item">
|
|
||||||
<h1 class="section__tab-title">{{ _('Submissions') }} ({{ udoc['nSubmit']|default(0) }})</h1>
|
|
||||||
<div class="section__tab-main">
|
|
||||||
{% if not rdocs %}
|
|
||||||
{{ nothing.render("Oh, the user hasn't submitted yet!") }}
|
|
||||||
{% else %}
|
|
||||||
<div class="section__header">
|
|
||||||
<h1 class="section__title">{{ _('Recent Submissions') }}</h1>
|
|
||||||
</div>
|
|
||||||
<div class="section__body no-padding">
|
|
||||||
<table class="data-table record_main__table">
|
|
||||||
<colgroup>
|
|
||||||
<col class="col--status">
|
|
||||||
<col class="col--problem">
|
|
||||||
<col class="col--submit-by">
|
|
||||||
<col class="col--time">
|
|
||||||
<col class="col--memory">
|
|
||||||
<col class="col--lang">
|
|
||||||
<col class="col--submit-at">
|
|
||||||
</colgroup>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th class="col--status record-status--border">{{ _('Status') }}</th>
|
|
||||||
<th class="col--problem">{{ _('Problem') }}</th>
|
|
||||||
<th class="col--submit-by">{{ _('Submit By') }}</th>
|
|
||||||
<th class="col--time">{{ _('Time') }}</th>
|
|
||||||
<th class="col--memory">{{ _('Memory') }}</th>
|
|
||||||
<th class="col--lang">{{ _('Language') }}</th>
|
|
||||||
<th class="col--submit-at">{{ _('Submit At') }}</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{%- for rdoc in rdocs -%}
|
|
||||||
{% set pdoc = pdict[rdoc.pid] %}
|
|
||||||
{% include 'record_main_tr.html' %}
|
|
||||||
{%- endfor -%}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -174,6 +115,12 @@
|
|||||||
<div class="section side">
|
<div class="section side">
|
||||||
<div class="section__body">
|
<div class="section__body">
|
||||||
<div class="balancer sidebar-user-stat">
|
<div class="balancer sidebar-user-stat">
|
||||||
|
<div class="balancer__body">
|
||||||
|
<div class="numbox">
|
||||||
|
<div class="numbox__num medium">{{ udoc.nSubmit or 0 }}</div>
|
||||||
|
<div class="numbox__text">{{ _('Submitted') }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="balancer__body">
|
<div class="balancer__body">
|
||||||
<div class="numbox">
|
<div class="numbox">
|
||||||
<div class="numbox__num medium">{{ udoc.nAccept or 0 }}</div>
|
<div class="numbox__num medium">{{ udoc.nAccept or 0 }}</div>
|
||||||
@@ -186,12 +133,6 @@
|
|||||||
<div class="numbox__text">{{ _('Solutions Liked') }}</div>
|
<div class="numbox__text">{{ _('Solutions Liked') }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="balancer__body">
|
|
||||||
<div class="numbox">
|
|
||||||
<div class="numbox__num medium">{{ udoc.nProblems or 0 }}</div>
|
|
||||||
<div class="numbox__text">{{ _('Upload Problem') }}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in new issue
Block a user