diff --git a/ui/artalk-sidebar/src/App.vue b/ui/artalk-sidebar/src/App.vue index 575436cd..38c30f13 100644 --- a/ui/artalk-sidebar/src/App.vue +++ b/ui/artalk-sidebar/src/App.vue @@ -67,7 +67,7 @@ function syncArtalk(artalk: Artalk) { .getApi() .user.getUserStatus({ email: artalkUserData.email, - name: artalkUserData.nick, + name: artalkUserData.name, }) .then((res) => { if (res.data.is_admin && !res.data.is_login) { @@ -76,7 +76,7 @@ function syncArtalk(artalk: Artalk) { // 将全部通知标记为已读 artalk.ctx.getApi().notifies.markAllNotifyRead({ email: artalkUserData.email, - name: artalkUserData.nick, + name: artalkUserData.name, }) } }) diff --git a/ui/artalk-sidebar/src/components/AppHeader.vue b/ui/artalk-sidebar/src/components/AppHeader.vue index 20b8c1ed..ffa98a31 100644 --- a/ui/artalk-sidebar/src/components/AppHeader.vue +++ b/ui/artalk-sidebar/src/components/AppHeader.vue @@ -8,7 +8,7 @@ const nav = useNavStore() const router = useRouter() const user = useUserStore() const { t } = useI18n() -const { site: curtSite, isAdmin, avatar } = storeToRefs(user) +const { site: curtSite, is_admin: isAdmin, avatar } = storeToRefs(user) const avatarClickHandler = () => { if (!isOpenFromSidebar()) logout() diff --git a/ui/artalk-sidebar/src/components/AppNavigationMenu.ts b/ui/artalk-sidebar/src/components/AppNavigationMenu.ts index d26417f8..f9760ed1 100644 --- a/ui/artalk-sidebar/src/components/AppNavigationMenu.ts +++ b/ui/artalk-sidebar/src/components/AppNavigationMenu.ts @@ -79,7 +79,7 @@ export interface SearchStateApi { */ export const useNavigationMenu = (props: NavigationStoreProps = {}) => { const router = useRouter() - const { isAdmin } = storeToRefs(useUserStore()) + const { is_admin: isAdmin } = storeToRefs(useUserStore()) const { tabs, curtPage, curtTab, isMobile, isSearchEnabled } = storeToRefs(useNavStore()) /** diff --git a/ui/artalk-sidebar/src/global.ts b/ui/artalk-sidebar/src/global.ts index f501db45..312b2a54 100644 --- a/ui/artalk-sidebar/src/global.ts +++ b/ui/artalk-sidebar/src/global.ts @@ -1,5 +1,5 @@ import Artalk from 'artalk' -import type { ArtalkType } from 'artalk' +import type { LocalUser } from 'artalk' export let artalk: Artalk | null = null @@ -24,10 +24,19 @@ function getBootParams() { window.history.replaceState({}, '', window.location.pathname) } + const userFromURL = JSON.parse(p.get('user') || '{}') + const user: LocalUser = { + name: userFromURL.name || '', + email: userFromURL.email || '', + link: userFromURL.link || '', + token: userFromURL.token || '', + is_admin: userFromURL.is_admin || false, + } + return { + user, pageKey: p.get('pageKey') || '', site: p.get('site') || '', - user: <ArtalkType.LocalUser>JSON.parse(p.get('user') || '{}'), view: p.get('view') || '', viewParams: <any>null, darkMode: p.get('darkMode') === '1', diff --git a/ui/artalk-sidebar/src/pages/comments.vue b/ui/artalk-sidebar/src/pages/comments.vue index 60d764b9..ea9f19da 100644 --- a/ui/artalk-sidebar/src/pages/comments.vue +++ b/ui/artalk-sidebar/src/pages/comments.vue @@ -15,7 +15,7 @@ const search = ref('') onMounted(() => { // 初始化导航条 - if (user.isAdmin) { + if (user.is_admin) { nav.updateTabs( { all: 'all', @@ -55,7 +55,7 @@ onMounted(() => { listFetchParamsModifier: (params) => { params.site_name = curtSite.value // 站点名 - let scope = user.isAdmin ? 'site' : 'user' + let scope = user.is_admin ? 'site' : 'user' let type = curtTab.value if (curtTab.value === 'personal_all') { diff --git a/ui/artalk-sidebar/src/pages/login.vue b/ui/artalk-sidebar/src/pages/login.vue index d4c1dadf..5b08c48c 100644 --- a/ui/artalk-sidebar/src/pages/login.vue +++ b/ui/artalk-sidebar/src/pages/login.vue @@ -51,12 +51,8 @@ function login(username?: string) { password: userForm.value.password, }) .then((res) => { - const user = res.data.user artalk.ctx.get('user').update({ - nick: user.name, - email: user.email, - link: user.link, - isAdmin: user.is_admin, + ...res.data.user, token: res.data.token, }) useUserStore().sync() diff --git a/ui/artalk-sidebar/src/stores/user.ts b/ui/artalk-sidebar/src/stores/user.ts index e18303e1..65c5aed5 100644 --- a/ui/artalk-sidebar/src/stores/user.ts +++ b/ui/artalk-sidebar/src/stores/user.ts @@ -1,42 +1,36 @@ +import type { LocalUser } from 'artalk' import { defineStore } from 'pinia' import sha256 from 'crypto-js/sha256' import md5 from 'crypto-js/md5' import { bootParams, getArtalk } from '../global' +interface UserState extends LocalUser { + /** + * Current site name + */ + site: string +} + export const useUserStore = defineStore('user', { - state: () => ({ - site: bootParams.site || '', - name: bootParams.user.nick || '', - email: bootParams.user.email || '', - isAdmin: bootParams.user.isAdmin || false, - token: bootParams.user.token || '', - }), + state: () => + <UserState>{ + site: bootParams.site || '', + ...bootParams.user, + }, actions: { logout() { - this.site = '' - this.name = '' - this.email = '' - this.isAdmin = false - this.token = '' - + this.$reset() getArtalk()?.ctx.get('user').logout() }, sync() { const user = getArtalk()?.ctx.get('user') if (!user) throw new Error('Artalk is not initialized') if (!user.checkHasBasicUserInfo()) throw new Error('User is not logged in') - const userData = user.getData() - this.site = '' - this.name = userData.nick - this.email = userData.email - this.isAdmin = userData.isAdmin - this.token = userData.token + this.$patch({ ...user.getData(), site: '' }) }, }, getters: { - avatar: (state) => { - return getGravatar(state.email) - }, + avatar: (state) => getGravatar(state.email), }, }) diff --git a/ui/artalk/src/components/checker/admin.ts b/ui/artalk/src/components/checker/admin.ts index bc26e7ef..954bef55 100644 --- a/ui/artalk/src/components/checker/admin.ts +++ b/ui/artalk/src/components/checker/admin.ts @@ -8,7 +8,7 @@ const AdminChecker: Checker<{ token: string }> = { async request(checker, inputVal) { return ( await checker.getApi().user.login({ - name: checker.getUser().getData().nick, + name: checker.getUser().getData().name, email: checker.getUser().getData().email, password: inputVal, }) @@ -21,7 +21,7 @@ const AdminChecker: Checker<{ token: string }> = { onSuccess(checker, res, inputVal, formEl) { checker.getUser().update({ - isAdmin: true, + is_admin: true, token: res.token, }) checker.getOpts().onReload() diff --git a/ui/artalk/src/config.ts b/ui/artalk/src/config.ts index dc69f215..62de44a3 100644 --- a/ui/artalk/src/config.ts +++ b/ui/artalk/src/config.ts @@ -105,7 +105,7 @@ export function convertApiOptions(conf: Partial<ArtalkConfig>, ctx?: ContextApi) getApiToken: () => ctx?.get('user').getData().token, userInfo: ctx?.get('user').checkHasBasicUserInfo() ? { - name: ctx?.get('user').getData().nick, + name: ctx?.get('user').getData().name, email: ctx?.get('user').getData().email, } : undefined, diff --git a/ui/artalk/src/editor/editor.html b/ui/artalk/src/editor/editor.html index 9f767418..9d6a4215 100644 --- a/ui/artalk/src/editor/editor.html +++ b/ui/artalk/src/editor/editor.html @@ -1,6 +1,6 @@ <div class="atk-main-editor"> <div class="atk-header"> - <input name="nick" class="atk-nick" type="text" required="required" /> + <input name="name" class="atk-name" type="text" required="required" /> <input name="email" class="atk-email" type="email" required="required" /> <input name="link" class="atk-link" type="url" /> </div> diff --git a/ui/artalk/src/editor/editor.ts b/ui/artalk/src/editor/editor.ts index e7b4e0de..6a3678d7 100644 --- a/ui/artalk/src/editor/editor.ts +++ b/ui/artalk/src/editor/editor.ts @@ -31,7 +31,7 @@ class Editor extends Component implements EditorApi { } getHeaderInputEls() { - return { nick: this.ui.$nick, email: this.ui.$email, link: this.ui.$link } + return { name: this.ui.$name, email: this.ui.$email, link: this.ui.$link } } getContentFinal() { diff --git a/ui/artalk/src/editor/ui.ts b/ui/artalk/src/editor/ui.ts index c54b09a3..8cfb34c0 100644 --- a/ui/artalk/src/editor/ui.ts +++ b/ui/artalk/src/editor/ui.ts @@ -3,7 +3,7 @@ import EditorHTML from './editor.html?raw' const Sel = { $header: '.atk-header', - $nick: '.atk-header [name="nick"]', + $name: '.atk-header [name="name"]', $email: '.atk-header [name="email"]', $link: '.atk-header [name="link"]', $textareaWrap: '.atk-textarea-wrap', @@ -19,7 +19,7 @@ const Sel = { export interface EditorUI extends Record<keyof typeof Sel, HTMLElement> { $el: HTMLElement - $nick: HTMLInputElement + $name: HTMLInputElement $email: HTMLInputElement $link: HTMLInputElement $textarea: HTMLTextAreaElement diff --git a/ui/artalk/src/lib/user.ts b/ui/artalk/src/lib/user.ts index fec4f76c..8d0de67a 100644 --- a/ui/artalk/src/lib/user.ts +++ b/ui/artalk/src/lib/user.ts @@ -15,11 +15,11 @@ class User { // Initialize this.data = { - nick: localUser.nick || '', + name: localUser.name || localUser.nick || '', // nick is deprecated (for historical compatibility) email: localUser.email || '', link: localUser.link || '', token: localUser.token || '', - isAdmin: localUser.isAdmin || false, + is_admin: localUser.is_admin || localUser.isAdmin || false, } } @@ -40,18 +40,18 @@ class User { /** * Logout * - * @description Logout will clear login status, but not clear user data (nick, email, link) + * @description Logout will clear login status, but not clear user data (name, email, link) */ logout() { this.update({ token: '', - isAdmin: false, + is_admin: false, }) } /** Check if user has filled basic data */ checkHasBasicUserInfo() { - return !!this.data.nick && !!this.data.email + return !!this.data.name && !!this.data.email } } diff --git a/ui/artalk/src/load.ts b/ui/artalk/src/load.ts index 878c1e20..1fb43a8d 100644 --- a/ui/artalk/src/load.ts +++ b/ui/artalk/src/load.ts @@ -138,7 +138,7 @@ export function onLoadErr(ctx: ContextApi, err: any) { errMsg: err.msg || String(err), errData: err.data, retryFn: () => load(ctx), - onOpenSidebar: ctx.get('user').getData().isAdmin + onOpenSidebar: ctx.get('user').getData().is_admin ? () => ctx.showSidebar({ view: sidebarOpenView as any, diff --git a/ui/artalk/src/plugins/admin-only-elem.ts b/ui/artalk/src/plugins/admin-only-elem.ts index d9ed6b31..7f7b3c1b 100644 --- a/ui/artalk/src/plugins/admin-only-elem.ts +++ b/ui/artalk/src/plugins/admin-only-elem.ts @@ -3,7 +3,7 @@ import type { ArtalkPlugin } from '@/types' export const AdminOnlyElem: ArtalkPlugin = (ctx) => { const scanApply = () => { applyAdminOnlyEls( - ctx.get('user').getData().isAdmin, + ctx.get('user').getData().is_admin, getAdminOnlyEls({ $root: ctx.$root, }), diff --git a/ui/artalk/src/plugins/editor/closable.ts b/ui/artalk/src/plugins/editor/closable.ts index c5f2b98b..58ac8959 100644 --- a/ui/artalk/src/plugins/editor/closable.ts +++ b/ui/artalk/src/plugins/editor/closable.ts @@ -34,7 +34,7 @@ export default class Closable extends EditorPlug { Utils.createElement(`<div class="atk-comment-closed">${$t('onlyAdminCanReply')}</div>`), ) - if (!this.kit.useUser().getData().isAdmin) { + if (!this.kit.useUser().getData().is_admin) { this.kit.useUI().$textarea.style.display = 'none' this.kit.useEvents().trigger('panel-close') this.kit.useUI().$bottom.style.display = 'none' diff --git a/ui/artalk/src/plugins/editor/header-user.ts b/ui/artalk/src/plugins/editor/header-user.ts index a6ba2e45..1eb5113c 100644 --- a/ui/artalk/src/plugins/editor/header-user.ts +++ b/ui/artalk/src/plugins/editor/header-user.ts @@ -14,13 +14,19 @@ export default class HeaderUser extends EditorPlug { this.kit.useUser().update({ [field]: $input.value.trim() }) // remote fetch user info - if (field === 'nick' || field === 'email') this.fetchUserInfo() // must after update user data, since fetchUserInfo() will use User.data + if (field === 'name' || field === 'email') this.fetchUserInfo() // must after update user data, since fetchUserInfo() will use User.data + } + + const placeholders = { + name: $t('nick'), + email: $t('email'), + link: $t('link'), } this.kit.useMounted(() => { Object.entries(this.kit.useEditor().getHeaderInputEls()).forEach(([key, $input]) => { // set placeholder - $input.placeholder = `${$t(key as any)}` + $input.placeholder = placeholders[key] // sync header values from User.data $input.value = this.kit.useUser().getData()[key] || '' diff --git a/ui/artalk/src/plugins/editor/state-edit.ts b/ui/artalk/src/plugins/editor/state-edit.ts index 68279216..834d69d7 100644 --- a/ui/artalk/src/plugins/editor/state-edit.ts +++ b/ui/artalk/src/plugins/editor/state-edit.ts @@ -30,7 +30,7 @@ export default class StateEdit extends EditorPlug { req: async () => { const saveData = { content: this.kit.useEditor().getContentFinal(), - nick: this.kit.useUI().$nick.value, + nick: this.kit.useUI().$name.value, email: this.kit.useUI().$email.value, link: this.kit.useUI().$link.value, } @@ -69,7 +69,7 @@ export default class StateEdit extends EditorPlug { ui.$header.style.display = 'none' // TODO: support modify header information - ui.$nick.value = comment.nick || '' + ui.$name.value = comment.nick || '' ui.$email.value = comment.email || '' ui.$link.value = comment.link || '' @@ -91,8 +91,8 @@ export default class StateEdit extends EditorPlug { this.comment = undefined - const { nick, email, link } = this.kit.useUser().getData() - ui.$nick.value = nick + const { name, email, link } = this.kit.useUser().getData() + ui.$name.value = name ui.$email.value = email ui.$link.value = link diff --git a/ui/artalk/src/plugins/editor/submit-add.ts b/ui/artalk/src/plugins/editor/submit-add.ts index 0f3322df..62ca0d62 100644 --- a/ui/artalk/src/plugins/editor/submit-add.ts +++ b/ui/artalk/src/plugins/editor/submit-add.ts @@ -15,12 +15,12 @@ export default class SubmitAddPreset { } async getSubmitAddParams() { - const { nick, email, link } = this.kit.useUser().getData() + const { name, email, link } = this.kit.useUser().getData() const conf = this.kit.useConf() return { content: this.kit.useEditor().getContentFinal(), - name: nick, + name, email, link, rid: 0, diff --git a/ui/artalk/src/plugins/list/sidebar-btn.ts b/ui/artalk/src/plugins/list/sidebar-btn.ts index 139ab75f..2f9c82e5 100644 --- a/ui/artalk/src/plugins/list/sidebar-btn.ts +++ b/ui/artalk/src/plugins/list/sidebar-btn.ts @@ -9,12 +9,12 @@ export const SidebarBtn: ArtalkPlugin = (ctx) => { const user = ctx.get('user').getData() // 已输入个人信息 - if (!!user.nick && !!user.email) { + if (!!user.name && !!user.email) { $openSidebarBtn.classList.remove('atk-hide') // update button text (normal user or admin) const $btnText = $openSidebarBtn.querySelector<HTMLElement>('.atk-text') - if ($btnText) $btnText.innerText = !user.isAdmin ? $t('msgCenter') : $t('ctrlCenter') + if ($btnText) $btnText.innerText = !user.is_admin ? $t('msgCenter') : $t('ctrlCenter') } else { $openSidebarBtn.classList.add('atk-hide') } diff --git a/ui/artalk/src/types/config.ts b/ui/artalk/src/types/config.ts index 2ba54d64..74c72bd3 100644 --- a/ui/artalk/src/types/config.ts +++ b/ui/artalk/src/types/config.ts @@ -155,22 +155,23 @@ export interface ArtalkConfig { } /** - * 本地持久化用户数据 - * @note 始终保持一层结构,不支持多层结构 + * Local User Data (in localStorage) + * + * @note Keep flat for easy handling */ export interface LocalUser { - /** 昵称 */ - nick: string + /** Username (aka. Nickname) */ + name: string - /** 邮箱 */ + /** Email */ email: string - /** 链接 */ + /** Link (aka. Website) */ link: string - /** TOKEN */ + /** Token (for authorization) */ token: string - /** 是否为管理员 */ - isAdmin: boolean + /** Admin flag */ + is_admin: boolean } diff --git a/ui/plugin-auth/EditorUser.tsx b/ui/plugin-auth/EditorUser.tsx index be6bd94b..59260917 100644 --- a/ui/plugin-auth/EditorUser.tsx +++ b/ui/plugin-auth/EditorUser.tsx @@ -7,10 +7,10 @@ const EditorUser = ({ ctx }: { ctx: ContextApi }) => { window.confirm(ctx.$t('logoutConfirm')) && ctx.get('user').update({ token: '', - nick: '', + name: '', email: '', link: '', - isAdmin: false, + is_admin: false, }) } @@ -33,7 +33,7 @@ const EditorUser = ({ ctx }: { ctx: ContextApi }) => { <div class="atk-editor-user"> <div class="atk-user-profile-btn atk-user-btn"> {/* <div class="atk-avatar" style={{ "background-image": `url('https://avatars.githubusercontent.com/u/76841221?s=200&v=4')` }}></div> */} - <div class="atk-name">{user().nick}</div> + <div class="atk-name">{user().name}</div> </div> <div class="atk-logout atk-user-btn" onClick={logoutHandler} aria-label="Logout"> <svg diff --git a/ui/plugin-auth/lib/token-login.ts b/ui/plugin-auth/lib/token-login.ts index d05280c7..d7925c90 100644 --- a/ui/plugin-auth/lib/token-login.ts +++ b/ui/plugin-auth/lib/token-login.ts @@ -1,22 +1,13 @@ -import type { ContextApi } from 'artalk' +import type { ContextApi, LocalUser } from 'artalk' interface ResponseLoginData { - user: { - name: string - email: string - link: string - is_admin: boolean - } + user: LocalUser token: string } -export const loginByApiRes = (ctx: ContextApi, data: ResponseLoginData) => { - const { user, token } = data +export const loginByApiRes = (ctx: ContextApi, { user, token }: ResponseLoginData) => { ctx.get('user').update({ - nick: user.name, - email: user.email, - link: user.link, - isAdmin: user.is_admin, + ...user, token, }) } @@ -29,10 +20,10 @@ export const loginByToken = (ctx: ContextApi, token: string) => { .then((res) => { const { user } = res.data ctx.get('user').update({ - nick: user.name, + name: user.name, email: user.email, link: user.link, - isAdmin: user.is_admin, + is_admin: user.is_admin, }) }) } diff --git a/ui/plugin-auth/main.tsx b/ui/plugin-auth/main.tsx index 067f0496..ce11bd46 100644 --- a/ui/plugin-auth/main.tsx +++ b/ui/plugin-auth/main.tsx @@ -30,7 +30,7 @@ export const ArtalkAuthPlugin: ArtalkPlugin = (ctx) => { const onSkip = () => { ctx.get('editor').getUI().$header.style.display = '' - ctx.get('editor').getUI().$nick.focus() + ctx.get('editor').getUI().$name.focus() ctx.updateConf({ beforeSubmit: undefined, })