From 96724361514f29cc5c2d26514930c5a3ddd7f974 Mon Sep 17 00:00:00 2001 From: undefined Date: Sat, 7 Jan 2023 22:19:10 +0800 Subject: [PATCH] ui: avatar uploader --- packages/hydrooj/src/handler/home.ts | 20 ++++++ packages/ui-default/locales/en.yaml | 1 + packages/ui-default/locales/zh.yaml | 1 + .../ui-default/pages/home_settings.page.ts | 69 +++++++++++++++++++ .../ui-default/templates/home_settings.html | 15 ++++ 5 files changed, 106 insertions(+) create mode 100644 packages/ui-default/pages/home_settings.page.ts diff --git a/packages/hydrooj/src/handler/home.ts b/packages/hydrooj/src/handler/home.ts index 74eef13c7..fea6b8cf2 100644 --- a/packages/hydrooj/src/handler/home.ts +++ b/packages/hydrooj/src/handler/home.ts @@ -1,3 +1,4 @@ +import path from 'path'; import yaml from 'js-yaml'; import { ObjectID } from 'mongodb'; import { camelCase } from '@hydrooj/utils/lib/utils'; @@ -20,6 +21,7 @@ import domain from '../model/domain'; import message from '../model/message'; import ProblemModel from '../model/problem'; import * as setting from '../model/setting'; +import storage from '../model/storage'; import * as system from '../model/system'; import token from '../model/token'; import * as training from '../model/training'; @@ -288,6 +290,23 @@ class HomeSettingsHandler extends Handler { } } +class HomeAvatarHandler extends Handler { + @param('avatar', Types.String, true) + async post(domainId: string, input: string) { + if (input) await user.setById(this.user._id, { avatar: input }); + else if (this.request.files.file) { + const file = this.request.files.file; + if (file.size > 8 * 1024 * 1024) throw new ValidationError('file'); + const ext = path.extname(file.originalFilename); + if (!['.jpg', '.jpeg', '.png'].includes(ext)) throw new ValidationError('file'); + await storage.put(`user/${this.user._id}/.avatar${ext}`, file.filepath, this.user._id); + // TODO: cached avatar + await user.setById(this.user._id, { avatar: `url:/file/${this.user._id}/.avatar${ext}` }); + } else throw new ValidationError('avatar'); + this.back(); + } +} + class UserChangemailWithCodeHandler extends Handler { @param('code', Types.String) async get(domainId: string, code: string) { @@ -444,6 +463,7 @@ export async function apply(ctx: Context) { ctx.Route('home_security', '/home/security', HomeSecurityHandler, PRIV.PRIV_USER_PROFILE); ctx.Route('user_changemail_with_code', '/home/changeMail/:code', UserChangemailWithCodeHandler, PRIV.PRIV_USER_PROFILE); ctx.Route('home_settings', '/home/settings/:category', HomeSettingsHandler, PRIV.PRIV_USER_PROFILE); + ctx.Route('home_avatar', '/home/avatar', HomeAvatarHandler, PRIV.PRIV_USER_PROFILE); ctx.Route('home_domain', '/home/domain', HomeDomainHandler, PRIV.PRIV_USER_PROFILE); ctx.Route('home_domain_create', '/home/domain/create', HomeDomainCreateHandler, PRIV.PRIV_CREATE_DOMAIN); if (system.get('server.message')) ctx.Route('home_messages', '/home/messages', HomeMessagesHandler, PRIV.PRIV_USER_PROFILE); diff --git a/packages/ui-default/locales/en.yaml b/packages/ui-default/locales/en.yaml index f7c73a83f..f5cb64e69 100644 --- a/packages/ui-default/locales/en.yaml +++ b/packages/ui-default/locales/en.yaml @@ -63,3 +63,4 @@ user_logout: Logout user_lostpass: Lost Password user_register: Register wiki_help: Help +You can also upload your avatar to Gravatar and it will be automatically updated here.: You can also upload your avatar to Gravatar and it will be automatically updated here. diff --git a/packages/ui-default/locales/zh.yaml b/packages/ui-default/locales/zh.yaml index 89ed446ee..7a1078569 100644 --- a/packages/ui-default/locales/zh.yaml +++ b/packages/ui-default/locales/zh.yaml @@ -960,6 +960,7 @@ Write Your Comment: 发表您的评论 Write Your Solution: 发表您的题解 Year: 年 You are not allowed to join the domain. The link is either invalid or expired.: 您无法加入该域,链接无效或已过期。 +You can also upload your avatar to Gravatar and it will be automatically updated here.: 你也可以将头像上传到 Gravatar,本站的头像将自动更新。 You can create your own training plans and share with others.: 您可以创建您自己的训练计划并且与他人分享。 You cannot submit for this problem because the contest is ended. You can click "Open in Problem Set" to view this problem in normal mode.: 该比赛已结束,您无法在比赛模式下递交该题目。您可以点击“在题库中打开”以普通模式查看和递交本题。 You cannot submit for this problem because the homework's deadline is due.: 作业已超过截止时间,您无法递交本题目。 diff --git a/packages/ui-default/pages/home_settings.page.ts b/packages/ui-default/pages/home_settings.page.ts new file mode 100644 index 000000000..d0069c986 --- /dev/null +++ b/packages/ui-default/pages/home_settings.page.ts @@ -0,0 +1,69 @@ +import $ from 'jquery'; +import Notification from 'vj/components/notification'; +import { NamedPage } from 'vj/misc/Page'; +import { + delay, i18n, request, tpl, +} from 'vj/utils'; + +export default new NamedPage('home_account', () => { + document.getElementsByName('avatar')[0].parentNode.parentNode.parentElement.remove(); + const $type = $(tpl` + + `); + const $text = $(tpl``); + const $layout = $(` +
+ ${[['type', 3], ['text', 6], ['btn', 3]].map(([i, w]) => ` +
+
+
+ `).join(' ')} +
+ `); + const $file: JQuery = $(''); + const $confirm = $(tpl``); + $('button.change-avatar').on('click', () => { + $('.changeAvatar').append($layout); + $('[name="form_item_type"]').append($type); + $('[name="form_item_text"]').append($text); + $('[name="form_item_text"]').append($file); + $('[name="form_item_btn"]').append($confirm); + $('button.change-avatar').hide(); + $type.trigger('change'); + }); + $type.on('change', () => { + if ($type.val() === 'upload') { + $text.hide(); + $file.show(); + } else { + $text.show(); + $file.hide(); + const placeholder = $type.val() === 'gravatar' + ? 'Email address' + : $type.val() === 'github' + ? 'Github username' + : 'QQ ID'; + $text.attr('placeholder', i18n(placeholder)); + } + }); + $confirm.on('click', async () => { + if ($type.val() === 'upload') { + const formData = new FormData(); + formData.append('file', $file[0].files[0]); + await request.postFile('/home/avatar', formData); + Notification.success(i18n('Upload success.')); + } else { + await request.post('/home/avatar', { + avatar: `${$type.val()}:${$text.val()}`, + }); + Notification.success(i18n('Updated.')); + } + await delay(800); + window.location.reload(); + }); +}); diff --git a/packages/ui-default/templates/home_settings.html b/packages/ui-default/templates/home_settings.html index 7cffae76f..d468ae6e0 100644 --- a/packages/ui-default/templates/home_settings.html +++ b/packages/ui-default/templates/home_settings.html @@ -1,5 +1,20 @@ {% extends "layout/home_base.html" %} {% block home_content %} +{% if category == "account" %} +
+
+

{{ _('Avatar') }}

+
+
+

{{ _('You can also upload your avatar to Gravatar and it will be automatically updated here.')|safe }}

+
+
+ {{ current.uname }} +
+ +
+
+{% endif %}
{% include "partials/setting.html" %}