From ce32cd576be1496eba4dc2eec804e1059d99457d Mon Sep 17 00:00:00 2001 From: Johann150 <johann.galle@protonmail.com> Date: Thu, 23 Sep 2021 15:32:16 +0200 Subject: [PATCH 001/107] fix inboxQueue import (#7829) --- src/server/api/endpoints/admin/queue/inbox-delayed.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/api/endpoints/admin/queue/inbox-delayed.ts b/src/server/api/endpoints/admin/queue/inbox-delayed.ts index 59e5c834ed..1925906c28 100644 --- a/src/server/api/endpoints/admin/queue/inbox-delayed.ts +++ b/src/server/api/endpoints/admin/queue/inbox-delayed.ts @@ -1,6 +1,6 @@ import { URL } from 'url'; import define from '../../../define'; -import { inboxQueue } from '@/queue/index'; +import { inboxQueue } from '@/queue/queues'; export const meta = { tags: ['admin'], From 8a558eed362bfdaee5453191616326579a610c29 Mon Sep 17 00:00:00 2001 From: MeiMei <30769358+mei23@users.noreply.github.com> Date: Thu, 23 Sep 2021 22:41:02 +0900 Subject: [PATCH 002/107] Update index.ts (#7830) --- src/queue/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/queue/index.ts b/src/queue/index.ts index 0ce10a4c60..c787253ded 100644 --- a/src/queue/index.ts +++ b/src/queue/index.ts @@ -173,7 +173,7 @@ export function createImportUserListsJob(user: ThinUser, fileId: DriveFile['id'] }); } -export function createDeleteAccountJob(user: ThinUser, opts: { soft?: boolean; }) { +export function createDeleteAccountJob(user: ThinUser, opts: { soft?: boolean; } = {}) { return dbQueue.add('deleteAccount', { user: user, soft: opts.soft From ce4ea5071f58a3d9af7624d02a36f97200186431 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Thu, 23 Sep 2021 23:01:32 +0900 Subject: [PATCH 003/107] =?UTF-8?q?enhance(client):=20=E3=82=A2=E3=83=8B?= =?UTF-8?q?=E3=83=A1=E3=83=BC=E3=82=B7=E3=83=A7=E3=83=B3=E3=82=92=E6=B8=9B?= =?UTF-8?q?=E3=82=89=E3=81=99=E8=A8=AD=E5=AE=9A=E3=82=92=E3=83=A1=E3=83=8B?= =?UTF-8?q?=E3=83=A5=E3=83=BC=E3=81=AE=E3=82=A2=E3=83=8B=E3=83=A1=E3=83=BC?= =?UTF-8?q?=E3=82=B7=E3=83=A7=E3=83=B3=E3=81=AB=E3=82=82=E9=81=A9=E7=94=A8?= =?UTF-8?q?=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolve #7826 --- CHANGELOG.md | 7 +++++++ src/client/directives/click-anime.ts | 3 +++ 2 files changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f8331854c8..d515adf2d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,13 @@ --> +## 12.x.x (unreleased) + +### Improvements +- アニメーションを減らす設定をメニューのアニメーションにも適用するように + +### Bugfixes + ## 12.91.0 (2021/09/22) ### Improvements diff --git a/src/client/directives/click-anime.ts b/src/client/directives/click-anime.ts index 9fd583d6dd..0d5a6da94e 100644 --- a/src/client/directives/click-anime.ts +++ b/src/client/directives/click-anime.ts @@ -1,7 +1,10 @@ import { Directive } from 'vue'; +import { defaultStore } from '@client/store'; export default { mounted(el, binding, vn) { + if (!defaultStore.state.animation) return; + el.classList.add('_anime_bounce_standBy'); el.addEventListener('mousedown', () => { From 76cdbe74ba15358422293102a9c9cf240d83932b Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 24 Sep 2021 22:28:17 +0900 Subject: [PATCH 004/107] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d515adf2d8..7d412fb7d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ - アニメーションを減らす設定をメニューのアニメーションにも適用するように ### Bugfixes +- Fix createDeleteAccountJob +- admin inbox queue does not show individual jobs ## 12.91.0 (2021/09/22) From ac93af8eb5c0c3c566f4bf9630027d07222736fe Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 25 Sep 2021 10:53:55 +0900 Subject: [PATCH 005/107] Update extensions.json --- .vscode/extensions.json | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 40b781b552..843b732cd4 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,12 +1,10 @@ { "recommendations": [ - "ducksoupdev.vue2", "editorconfig.editorconfig", "eg2.vscode-npm-script", - "hollowtree.vue-snippets", "ms-vscode.typescript-javascript-grammar", "ms-vscode.vscode-typescript-tslint-plugin", - "octref.vetur", + "johnsoncodehk.volar", "sysoev.language-stylus" ] } From da71d8f4afb83d62891388ab6900c297a60f51cb Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 26 Sep 2021 01:53:56 +0900 Subject: [PATCH 006/107] fix(client): fix tabs of page header behaviour --- CHANGELOG.md | 4 +++- src/client/ui/_common_/header.vue | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d412fb7d1..fcf2f369f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,11 +10,13 @@ ## 12.x.x (unreleased) ### Improvements -- アニメーションを減らす設定をメニューのアニメーションにも適用するように +- クライアント: アニメーションを減らす設定をメニューのアニメーションにも適用するように ### Bugfixes - Fix createDeleteAccountJob - admin inbox queue does not show individual jobs +- クライアント: ヘッダーのタブが折り返される問題を修正 +- クライアント: ヘッダーにタブが表示されている状態でタイトルをクリックしたときにタブ選択が表示されるのを修正 ## 12.91.0 (2021/09/22) diff --git a/src/client/ui/_common_/header.vue b/src/client/ui/_common_/header.vue index 1e0db9a3a1..f21be2f9cd 100644 --- a/src/client/ui/_common_/header.vue +++ b/src/client/ui/_common_/header.vue @@ -141,6 +141,7 @@ export default defineComponent({ showTabsPopup(ev) { if (!this.hasTabs) return; + if (!this.narrow) return; ev.preventDefault(); ev.stopPropagation(); const menu = this.info.tabs.map(tab => ({ @@ -218,6 +219,7 @@ export default defineComponent({ white-space: nowrap; text-align: left; font-weight: bold; + flex-shrink: 0; > .avatar { $size: 32px; @@ -263,6 +265,8 @@ export default defineComponent({ > .tabs { margin-left: 16px; font-size: 0.8em; + overflow: auto; + white-space: nowrap; > .tab { display: inline-block; From ece3ac967d90cb2e9744a2ebd55dcbee90cdb980 Mon Sep 17 00:00:00 2001 From: MeiMei <30769358+mei23@users.noreply.github.com> Date: Sun, 26 Sep 2021 01:57:38 +0900 Subject: [PATCH 007/107] Tune mfmToHtml (#7841) * Tune mfmToHtml * typo * add --- src/mfm/from-html.ts | 124 ++++++++++++++++++++++++++++++++++++++----- test/mfm.ts | 24 +++++++++ 2 files changed, 136 insertions(+), 12 deletions(-) diff --git a/src/mfm/from-html.ts b/src/mfm/from-html.ts index 4c8e2dbec8..14279f3383 100644 --- a/src/mfm/from-html.ts +++ b/src/mfm/from-html.ts @@ -5,7 +5,9 @@ import { URL } from 'url'; const urlRegex = /^https?:\/\/[\w\/:%#@$&?!()\[\]~.,=+\-]+/; const urlRegexFull = /^https?:\/\/[\w\/:%#@$&?!()\[\]~.,=+\-]+$/; -export function fromHtml(html: string, hashtagNames?: string[]): string { +export function fromHtml(html: string, hashtagNames?: string[]): string | null { + if (html == null) return null; + const dom = parse5.parseFragment(html); let text = ''; @@ -19,6 +21,7 @@ export function fromHtml(html: string, hashtagNames?: string[]): string { function getText(node: parse5.Node): string { if (treeAdapter.isTextNode(node)) return node.value; if (!treeAdapter.isElementNode(node)) return ''; + if (node.nodeName === 'br') return '\n'; if (node.childNodes) { return node.childNodes.map(n => getText(n)).join(''); @@ -27,6 +30,14 @@ export function fromHtml(html: string, hashtagNames?: string[]): string { return ''; } + function appendChildren(childNodes: parse5.ChildNode[]): void { + if (childNodes) { + for (const n of childNodes) { + analyze(n); + } + } + } + function analyze(node: parse5.Node) { if (treeAdapter.isTextNode(node)) { text += node.value; @@ -42,6 +53,7 @@ export function fromHtml(html: string, hashtagNames?: string[]): string { break; case 'a': + { const txt = getText(node); const rel = node.attrs.find(x => x.name === 'rel'); const href = node.attrs.find(x => x.name === 'href'); @@ -87,23 +99,111 @@ export function fromHtml(html: string, hashtagNames?: string[]): string { text += generateLink(); } break; + } + + case 'h1': + { + text += '【'; + appendChildren(node.childNodes); + text += '】\n'; + break; + } + + case 'b': + case 'strong': + { + text += '**'; + appendChildren(node.childNodes); + text += '**'; + break; + } + + case 'small': + { + text += '<small>'; + appendChildren(node.childNodes); + text += '</small>'; + break; + } + + case 's': + case 'del': + { + text += '~~'; + appendChildren(node.childNodes); + text += '~~'; + break; + } + + case 'i': + case 'em': + { + text += '<i>'; + appendChildren(node.childNodes); + text += '</i>'; + break; + } + + // block code (<pre><code>) + case 'pre': { + if (node.childNodes.length === 1 && node.childNodes[0].nodeName === 'code') { + text += '```\n'; + text += getText(node.childNodes[0]); + text += '\n```\n'; + } else { + appendChildren(node.childNodes); + } + break; + } + + // inline code (<code>) + case 'code': { + text += '`'; + appendChildren(node.childNodes); + text += '`'; + break; + } + + case 'blockquote': { + const t = getText(node); + if (t) { + text += '> '; + text += t.split('\n').join(`\n> `); + } + break; + } case 'p': + case 'h2': + case 'h3': + case 'h4': + case 'h5': + case 'h6': + { text += '\n\n'; - if (node.childNodes) { - for (const n of node.childNodes) { - analyze(n); - } - } + appendChildren(node.childNodes); break; + } - default: - if (node.childNodes) { - for (const n of node.childNodes) { - analyze(n); - } - } + // other block elements + case 'div': + case 'header': + case 'footer': + case 'article': + case 'li': + case 'dt': + case 'dd': + { + text += '\n'; + appendChildren(node.childNodes); break; + } + + default: // includes inline elements + { + appendChildren(node.childNodes); + break; + } } } } diff --git a/test/mfm.ts b/test/mfm.ts index d9b98cdac3..ecf886ad6c 100644 --- a/test/mfm.ts +++ b/test/mfm.ts @@ -19,6 +19,30 @@ describe('toHtml', () => { }); describe('fromHtml', () => { + it('p', () => { + assert.deepStrictEqual(fromHtml('<p>a</p><p>b</p>'), 'a\n\nb'); + }); + + it('block element', () => { + assert.deepStrictEqual(fromHtml('<div>a</div><div>b</div>'), 'a\nb'); + }); + + it('inline element', () => { + assert.deepStrictEqual(fromHtml('<ul><li>a</li><li>b</li></ul>'), 'a\nb'); + }); + + it('block code', () => { + assert.deepStrictEqual(fromHtml('<pre><code>a\nb</code></pre>'), '```\na\nb\n```'); + }); + + it('inline code', () => { + assert.deepStrictEqual(fromHtml('<code>a</code>'), '`a`'); + }); + + it('quote', () => { + assert.deepStrictEqual(fromHtml('<blockquote>a\nb</blockquote>'), '> a\n> b'); + }); + it('br', () => { assert.deepStrictEqual(fromHtml('<p>abc<br><br/>d</p>'), 'abc\n\nd'); }); From 67e2768c3e7dec14fa12e27b60ba165dc9cb4de0 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 26 Sep 2021 01:58:20 +0900 Subject: [PATCH 008/107] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fcf2f369f5..4a5f497ddb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ ### Improvements - クライアント: アニメーションを減らす設定をメニューのアニメーションにも適用するように +- ActivityPub: HTML -> MFMの変換を強化 ### Bugfixes - Fix createDeleteAccountJob From a75f3fb87c2fdd6d0eb66af4c2ce9939a29c180d Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 26 Sep 2021 02:10:07 +0900 Subject: [PATCH 009/107] refactor: fix types --- src/client/scripts/autocomplete.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/client/scripts/autocomplete.ts b/src/client/scripts/autocomplete.ts index 924d6a62ee..c4bcc4b724 100644 --- a/src/client/scripts/autocomplete.ts +++ b/src/client/scripts/autocomplete.ts @@ -7,9 +7,9 @@ export class Autocomplete { private suggestion: { x: Ref<number>; y: Ref<number>; - q: Ref<string>; + q: Ref<string | null>; close: Function; - }; + } | null; private textarea: any; private vm: any; private currentType: string; @@ -122,7 +122,7 @@ export class Autocomplete { /** * サジェストを提示します。 */ - private async open(type: string, q: string) { + private async open(type: string, q: string | null) { if (type != this.currentType) { this.close(); } From a70dbb7e74356caed2fe03bec05c4e8d5bde4316 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 26 Sep 2021 02:55:11 +0900 Subject: [PATCH 010/107] =?UTF-8?q?feat(client):=20MFM=E9=96=A2=E6=95=B0?= =?UTF-8?q?=E6=A7=8B=E6=96=87=E3=81=AE=E3=82=B5=E3=82=B8=E3=82=A7=E3=82=B9?= =?UTF-8?q?=E3=83=88=E3=82=92=E5=AE=9F=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + src/client/components/autocomplete.vue | 47 ++++++++++++++++---------- src/client/scripts/autocomplete.ts | 29 +++++++++++++++- 3 files changed, 58 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a5f497ddb..9bbc09860e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ ### Improvements - クライアント: アニメーションを減らす設定をメニューのアニメーションにも適用するように +- クライアント: MFM関数構文のサジェストを実装 - ActivityPub: HTML -> MFMの変換を強化 ### Bugfixes diff --git a/src/client/components/autocomplete.vue b/src/client/components/autocomplete.vue index 065ee6de2e..e2c1af3356 100644 --- a/src/client/components/autocomplete.vue +++ b/src/client/components/autocomplete.vue @@ -10,12 +10,12 @@ </li> <li @click="chooseUser()" @keydown="onKeydown" tabindex="-1" class="choose">{{ $ts.selectUser }}</li> </ol> - <ol class="hashtags" ref="suggests" v-if="hashtags.length > 0"> + <ol class="hashtags" ref="suggests" v-else-if="hashtags.length > 0"> <li v-for="hashtag in hashtags" @click="complete(type, hashtag)" @keydown="onKeydown" tabindex="-1"> <span class="name">{{ hashtag }}</span> </li> </ol> - <ol class="emojis" ref="suggests" v-if="emojis.length > 0"> + <ol class="emojis" ref="suggests" v-else-if="emojis.length > 0"> <li v-for="emoji in emojis" @click="complete(type, emoji.emoji)" @keydown="onKeydown" tabindex="-1"> <span class="emoji" v-if="emoji.isCustomEmoji"><img :src="$store.state.disableShowingAnimatedImages ? getStaticImageUrl(emoji.url) : emoji.url" :alt="emoji.emoji"/></span> <span class="emoji" v-else-if="!$store.state.useOsNativeEmojis"><img :src="emoji.url" :alt="emoji.emoji"/></span> @@ -24,6 +24,11 @@ <span class="alias" v-if="emoji.aliasOf">({{ emoji.aliasOf }})</span> </li> </ol> + <ol class="mfmTags" ref="suggests" v-else-if="mfmTags.length > 0"> + <li v-for="tag in mfmTags" @click="complete(type, tag)" @keydown="onKeydown" tabindex="-1"> + <span class="tag">{{ tag }}</span> + </li> + </ol> </div> </template> @@ -106,6 +111,8 @@ emojiDefinitions.sort((a, b) => a.name.length - b.name.length); const emojiDb = markRaw(emojiDefinitions.concat(emjdb)); //#endregion +const MFM_TAGS = ['tada', 'jelly', 'twitch', 'shake', 'spin', 'jump', 'bounce', 'flip', 'x2', 'x3', 'x4', 'font', 'blur', 'rainbow', 'sparkle']; + export default defineComponent({ props: { type: { @@ -137,11 +144,6 @@ export default defineComponent({ type: Number, required: true, }, - - showing: { - type: Boolean, - required: true - }, }, emits: ['done', 'closed'], @@ -154,18 +156,11 @@ export default defineComponent({ hashtags: [], emojis: [], items: [], + mfmTags: [], select: -1, } }, - watch: { - showing() { - if (!this.showing) { - this.$emit('closed'); - } - } - }, - updated() { this.setPosition(); this.items = (this.$refs.suggests as Element | undefined)?.children || []; @@ -236,7 +231,9 @@ export default defineComponent({ } } - if (this.type == 'user') { + console.log(this.type); + + if (this.type === 'user') { if (this.q == null) { this.users = []; this.fetching = false; @@ -262,7 +259,7 @@ export default defineComponent({ sessionStorage.setItem(cacheKey, JSON.stringify(users)); }); } - } else if (this.type == 'hashtag') { + } else if (this.type === 'hashtag') { if (this.q == null || this.q == '') { this.hashtags = JSON.parse(localStorage.getItem('hashtags') || '[]'); this.fetching = false; @@ -286,7 +283,7 @@ export default defineComponent({ }); } } - } else if (this.type == 'emoji') { + } else if (this.type === 'emoji') { if (this.q == null || this.q == '') { // 最近使った絵文字をサジェスト this.emojis = this.$store.state.recentlyUsedEmojis.map(emoji => emojiDb.find(e => e.emoji == emoji)).filter(x => x != null); @@ -314,6 +311,14 @@ export default defineComponent({ } this.emojis = matched; + } else if (this.type === 'mfmTag') { + console.log(this.q); + if (this.q == null || this.q == '') { + this.mfmTags = MFM_TAGS; + return; + } + + this.mfmTags = MFM_TAGS.filter(tag => tag.startsWith(this.q)); } }, @@ -490,5 +495,11 @@ export default defineComponent({ margin: 0 0 0 8px; } } + + > .mfmTags > li { + + .name { + } + } } </style> diff --git a/src/client/scripts/autocomplete.ts b/src/client/scripts/autocomplete.ts index c4bcc4b724..e952ad3907 100644 --- a/src/client/scripts/autocomplete.ts +++ b/src/client/scripts/autocomplete.ts @@ -70,11 +70,13 @@ export class Autocomplete { const mentionIndex = text.lastIndexOf('@'); const hashtagIndex = text.lastIndexOf('#'); const emojiIndex = text.lastIndexOf(':'); + const mfmTagIndex = text.lastIndexOf('$'); const max = Math.max( mentionIndex, hashtagIndex, - emojiIndex); + emojiIndex, + mfmTagIndex); if (max == -1) { this.close(); @@ -83,6 +85,7 @@ export class Autocomplete { const isMention = mentionIndex != -1; const isHashtag = hashtagIndex != -1; + const isMfmTag = mfmTagIndex != -1; const isEmoji = emojiIndex != -1 && text.split(/:[a-z0-9_+\-]+:/).pop()!.includes(':'); let opened = false; @@ -114,6 +117,14 @@ export class Autocomplete { } } + if (isMfmTag && !opened) { + const mfmTag = text.substr(mfmTagIndex + 1); + if (!mfmTag.includes(' ')) { + this.open('mfmTag', mfmTag); + opened = true; + } + } + if (!opened) { this.close(); } @@ -244,6 +255,22 @@ export class Autocomplete { const pos = trimmedBefore.length + value.length; this.textarea.setSelectionRange(pos, pos); }); + } else if (type == 'mfmTag') { + const source = this.text; + + const before = source.substr(0, caret); + const trimmedBefore = before.substring(0, before.lastIndexOf('$')); + const after = source.substr(caret); + + // 挿入 + this.text = `${trimmedBefore}$[${value} ]${after}`; + + // キャレットを戻す + this.vm.$nextTick(() => { + this.textarea.focus(); + const pos = trimmedBefore.length + (value.length + 3); + this.textarea.setSelectionRange(pos, pos); + }); } } } From 5fd549656b14d2ba79dca70bc87325844ed1a27a Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 26 Sep 2021 02:56:02 +0900 Subject: [PATCH 011/107] chore: clean up --- src/client/components/autocomplete.vue | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/client/components/autocomplete.vue b/src/client/components/autocomplete.vue index e2c1af3356..98998293ac 100644 --- a/src/client/components/autocomplete.vue +++ b/src/client/components/autocomplete.vue @@ -231,8 +231,6 @@ export default defineComponent({ } } - console.log(this.type); - if (this.type === 'user') { if (this.q == null) { this.users = []; From 4b8a2d2a6bbdeb50aeb55220cfff37c048c10c74 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 26 Sep 2021 03:16:30 +0900 Subject: [PATCH 012/107] =?UTF-8?q?fix(client):=20=E7=B5=B5=E6=96=87?= =?UTF-8?q?=E5=AD=97=E4=B8=80=E8=A6=A7=E3=83=9A=E3=83=BC=E3=82=B8=E3=81=AE?= =?UTF-8?q?=E3=82=BF=E3=82=B0=E4=B8=80=E8=A6=A7=E3=82=92=E3=81=A8=E3=82=8A?= =?UTF-8?q?=E3=81=82=E3=81=88=E3=81=9A=E7=84=A1=E5=8A=B9=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 重いため --- src/client/pages/emojis.category.vue | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/client/pages/emojis.category.vue b/src/client/pages/emojis.category.vue index 091c3f20a9..69e22361d8 100644 --- a/src/client/pages/emojis.category.vue +++ b/src/client/pages/emojis.category.vue @@ -5,9 +5,11 @@ <template #prefix><i class="fas fa-search"></i></template> </MkInput> + <!-- たくさんあると重い <div class="tags"> <span class="tag _button" v-for="tag in tags" :class="{ active: selectedTags.has(tag) }" @click="toggleTag(tag)">{{ tag }}</span> </div> + --> </div> <MkFolder class="emojis" v-if="searchEmojis"> From 8d93f148beb12b9d740edfd98bdc6778dfc52be4 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 26 Sep 2021 03:17:16 +0900 Subject: [PATCH 013/107] =?UTF-8?q?=E9=87=8D=E3=81=84=E3=81=A8=E3=81=84?= =?UTF-8?q?=E3=81=86=E3=81=8B=E9=82=AA=E9=AD=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/client/pages/emojis.category.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/pages/emojis.category.vue b/src/client/pages/emojis.category.vue index 69e22361d8..7fee792854 100644 --- a/src/client/pages/emojis.category.vue +++ b/src/client/pages/emojis.category.vue @@ -5,7 +5,7 @@ <template #prefix><i class="fas fa-search"></i></template> </MkInput> - <!-- たくさんあると重い + <!-- たくさんあると邪魔 <div class="tags"> <span class="tag _button" v-for="tag in tags" :class="{ active: selectedTags.has(tag) }" @click="toggleTag(tag)">{{ tag }}</span> </div> From 78b400e8b0505ca319c77209f0c6760140622248 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 26 Sep 2021 03:25:00 +0900 Subject: [PATCH 014/107] =?UTF-8?q?fix(client):=20MFM=E9=96=A2=E6=95=B0?= =?UTF-8?q?=E6=A7=8B=E6=96=87=E3=81=AE=E3=82=B5=E3=82=B8=E3=82=A7=E3=82=B9?= =?UTF-8?q?=E3=83=88=E3=81=A7=E6=8B=AC=E5=BC=A7=E3=82=92=E7=84=A1=E8=A6=96?= =?UTF-8?q?=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/client/scripts/autocomplete.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/scripts/autocomplete.ts b/src/client/scripts/autocomplete.ts index e952ad3907..c0c33b2c7e 100644 --- a/src/client/scripts/autocomplete.ts +++ b/src/client/scripts/autocomplete.ts @@ -120,7 +120,7 @@ export class Autocomplete { if (isMfmTag && !opened) { const mfmTag = text.substr(mfmTagIndex + 1); if (!mfmTag.includes(' ')) { - this.open('mfmTag', mfmTag); + this.open('mfmTag', mfmTag.replace('[', '')); opened = true; } } From ebce02c253400907e5c31972785d99863d256bb5 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 26 Sep 2021 03:25:52 +0900 Subject: [PATCH 015/107] chore: clean up --- src/client/components/autocomplete.vue | 1 - 1 file changed, 1 deletion(-) diff --git a/src/client/components/autocomplete.vue b/src/client/components/autocomplete.vue index 98998293ac..e621b26229 100644 --- a/src/client/components/autocomplete.vue +++ b/src/client/components/autocomplete.vue @@ -310,7 +310,6 @@ export default defineComponent({ this.emojis = matched; } else if (this.type === 'mfmTag') { - console.log(this.q); if (this.q == null || this.q == '') { this.mfmTags = MFM_TAGS; return; From 0d3a36e519f24aac9d56cf0989fb7a44e123d72a Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Tue, 28 Sep 2021 01:50:02 +0900 Subject: [PATCH 016/107] add todo --- src/models/repositories/note.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/models/repositories/note.ts b/src/models/repositories/note.ts index c0ac22b2db..0f00c34c9c 100644 --- a/src/models/repositories/note.ts +++ b/src/models/repositories/note.ts @@ -272,6 +272,7 @@ export class NoteRepository extends Repository<Note> { const tokens = packed.text ? mfm.parse(packed.text) : []; mfm.inspect(tokens, node => { if (node.type === 'text') { + // TODO: quoteなtextはskip node.props.text = nyaize(node.props.text); } }); From 1ac1a968b9edd801aa13e8dae56ca378744d9e2e Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Thu, 30 Sep 2021 00:50:45 +0900 Subject: [PATCH 017/107] refactor components --- CONTRIBUTING.md | 4 + locales/ja-JP.yml | 2 + package.json | 6 +- src/client/components/abuse-report-window.vue | 2 +- src/client/components/cw-button.vue | 8 +- .../components/{form => debobigego}/base.vue | 24 +- .../{form => debobigego}/button.vue | 10 +- .../form.scss => debobigego/debobigego.scss} | 24 +- .../components/{form => debobigego}/group.vue | 26 +- .../components/{form => debobigego}/info.vue | 4 +- .../components/{ui => debobigego}/input.vue | 173 +++++------- .../{form => debobigego}/key-value-view.vue | 8 +- .../components/{form => debobigego}/link.vue | 8 +- .../{form => debobigego}/object-view.vue | 10 +- .../{form => debobigego}/pagination.vue | 2 +- src/client/components/debobigego/radios.vue | 112 ++++++++ src/client/components/debobigego/range.vue | 122 ++++++++ src/client/components/debobigego/select.vue | 145 ++++++++++ .../{form => debobigego}/suspense.vue | 12 +- src/client/components/debobigego/switch.vue | 132 +++++++++ src/client/components/debobigego/textarea.vue | 161 +++++++++++ .../components/{form => debobigego}/tuple.vue | 2 +- src/client/components/dialog.vue | 4 +- src/client/components/forgot-password.vue | 2 +- src/client/components/form-dialog.vue | 28 +- src/client/components/form/input.vue | 183 ++++++------ src/client/components/{ui => form}/radio.vue | 0 src/client/components/form/radios.vue | 88 ++---- src/client/components/form/range.vue | 141 +++++----- src/client/components/form/section.vue | 31 +++ src/client/components/form/select.vue | 247 ++++++++++++----- src/client/components/form/slot.vue | 50 ++++ src/client/components/form/switch.vue | 192 +++++++------ src/client/components/form/textarea.vue | 195 +++++++++---- src/client/components/instance-stats.vue | 2 +- src/client/components/note-detailed.vue | 2 +- src/client/components/note-preview.vue | 2 +- src/client/components/note.sub.vue | 2 +- src/client/components/note.vue | 2 +- .../notification-setting-window.vue | 2 +- .../components/page/page.number-input.vue | 2 +- src/client/components/page/page.post.vue | 2 +- .../components/page/page.radio-button.vue | 2 +- src/client/components/page/page.switch.vue | 2 +- .../components/page/page.text-input.vue | 2 +- .../components/page/page.textarea-input.vue | 2 +- src/client/components/page/page.textarea.vue | 2 +- src/client/components/poll-editor.vue | 6 +- src/client/components/sample.vue | 8 +- src/client/components/signin.vue | 10 +- src/client/components/signup.vue | 22 +- src/client/components/tab.vue | 8 +- .../components/taskmanager.api-window.vue | 2 +- src/client/components/taskmanager.vue | 2 +- .../components/token-generate-window.vue | 6 +- src/client/components/ui/button.vue | 8 - src/client/components/ui/radios.vue | 58 ---- src/client/components/ui/range.vue | 139 ---------- src/client/components/ui/select.vue | 262 ------------------ src/client/components/ui/switch.vue | 144 ---------- src/client/components/ui/textarea.vue | 254 ----------------- src/client/components/user-select-dialog.vue | 4 +- src/client/components/widgets.vue | 2 +- src/client/pages/about-misskey.vue | 14 +- src/client/pages/about.vue | 16 +- src/client/pages/advanced-theme-editor.vue | 30 +- src/client/pages/api-console.vue | 8 +- src/client/pages/channel-editor.vue | 4 +- src/client/pages/channels.vue | 2 +- src/client/pages/docs.vue | 4 +- src/client/pages/emojis.category.vue | 6 +- src/client/pages/explore.vue | 2 +- src/client/pages/federation.vue | 8 +- src/client/pages/gallery/edit.vue | 24 +- src/client/pages/gallery/index.vue | 4 +- src/client/pages/instance-info.vue | 28 +- src/client/pages/instance/abuses.vue | 8 +- src/client/pages/instance/ads.vue | 6 +- src/client/pages/instance/announcements.vue | 4 +- src/client/pages/instance/bot-protection.vue | 42 +-- src/client/pages/instance/database.vue | 10 +- src/client/pages/instance/email-settings.vue | 32 +-- .../pages/instance/emoji-edit-dialog.vue | 2 +- src/client/pages/instance/emojis.vue | 4 +- src/client/pages/instance/file-dialog.vue | 2 +- src/client/pages/instance/files-settings.vue | 20 +- src/client/pages/instance/files.vue | 4 +- src/client/pages/instance/index.vue | 12 +- src/client/pages/instance/instance-block.vue | 18 +- src/client/pages/instance/instance.vue | 4 +- .../pages/instance/integrations-discord.vue | 18 +- .../pages/instance/integrations-github.vue | 18 +- .../pages/instance/integrations-twitter.vue | 18 +- src/client/pages/instance/integrations.vue | 16 +- src/client/pages/instance/logs.vue | 6 +- src/client/pages/instance/metrics.vue | 22 +- src/client/pages/instance/object-storage.vue | 36 +-- src/client/pages/instance/other-settings.vue | 18 +- src/client/pages/instance/overview.vue | 24 +- src/client/pages/instance/proxy-account.vue | 16 +- src/client/pages/instance/queue.chart.vue | 6 +- src/client/pages/instance/queue.vue | 4 +- src/client/pages/instance/relays.vue | 10 +- src/client/pages/instance/security.vue | 16 +- src/client/pages/instance/service-worker.vue | 18 +- src/client/pages/instance/settings.vue | 38 +-- src/client/pages/instance/users.vue | 4 +- src/client/pages/mfm-cheat-sheet.vue | 2 +- src/client/pages/my-antennas/editor.vue | 10 +- src/client/pages/my-groups/index.vue | 2 +- .../page-editor/els/page-editor.el.button.vue | 6 +- .../page-editor/els/page-editor.el.canvas.vue | 2 +- .../els/page-editor.el.counter.vue | 2 +- .../page-editor/els/page-editor.el.if.vue | 4 +- .../page-editor/els/page-editor.el.note.vue | 4 +- .../els/page-editor.el.number-input.vue | 2 +- .../page-editor/els/page-editor.el.post.vue | 6 +- .../els/page-editor.el.radio-button.vue | 4 +- .../els/page-editor.el.section.vue | 2 +- .../page-editor/els/page-editor.el.switch.vue | 4 +- .../els/page-editor.el.text-input.vue | 2 +- .../els/page-editor.el.textarea-input.vue | 4 +- .../pages/page-editor/page-editor.blocks.vue | 13 +- .../page-editor/page-editor.script-block.vue | 94 +++---- src/client/pages/page-editor/page-editor.vue | 10 +- src/client/pages/pages.vue | 2 +- src/client/pages/reset-password.vue | 12 +- src/client/pages/reversi/game.setting.vue | 4 +- src/client/pages/room/room.vue | 2 +- src/client/pages/settings/2fa.vue | 10 +- src/client/pages/settings/account-info.vue | 10 +- src/client/pages/settings/accounts.vue | 15 +- src/client/pages/settings/api.vue | 11 +- src/client/pages/settings/apps.vue | 13 +- src/client/pages/settings/custom-css.vue | 15 +- src/client/pages/settings/deck.vue | 15 +- src/client/pages/settings/delete-account.vue | 11 +- src/client/pages/settings/drive.vue | 21 +- src/client/pages/settings/email-address.vue | 11 +- .../pages/settings/email-notification.vue | 21 +- src/client/pages/settings/email.vue | 15 +- .../pages/settings/experimental-features.vue | 10 +- src/client/pages/settings/general.vue | 61 ++-- src/client/pages/settings/import-export.vue | 9 +- src/client/pages/settings/index.link.vue | 97 +++++++ src/client/pages/settings/index.vue | 99 ++++--- src/client/pages/settings/integration.vue | 23 +- src/client/pages/settings/menu.vue | 15 +- src/client/pages/settings/mute-block.vue | 13 +- src/client/pages/settings/notifications.vue | 11 +- src/client/pages/settings/other.vue | 17 +- src/client/pages/settings/plugin.install.vue | 15 +- src/client/pages/settings/plugin.manage.vue | 21 +- src/client/pages/settings/plugin.vue | 9 +- src/client/pages/settings/privacy.vue | 29 +- src/client/pages/settings/profile.vue | 35 +-- src/client/pages/settings/reaction.vue | 19 +- src/client/pages/settings/registry.keys.vue | 13 +- src/client/pages/settings/registry.value.vue | 15 +- src/client/pages/settings/registry.vue | 13 +- src/client/pages/settings/security.vue | 15 +- src/client/pages/settings/sounds.vue | 15 +- src/client/pages/settings/theme.install.vue | 13 +- src/client/pages/settings/theme.manage.vue | 11 +- src/client/pages/settings/theme.vue | 27 +- src/client/pages/settings/update.vue | 15 +- src/client/pages/settings/word-mute.vue | 19 +- src/client/pages/test.vue | 8 +- src/client/pages/theme-editor.vue | 30 +- src/client/pages/user-ap-info.vue | 16 +- src/client/pages/user-info.vue | 26 +- src/client/pages/user/index.timeline.vue | 2 +- src/client/pages/welcome.setup.vue | 2 +- src/client/style.scss | 14 +- src/client/ui/_common_/sidebar.vue | 2 +- src/client/ui/chat/note-preview.vue | 2 +- src/client/ui/chat/note.sub.vue | 2 +- src/client/ui/chat/note.vue | 2 +- yarn.lock | 166 +++++------ 179 files changed, 2611 insertions(+), 2386 deletions(-) rename src/client/components/{form => debobigego}/base.vue (63%) rename src/client/components/{form => debobigego}/button.vue (77%) rename src/client/components/{form/form.scss => debobigego/debobigego.scss} (52%) rename src/client/components/{form => debobigego}/group.vue (57%) rename src/client/components/{form => debobigego}/info.vue (87%) rename src/client/components/{ui => debobigego}/input.vue (62%) rename src/client/components/{form => debobigego}/key-value-view.vue (78%) rename src/client/components/{form => debobigego}/link.vue (84%) rename src/client/components/{form => debobigego}/object-view.vue (88%) rename src/client/components/{form => debobigego}/pagination.vue (95%) create mode 100644 src/client/components/debobigego/radios.vue create mode 100644 src/client/components/debobigego/range.vue create mode 100644 src/client/components/debobigego/select.vue rename src/client/components/{form => debobigego}/suspense.vue (87%) create mode 100644 src/client/components/debobigego/switch.vue create mode 100644 src/client/components/debobigego/textarea.vue rename src/client/components/{form => debobigego}/tuple.vue (87%) rename src/client/components/{ui => form}/radio.vue (100%) create mode 100644 src/client/components/form/section.vue create mode 100644 src/client/components/form/slot.vue delete mode 100644 src/client/components/ui/radios.vue delete mode 100644 src/client/components/ui/range.vue delete mode 100644 src/client/components/ui/select.vue delete mode 100644 src/client/components/ui/switch.vue delete mode 100644 src/client/components/ui/textarea.vue create mode 100644 src/client/pages/settings/index.link.vue diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 72a7dc4b16..06154f1f44 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -177,6 +177,10 @@ npx ts-node ./node_modules/typeorm/cli.js migration:generate -n 変更の名前 ### JSONのimportに気を付けよう TypeScriptでjsonをimportすると、tscでコンパイルするときにそのjsonファイルも一緒にdistディレクトリに吐き出されてしまう。この挙動により、意図せずファイルの書き換えが発生することがあるので、jsonをimportするときは書き換えられても良いものかどうか確認すること。書き換えされて欲しくない場合は、importで読み込むのではなく、`fs.readFileSync`などの関数を使って読み込むようにすればよい。 +### コンポーネントのスタイル定義でmarginを持たせない +コンポーネント自身がmarginを設定するのは問題の元となることはよく知られている +marginはそのコンポーネントを使う側が設定する + ## その他 ### HTMLのクラス名で follow という単語は使わない 広告ブロッカーで誤ってブロックされる diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index f050ad4df8..f5f859b9ff 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -787,6 +787,8 @@ pubSub: "Pub/Subのアカウント" lastCommunication: "直近の通信" resolved: "解決済み" unresolved: "未解決" +itsOn: "オンになっています" +itsOff: "オフになっています" _accountDelete: accountDelete: "アカウントの削除" diff --git a/package.json b/package.json index 2d0c1cc5fc..9f37bea172 100644 --- a/package.json +++ b/package.json @@ -104,7 +104,7 @@ "@types/websocket": "1.0.4", "@types/ws": "7.4.7", "@typescript-eslint/parser": "4.31.2", - "@vue/compiler-sfc": "3.2.13", + "@vue/compiler-sfc": "3.2.19", "abort-controller": "3.0.0", "apexcharts": "3.28.3", "autobind-decorator": "2.4.0", @@ -233,7 +233,7 @@ "uuid": "8.3.2", "v-debounce": "0.1.2", "vanilla-tilt": "1.7.2", - "vue": "3.2.13", + "vue": "3.2.19", "vue-loader": "16.7.0", "vue-prism-editor": "2.0.0-alpha.2", "vue-router": "4.0.5", @@ -241,7 +241,7 @@ "vue-svg-loader": "0.17.0-beta.2", "vuedraggable": "4.0.1", "web-push": "3.4.5", - "webpack": "5.53.0", + "webpack": "5.54.0", "webpack-cli": "4.8.0", "websocket": "1.0.34", "ws": "8.2.2", diff --git a/src/client/components/abuse-report-window.vue b/src/client/components/abuse-report-window.vue index 266c0d566f..21a19385ae 100644 --- a/src/client/components/abuse-report-window.vue +++ b/src/client/components/abuse-report-window.vue @@ -25,7 +25,7 @@ <script lang="ts"> import { defineComponent, markRaw } from 'vue'; import XWindow from '@client/components/ui/window.vue'; -import MkTextarea from '@client/components/ui/textarea.vue'; +import MkTextarea from '@client/components/form/textarea.vue'; import MkButton from '@client/components/ui/button.vue'; import * as os from '@client/os'; diff --git a/src/client/components/cw-button.vue b/src/client/components/cw-button.vue index d2336085ad..3a172f5d5e 100644 --- a/src/client/components/cw-button.vue +++ b/src/client/components/cw-button.vue @@ -1,7 +1,7 @@ <template> <button class="nrvgflfu _button" @click="toggle"> - <b>{{ value ? $ts._cw.hide : $ts._cw.show }}</b> - <span v-if="!value">{{ label }}</span> + <b>{{ modelValue ? $ts._cw.hide : $ts._cw.show }}</b> + <span v-if="!modelValue">{{ label }}</span> </button> </template> @@ -12,7 +12,7 @@ import { concat } from '../../prelude/array'; export default defineComponent({ props: { - value: { + modelValue: { type: Boolean, required: true }, @@ -36,7 +36,7 @@ export default defineComponent({ length, toggle() { - this.$emit('update:value', !this.value); + this.$emit('update:modelValue', !this.modelValue); } } }); diff --git a/src/client/components/form/base.vue b/src/client/components/debobigego/base.vue similarity index 63% rename from src/client/components/form/base.vue rename to src/client/components/debobigego/base.vue index 132942d527..f551a3478b 100644 --- a/src/client/components/form/base.vue +++ b/src/client/components/debobigego/base.vue @@ -21,39 +21,39 @@ export default defineComponent({ <style lang="scss" scoped> .rbusrurv { // 他のCSSからも参照されるので消さないように - --formXPadding: 32px; - --formYPadding: 32px; + --debobigegoXPadding: 32px; + --debobigegoYPadding: 32px; - --formContentHMargin: 16px; + --debobigegoContentHMargin: 16px; font-size: 95%; line-height: 1.3em; background: var(--bg); - padding: var(--formYPadding) var(--formXPadding); + padding: var(--debobigegoYPadding) var(--debobigegoXPadding); max-width: 750px; margin: 0 auto; &:not(.wide).max-width_400px { - --formXPadding: 0px; + --debobigegoXPadding: 0px; > ::v-deep(*) { - ._formPanel { + ._debobigegoPanel { border: solid 0.5px var(--divider); border-radius: 0; border-left: none; border-right: none; } - ._form_group { - > *:not(._formNoConcat) { - &:not(:last-child):not(._formNoConcatPrev) { - &._formPanel, ._formPanel { + ._debobigego_group { + > *:not(._debobigegoNoConcat) { + &:not(:last-child):not(._debobigegoNoConcatPrev) { + &._debobigegoPanel, ._debobigegoPanel { border-bottom: solid 0.5px var(--divider); } } - &:not(:first-child):not(._formNoConcatNext) { - &._formPanel, ._formPanel { + &:not(:first-child):not(._debobigegoNoConcatNext) { + &._debobigegoPanel, ._debobigegoPanel { border-top: none; } } diff --git a/src/client/components/form/button.vue b/src/client/components/debobigego/button.vue similarity index 77% rename from src/client/components/form/button.vue rename to src/client/components/debobigego/button.vue index b4f0890945..b883e817a4 100644 --- a/src/client/components/form/button.vue +++ b/src/client/components/debobigego/button.vue @@ -1,7 +1,7 @@ <template> -<div class="yzpgjkxe _formItem"> - <div class="_formLabel"><slot name="label"></slot></div> - <button class="main _button _formPanel _formClickable" :class="{ center, primary, danger }"> +<div class="yzpgjkxe _debobigegoItem"> + <div class="_debobigegoLabel"><slot name="label"></slot></div> + <button class="main _button _debobigegoPanel _debobigegoClickable" :class="{ center, primary, danger }"> <slot></slot> <div class="suffix"> <slot name="suffix"></slot> @@ -10,13 +10,13 @@ </div> </div> </button> - <div class="_formCaption"><slot name="desc"></slot></div> + <div class="_debobigegoCaption"><slot name="desc"></slot></div> </div> </template> <script lang="ts"> import { defineComponent } from 'vue'; -import './form.scss'; +import './debobigego.scss'; export default defineComponent({ props: { diff --git a/src/client/components/form/form.scss b/src/client/components/debobigego/debobigego.scss similarity index 52% rename from src/client/components/form/form.scss rename to src/client/components/debobigego/debobigego.scss index 00f40df9b1..833b656b66 100644 --- a/src/client/components/form/form.scss +++ b/src/client/components/debobigego/debobigego.scss @@ -1,9 +1,9 @@ -._formPanel { +._debobigegoPanel { background: var(--panel); border-radius: var(--radius); transition: background 0.2s ease; - &._formClickable { + &._debobigegoClickable { &:hover { //background: var(--panelHighlight); } @@ -15,8 +15,8 @@ } } -._formLabel, -._formCaption { +._debobigegoLabel, +._debobigegoCaption { font-size: 80%; color: var(--fgTransparentWeak); @@ -25,28 +25,28 @@ } } -._formLabel { +._debobigegoLabel { position: sticky; top: var(--stickyTop, 0px); z-index: 2; - margin: -8px calc(var(--formXPadding) * -1) 0 calc(var(--formXPadding) * -1); - padding: 8px calc(var(--formContentHMargin) + var(--formXPadding)) 8px calc(var(--formContentHMargin) + var(--formXPadding)); + margin: -8px calc(var(--debobigegoXPadding) * -1) 0 calc(var(--debobigegoXPadding) * -1); + padding: 8px calc(var(--debobigegoContentHMargin) + var(--debobigegoXPadding)) 8px calc(var(--debobigegoContentHMargin) + var(--debobigegoXPadding)); background: var(--X17); -webkit-backdrop-filter: var(--blur, blur(10px)); backdrop-filter: var(--blur, blur(10px)); } -._themeChanging_ ._formLabel { +._themeChanging_ ._debobigegoLabel { transition: none !important; background: transparent; } -._formCaption { - padding: 8px var(--formContentHMargin) 0 var(--formContentHMargin); +._debobigegoCaption { + padding: 8px var(--debobigegoContentHMargin) 0 var(--debobigegoContentHMargin); } -._formItem { - & + ._formItem { +._debobigegoItem { + & + ._debobigegoItem { margin-top: 24px; } } diff --git a/src/client/components/form/group.vue b/src/client/components/debobigego/group.vue similarity index 57% rename from src/client/components/form/group.vue rename to src/client/components/debobigego/group.vue index 34ccaeff07..cba2c6ec94 100644 --- a/src/client/components/form/group.vue +++ b/src/client/components/debobigego/group.vue @@ -1,10 +1,10 @@ <template> -<div class="vrtktovg _formItem _formNoConcat" v-size="{ max: [500] }" v-sticky-container> - <div class="_formLabel"><slot name="label"></slot></div> - <div class="main _form_group" ref="child"> +<div class="vrtktovg _debobigegoItem _debobigegoNoConcat" v-size="{ max: [500] }" v-sticky-container> + <div class="_debobigegoLabel"><slot name="label"></slot></div> + <div class="main _debobigego_group" ref="child"> <slot></slot> </div> - <div class="_formCaption"><slot name="caption"></slot></div> + <div class="_debobigegoCaption"><slot name="caption"></slot></div> </div> </template> @@ -20,9 +20,9 @@ export default defineComponent({ const els = Array.from(child.value.children); for (let i = 0; i < els.length; i++) { const el = els[i]; - if (el.classList.contains('_formNoConcat')) { - if (els[i - 1]) els[i - 1].classList.add('_formNoConcatPrev'); - if (els[i + 1]) els[i + 1].classList.add('_formNoConcatNext'); + if (el.classList.contains('_debobigegoNoConcat')) { + if (els[i - 1]) els[i - 1].classList.add('_debobigegoNoConcatPrev'); + if (els[i + 1]) els[i + 1].classList.add('_debobigegoNoConcatNext'); } } }; @@ -52,21 +52,21 @@ export default defineComponent({ <style lang="scss" scoped> .vrtktovg { > .main { - > ::v-deep(*):not(._formNoConcat) { - &:not(._formNoConcatNext) { + > ::v-deep(*):not(._debobigegoNoConcat) { + &:not(._debobigegoNoConcatNext) { margin: 0; } - &:not(:last-child):not(._formNoConcatPrev) { - &._formPanel, ._formPanel { + &:not(:last-child):not(._debobigegoNoConcatPrev) { + &._debobigegoPanel, ._debobigegoPanel { border-bottom: solid 0.5px var(--divider); border-bottom-left-radius: 0; border-bottom-right-radius: 0; } } - &:not(:first-child):not(._formNoConcatNext) { - &._formPanel, ._formPanel { + &:not(:first-child):not(._debobigegoNoConcatNext) { + &._debobigegoPanel, ._debobigegoPanel { border-top: none; border-top-left-radius: 0; border-top-right-radius: 0; diff --git a/src/client/components/form/info.vue b/src/client/components/debobigego/info.vue similarity index 87% rename from src/client/components/form/info.vue rename to src/client/components/debobigego/info.vue index 9fdcbdca62..41afb03304 100644 --- a/src/client/components/form/info.vue +++ b/src/client/components/debobigego/info.vue @@ -1,6 +1,6 @@ <template> -<div class="fzenkabp _formItem"> - <div class="_formPanel" :class="{ warn }"> +<div class="fzenkabp _debobigegoItem"> + <div class="_debobigegoPanel" :class="{ warn }"> <i v-if="warn" class="fas fa-exclamation-triangle"></i> <i v-else class="fas fa-info-circle"></i> <slot></slot> diff --git a/src/client/components/ui/input.vue b/src/client/components/debobigego/input.vue similarity index 62% rename from src/client/components/ui/input.vue rename to src/client/components/debobigego/input.vue index a916a0b035..d113f04d27 100644 --- a/src/client/components/ui/input.vue +++ b/src/client/components/debobigego/input.vue @@ -1,49 +1,53 @@ <template> -<div class="matxzzsk"> - <div class="label" @click="focus"><slot name="label"></slot></div> - <div class="input" :class="{ inline, disabled, focused }"> - <div class="prefix" ref="prefixEl"><slot name="prefix"></slot></div> - <input ref="inputEl" - :type="type" - v-model="v" - :disabled="disabled" - :required="required" - :readonly="readonly" - :placeholder="placeholder" - :pattern="pattern" - :autocomplete="autocomplete" - :spellcheck="spellcheck" - :step="step" - @focus="focused = true" - @blur="focused = false" - @keydown="onKeydown($event)" - @input="onInput" - :list="id" - > - <datalist :id="id" v-if="datalist"> - <option v-for="data in datalist" :value="data"/> - </datalist> - <div class="suffix" ref="suffixEl"><slot name="suffix"></slot></div> +<FormGroup class="_debobigegoItem"> + <template #label><slot></slot></template> + <div class="ztzhwixg _debobigegoItem" :class="{ inline, disabled }"> + <div class="icon" ref="icon"><slot name="icon"></slot></div> + <div class="input _debobigegoPanel"> + <div class="prefix" ref="prefixEl"><slot name="prefix"></slot></div> + <input ref="inputEl" + :type="type" + v-model="v" + :disabled="disabled" + :required="required" + :readonly="readonly" + :placeholder="placeholder" + :pattern="pattern" + :autocomplete="autocomplete" + :spellcheck="spellcheck" + :step="step" + @focus="focused = true" + @blur="focused = false" + @keydown="onKeydown($event)" + @input="onInput" + :list="id" + > + <datalist :id="id" v-if="datalist"> + <option v-for="data in datalist" :value="data"/> + </datalist> + <div class="suffix" ref="suffixEl"><slot name="suffix"></slot></div> + </div> </div> - <div class="caption"><slot name="caption"></slot></div> + <template #caption><slot name="desc"></slot></template> - <MkButton v-if="manualSave && changed" @click="updated" primary><i class="fas fa-save"></i> {{ $ts.save }}</MkButton> -</div> + <FormButton v-if="manualSave && changed" @click="updated" primary><i class="fas fa-save"></i> {{ $ts.save }}</FormButton> +</FormGroup> </template> <script lang="ts"> import { defineComponent, onMounted, onUnmounted, nextTick, ref, watch, computed, toRefs } from 'vue'; -import MkButton from './button.vue'; -import { debounce } from 'throttle-debounce'; +import './debobigego.scss'; +import FormButton from './button.vue'; +import FormGroup from './group.vue'; export default defineComponent({ components: { - MkButton, + FormGroup, + FormButton, }, - props: { modelValue: { - required: true + required: false }, type: { type: String, @@ -92,20 +96,13 @@ export default defineComponent({ required: false, default: false }, - debounce: { - type: Boolean, - required: false, - default: false - }, manualSave: { type: Boolean, required: false, default: false }, }, - emits: ['change', 'keydown', 'enter', 'update:modelValue'], - setup(props, context) { const { modelValue, type, autofocus } = toRefs(props); const v = ref(modelValue.value); @@ -140,19 +137,13 @@ export default defineComponent({ } }; - const debouncedUpdated = debounce(1000, updated); - - watch(modelValue, newValue => { + watch(modelValue.value, newValue => { v.value = newValue; }); watch(v, newValue => { if (!props.manualSave) { - if (props.debounce) { - debouncedUpdated(); - } else { - updated(); - } + updated(); } invalid.value = inputEl.value.validity.badInput; @@ -205,68 +196,59 @@ export default defineComponent({ </script> <style lang="scss" scoped> -.matxzzsk { - margin: 1.5em 0; +.ztzhwixg { + position: relative; - > .label { - font-size: 0.85em; - padding: 0 0 8px 12px; - user-select: none; + > .icon { + position: absolute; + top: 0; + left: 0; + width: 24px; + text-align: center; + line-height: 32px; - &:empty { - display: none; - } - } - - > .caption { - font-size: 0.8em; - padding: 8px 0 0 12px; - color: var(--fgTransparentWeak); - - &:empty { - display: none; + &:not(:empty) + .input { + margin-left: 28px; } } > .input { - $height: 42px; + $height: 48px; position: relative; > input { - appearance: none; - -webkit-appearance: none; display: block; height: $height; width: 100%; margin: 0; - padding: 0 12px; + padding: 0 16px; font: inherit; font-weight: normal; font-size: 1em; - color: var(--fg); - background: var(--panel); - border: solid 0.5px var(--inputBorder); - border-radius: 6px; + line-height: $height; + color: var(--inputText); + background: transparent; + border: none; + border-radius: 0; outline: none; box-shadow: none; box-sizing: border-box; - transition: border-color 0.1s ease-out; - &:hover { - border-color: var(--inputBorderHover); + &[type='file'] { + display: none; } } > .prefix, > .suffix { - display: flex; - align-items: center; + display: block; position: absolute; z-index: 1; top: 0; - padding: 0 12px; + padding: 0 16px; font-size: 1em; - height: $height; + line-height: $height; + color: var(--inputLabel); pointer-events: none; &:empty { @@ -285,32 +267,25 @@ export default defineComponent({ > .prefix { left: 0; - padding-right: 6px; + padding-right: 8px; } > .suffix { right: 0; - padding-left: 6px; + padding-left: 8px; } + } - &.inline { - display: inline-block; - margin: 0; - } + &.inline { + display: inline-block; + margin: 0; + } - &.focused { - > input { - border-color: var(--accent); - //box-shadow: 0 0 0 4px var(--focus); - } - } + &.disabled { + opacity: 0.7; - &.disabled { - opacity: 0.7; - - &, * { - cursor: not-allowed !important; - } + &, * { + cursor: not-allowed !important; } } } diff --git a/src/client/components/form/key-value-view.vue b/src/client/components/debobigego/key-value-view.vue similarity index 78% rename from src/client/components/form/key-value-view.vue rename to src/client/components/debobigego/key-value-view.vue index ca4c09867f..0e034a2d54 100644 --- a/src/client/components/form/key-value-view.vue +++ b/src/client/components/debobigego/key-value-view.vue @@ -1,6 +1,6 @@ <template> -<div class="_formItem"> - <div class="_formPanel anocepby"> +<div class="_debobigegoItem"> + <div class="_debobigegoPanel anocepby"> <span class="key"><slot name="key"></slot></span> <span class="value"><slot name="value"></slot></span> </div> @@ -9,7 +9,7 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import './form.scss'; +import './debobigego.scss'; export default defineComponent({ @@ -20,7 +20,7 @@ export default defineComponent({ .anocepby { display: flex; align-items: center; - padding: 14px var(--formContentHMargin); + padding: 14px var(--debobigegoContentHMargin); > .key { margin-right: 12px; diff --git a/src/client/components/form/link.vue b/src/client/components/debobigego/link.vue similarity index 84% rename from src/client/components/form/link.vue rename to src/client/components/debobigego/link.vue index e1d13c6431..885579eadf 100644 --- a/src/client/components/form/link.vue +++ b/src/client/components/debobigego/link.vue @@ -1,6 +1,6 @@ <template> -<div class="qmfkfnzi _formItem"> - <a class="main _button _formPanel _formClickable" :href="to" target="_blank" v-if="external"> +<div class="qmfkfnzi _debobigegoItem"> + <a class="main _button _debobigegoPanel _debobigegoClickable" :href="to" target="_blank" v-if="external"> <span class="icon"><slot name="icon"></slot></span> <span class="text"><slot></slot></span> <span class="right"> @@ -8,7 +8,7 @@ <i class="fas fa-external-link-alt icon"></i> </span> </a> - <MkA class="main _button _formPanel _formClickable" :class="{ active }" :to="to" :behavior="behavior" v-else> + <MkA class="main _button _debobigegoPanel _debobigegoClickable" :class="{ active }" :to="to" :behavior="behavior" v-else> <span class="icon"><slot name="icon"></slot></span> <span class="text"><slot></slot></span> <span class="right"> @@ -21,7 +21,7 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import './form.scss'; +import './debobigego.scss'; export default defineComponent({ props: { diff --git a/src/client/components/form/object-view.vue b/src/client/components/debobigego/object-view.vue similarity index 88% rename from src/client/components/form/object-view.vue rename to src/client/components/debobigego/object-view.vue index 59fb62b5e6..ea79daa915 100644 --- a/src/client/components/form/object-view.vue +++ b/src/client/components/debobigego/object-view.vue @@ -1,8 +1,8 @@ <template> -<FormGroup class="_formItem"> +<FormGroup class="_debobigegoItem"> <template #label><slot></slot></template> - <div class="drooglns _formItem" :class="{ tall }"> - <div class="input _formPanel"> + <div class="drooglns _debobigegoItem" :class="{ tall }"> + <div class="input _debobigegoPanel"> <textarea class="_monospace" v-model="v" readonly @@ -17,7 +17,7 @@ <script lang="ts"> import { defineComponent, ref, toRefs, watch } from 'vue'; import * as JSON5 from 'json5'; -import './form.scss'; +import './debobigego.scss'; import FormGroup from './group.vue'; export default defineComponent({ @@ -75,7 +75,7 @@ export default defineComponent({ max-width: 100%; min-height: 130px; margin: 0; - padding: 16px var(--formContentHMargin); + padding: 16px var(--debobigegoContentHMargin); box-sizing: border-box; font: inherit; font-weight: normal; diff --git a/src/client/components/form/pagination.vue b/src/client/components/debobigego/pagination.vue similarity index 95% rename from src/client/components/form/pagination.vue rename to src/client/components/debobigego/pagination.vue index 0a2f1ff0e1..2166f5065f 100644 --- a/src/client/components/form/pagination.vue +++ b/src/client/components/debobigego/pagination.vue @@ -1,5 +1,5 @@ <template> -<FormGroup class="uljviswt _formItem"> +<FormGroup class="uljviswt _debobigegoItem"> <template #label><slot name="label"></slot></template> <slot :items="items"></slot> <div class="empty" v-if="empty" key="_empty_"> diff --git a/src/client/components/debobigego/radios.vue b/src/client/components/debobigego/radios.vue new file mode 100644 index 0000000000..071c013afb --- /dev/null +++ b/src/client/components/debobigego/radios.vue @@ -0,0 +1,112 @@ +<script lang="ts"> +import { defineComponent, h } from 'vue'; +import MkRadio from '@client/components/form/radio.vue'; +import './debobigego.scss'; + +export default defineComponent({ + components: { + MkRadio + }, + props: { + modelValue: { + required: false + }, + }, + data() { + return { + value: this.modelValue, + } + }, + watch: { + modelValue() { + this.value = this.modelValue; + }, + value() { + this.$emit('update:modelValue', this.value); + } + }, + render() { + const label = this.$slots.desc(); + let options = this.$slots.default(); + + // なぜかFragmentになることがあるため + if (options.length === 1 && options[0].props == null) options = options[0].children; + + return h('div', { + class: 'cnklmpwm _debobigegoItem' + }, [ + h('div', { + class: '_debobigegoLabel', + }, label), + ...options.map(option => h('button', { + class: '_button _debobigegoPanel _debobigegoClickable', + key: option.key, + onClick: () => this.value = option.props.value, + }, [h('span', { + class: ['check', { checked: this.value === option.props.value }], + }), option.children])) + ]); + } +}); +</script> + +<style lang="scss"> +.cnklmpwm { + > button { + display: block; + width: 100%; + box-sizing: border-box; + padding: 14px 18px; + text-align: left; + + &:not(:first-of-type) { + border-top: none !important; + border-top-left-radius: 0; + border-top-right-radius: 0; + } + + &:not(:last-of-type) { + border-bottom: solid 0.5px var(--divider); + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + } + + > .check { + display: inline-block; + vertical-align: bottom; + position: relative; + width: 16px; + height: 16px; + margin-right: 8px; + background: none; + border: 2px solid var(--inputBorder); + border-radius: 100%; + transition: inherit; + + &:after { + content: ""; + display: block; + position: absolute; + top: 3px; + right: 3px; + bottom: 3px; + left: 3px; + border-radius: 100%; + opacity: 0; + transform: scale(0); + transition: .4s cubic-bezier(.25,.8,.25,1); + } + + &.checked { + border-color: var(--accent); + + &:after { + background-color: var(--accent); + transform: scale(1); + opacity: 1; + } + } + } + } +} +</style> diff --git a/src/client/components/debobigego/range.vue b/src/client/components/debobigego/range.vue new file mode 100644 index 0000000000..26fb0f37c6 --- /dev/null +++ b/src/client/components/debobigego/range.vue @@ -0,0 +1,122 @@ +<template> +<div class="ifitouly _debobigegoItem" :class="{ focused, disabled }"> + <div class="_debobigegoLabel"><slot name="label"></slot></div> + <div class="_debobigegoPanel main"> + <input + type="range" + ref="input" + v-model="v" + :disabled="disabled" + :min="min" + :max="max" + :step="step" + @focus="focused = true" + @blur="focused = false" + @input="$emit('update:value', $event.target.value)" + /> + </div> + <div class="_debobigegoCaption"><slot name="caption"></slot></div> +</div> +</template> + +<script lang="ts"> +import { defineComponent } from 'vue'; + +export default defineComponent({ + props: { + value: { + type: Number, + required: false, + default: 0 + }, + disabled: { + type: Boolean, + required: false, + default: false + }, + min: { + type: Number, + required: false, + default: 0 + }, + max: { + type: Number, + required: false, + default: 100 + }, + step: { + type: Number, + required: false, + default: 1 + }, + }, + data() { + return { + v: this.value, + focused: false + }; + }, + watch: { + value(v) { + this.v = parseFloat(v); + } + }, +}); +</script> + +<style lang="scss" scoped> +.ifitouly { + position: relative; + + > .main { + padding: 22px 16px; + + > input { + display: block; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background: var(--X10); + height: 4px; + width: 100%; + box-sizing: border-box; + margin: 0; + outline: 0; + border: 0; + border-radius: 7px; + + &.disabled { + opacity: 0.6; + cursor: not-allowed; + } + + &::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + cursor: pointer; + width: 20px; + height: 20px; + display: block; + border-radius: 50%; + border: none; + background: var(--accent); + box-shadow: 0 0 6px rgba(0, 0, 0, 0.3); + box-sizing: content-box; + } + + &::-moz-range-thumb { + -moz-appearance: none; + appearance: none; + cursor: pointer; + width: 20px; + height: 20px; + display: block; + border-radius: 50%; + border: none; + background: var(--accent); + box-shadow: 0 0 6px rgba(0, 0, 0, 0.3); + } + } + } +} +</style> diff --git a/src/client/components/debobigego/select.vue b/src/client/components/debobigego/select.vue new file mode 100644 index 0000000000..7a31371afc --- /dev/null +++ b/src/client/components/debobigego/select.vue @@ -0,0 +1,145 @@ +<template> +<div class="yrtfrpux _debobigegoItem" :class="{ disabled, inline }"> + <div class="_debobigegoLabel"><slot name="label"></slot></div> + <div class="icon" ref="icon"><slot name="icon"></slot></div> + <div class="input _debobigegoPanel _debobigegoClickable" @click="focus"> + <div class="prefix" ref="prefix"><slot name="prefix"></slot></div> + <select ref="input" + v-model="v" + :required="required" + :disabled="disabled" + @focus="focused = true" + @blur="focused = false" + > + <slot></slot> + </select> + <div class="suffix"> + <i class="fas fa-chevron-down"></i> + </div> + </div> + <div class="_debobigegoCaption"><slot name="caption"></slot></div> +</div> +</template> + +<script lang="ts"> +import { defineComponent } from 'vue'; +import './debobigego.scss'; + +export default defineComponent({ + props: { + modelValue: { + required: false + }, + required: { + type: Boolean, + required: false + }, + disabled: { + type: Boolean, + required: false + }, + inline: { + type: Boolean, + required: false, + default: false + }, + }, + data() { + return { + }; + }, + computed: { + v: { + get() { + return this.modelValue; + }, + set(v) { + this.$emit('update:modelValue', v); + } + }, + }, + methods: { + focus() { + this.$refs.input.focus(); + } + } +}); +</script> + +<style lang="scss" scoped> +.yrtfrpux { + position: relative; + + > .icon { + position: absolute; + top: 0; + left: 0; + width: 24px; + text-align: center; + line-height: 32px; + + &:not(:empty) + .input { + margin-left: 28px; + } + } + + > .input { + display: flex; + position: relative; + + > select { + display: block; + flex: 1; + width: 100%; + padding: 0 16px; + font: inherit; + font-weight: normal; + font-size: 1em; + height: 48px; + background: none; + border: none; + border-radius: 0; + outline: none; + box-shadow: none; + appearance: none; + -webkit-appearance: none; + color: var(--fg); + + option, + optgroup { + color: var(--fg); + background: var(--bg); + } + } + + > .prefix, + > .suffix { + display: block; + align-self: center; + justify-self: center; + font-size: 1em; + line-height: 32px; + color: var(--inputLabel); + pointer-events: none; + + &:empty { + display: none; + } + + > * { + display: block; + min-width: 16px; + } + } + + > .prefix { + padding-right: 4px; + } + + > .suffix { + padding: 0 16px 0 0; + opacity: 0.7; + } + } +} +</style> diff --git a/src/client/components/form/suspense.vue b/src/client/components/debobigego/suspense.vue similarity index 87% rename from src/client/components/form/suspense.vue rename to src/client/components/debobigego/suspense.vue index d04dc07624..e59e0ba12d 100644 --- a/src/client/components/form/suspense.vue +++ b/src/client/components/debobigego/suspense.vue @@ -1,15 +1,15 @@ <template> <transition name="fade" mode="out-in"> - <div class="_formItem" v-if="pending"> - <div class="_formPanel"> + <div class="_debobigegoItem" v-if="pending"> + <div class="_debobigegoPanel"> <MkLoading/> </div> </div> - <div v-else-if="resolved" class="_formItem"> + <div v-else-if="resolved" class="_debobigegoItem"> <slot :result="result"></slot> </div> - <div class="_formItem" v-else> - <div class="_formPanel eiurkvay"> + <div class="_debobigegoItem" v-else> + <div class="_debobigegoPanel eiurkvay"> <div><i class="fas fa-exclamation-triangle"></i> {{ $ts.somethingHappened }}</div> <MkButton inline @click="retry" class="retry"><i class="fas fa-redo-alt"></i> {{ $ts.retry }}</MkButton> </div> @@ -19,7 +19,7 @@ <script lang="ts"> import { defineComponent, PropType, ref, watch } from 'vue'; -import './form.scss'; +import './debobigego.scss'; import MkButton from '@client/components/ui/button.vue'; export default defineComponent({ diff --git a/src/client/components/debobigego/switch.vue b/src/client/components/debobigego/switch.vue new file mode 100644 index 0000000000..0ee0881236 --- /dev/null +++ b/src/client/components/debobigego/switch.vue @@ -0,0 +1,132 @@ +<template> +<div class="ijnpvmgr _debobigegoItem"> + <div class="main _debobigegoPanel _debobigegoClickable" + :class="{ disabled, checked }" + :aria-checked="checked" + :aria-disabled="disabled" + @click.prevent="toggle" + > + <input + type="checkbox" + ref="input" + :disabled="disabled" + @keydown.enter="toggle" + > + <span class="button" v-tooltip="checked ? $ts.itsOn : $ts.itsOff"> + <span></span> + </span> + <span class="label"> + <span><slot></slot></span> + </span> + </div> + <div class="_debobigegoCaption"><slot name="desc"></slot></div> +</div> +</template> + +<script lang="ts"> +import { defineComponent } from 'vue'; +import './debobigego.scss'; + +export default defineComponent({ + props: { + modelValue: { + type: Boolean, + default: false + }, + disabled: { + type: Boolean, + default: false + } + }, + computed: { + checked(): boolean { + return this.modelValue; + } + }, + methods: { + toggle() { + if (this.disabled) return; + this.$emit('update:modelValue', !this.checked); + } + } +}); +</script> + +<style lang="scss" scoped> +.ijnpvmgr { + > .main { + position: relative; + display: flex; + padding: 14px 16px; + cursor: pointer; + + > * { + user-select: none; + } + + &.disabled { + opacity: 0.6; + cursor: not-allowed; + } + + &.checked { + > .button { + background-color: var(--X10); + border-color: var(--X10); + + > * { + background-color: var(--accent); + transform: translateX(14px); + } + } + } + + > input { + position: absolute; + width: 0; + height: 0; + opacity: 0; + margin: 0; + } + + > .button { + position: relative; + display: inline-block; + flex-shrink: 0; + margin: 3px 0 0 0; + width: 34px; + height: 14px; + background: var(--X6); + outline: none; + border-radius: 14px; + transition: all 0.3s; + cursor: pointer; + + > * { + position: absolute; + top: -3px; + left: 0; + border-radius: 100%; + transition: background-color 0.3s, transform 0.3s; + width: 20px; + height: 20px; + background-color: #fff; + box-shadow: 0 2px 1px -1px rgba(#000, 0.2), 0 1px 1px 0 rgba(#000, 0.14), 0 1px 3px 0 rgba(#000, 0.12); + } + } + + > .label { + margin-left: 12px; + display: block; + transition: inherit; + color: var(--fg); + + > span { + display: block; + line-height: 20px; + transition: inherit; + } + } + } +} +</style> diff --git a/src/client/components/debobigego/textarea.vue b/src/client/components/debobigego/textarea.vue new file mode 100644 index 0000000000..64e8d47126 --- /dev/null +++ b/src/client/components/debobigego/textarea.vue @@ -0,0 +1,161 @@ +<template> +<FormGroup class="_debobigegoItem"> + <template #label><slot></slot></template> + <div class="rivhosbp _debobigegoItem" :class="{ tall, pre }"> + <div class="input _debobigegoPanel"> + <textarea ref="input" :class="{ code, _monospace: code }" + v-model="v" + :required="required" + :readonly="readonly" + :pattern="pattern" + :autocomplete="autocomplete" + :spellcheck="!code" + @input="onInput" + @focus="focused = true" + @blur="focused = false" + ></textarea> + </div> + </div> + <template #caption><slot name="desc"></slot></template> + + <FormButton v-if="manualSave && changed" @click="updated" primary><i class="fas fa-save"></i> {{ $ts.save }}</FormButton> +</FormGroup> +</template> + +<script lang="ts"> +import { defineComponent, ref, toRefs, watch } from 'vue'; +import './debobigego.scss'; +import FormButton from './button.vue'; +import FormGroup from './group.vue'; + +export default defineComponent({ + components: { + FormGroup, + FormButton, + }, + props: { + modelValue: { + required: false + }, + required: { + type: Boolean, + required: false + }, + readonly: { + type: Boolean, + required: false + }, + pattern: { + type: String, + required: false + }, + autocomplete: { + type: String, + required: false + }, + code: { + type: Boolean, + required: false + }, + tall: { + type: Boolean, + required: false, + default: false + }, + pre: { + type: Boolean, + required: false, + default: false + }, + manualSave: { + type: Boolean, + required: false, + default: false + }, + }, + setup(props, context) { + const { modelValue } = toRefs(props); + const v = ref(modelValue.value); + const changed = ref(false); + const inputEl = ref(null); + const focus = () => inputEl.value.focus(); + const onInput = (ev) => { + changed.value = true; + context.emit('change', ev); + }; + + const updated = () => { + changed.value = false; + context.emit('update:modelValue', v.value); + }; + + watch(modelValue.value, newValue => { + v.value = newValue; + }); + + watch(v, newValue => { + if (!props.manualSave) { + updated(); + } + }); + + return { + v, + updated, + changed, + focus, + onInput, + }; + } +}); +</script> + +<style lang="scss" scoped> +.rivhosbp { + position: relative; + + > .input { + position: relative; + + > textarea { + display: block; + width: 100%; + min-width: 100%; + max-width: 100%; + min-height: 130px; + margin: 0; + padding: 16px; + box-sizing: border-box; + font: inherit; + font-weight: normal; + font-size: 1em; + background: transparent; + border: none; + border-radius: 0; + outline: none; + box-shadow: none; + color: var(--fg); + + &.code { + tab-size: 2; + } + } + } + + &.tall { + > .input { + > textarea { + min-height: 200px; + } + } + } + + &.pre { + > .input { + > textarea { + white-space: pre; + } + } + } +} +</style> diff --git a/src/client/components/form/tuple.vue b/src/client/components/debobigego/tuple.vue similarity index 87% rename from src/client/components/form/tuple.vue rename to src/client/components/debobigego/tuple.vue index 6c8a22d189..8a4599fd64 100644 --- a/src/client/components/form/tuple.vue +++ b/src/client/components/debobigego/tuple.vue @@ -1,5 +1,5 @@ <template> -<div class="wthhikgt _formItem" v-size="{ max: [500] }"> +<div class="wthhikgt _debobigegoItem" v-size="{ max: [500] }"> <slot></slot> </div> </template> diff --git a/src/client/components/dialog.vue b/src/client/components/dialog.vue index f3611f050e..dd4932f61f 100644 --- a/src/client/components/dialog.vue +++ b/src/client/components/dialog.vue @@ -40,8 +40,8 @@ import { defineComponent } from 'vue'; import MkModal from '@client/components/ui/modal.vue'; import MkButton from '@client/components/ui/button.vue'; -import MkInput from '@client/components/ui/input.vue'; -import MkSelect from '@client/components/ui/select.vue'; +import MkInput from '@client/components/form/input.vue'; +import MkSelect from '@client/components/form/select.vue'; export default defineComponent({ components: { diff --git a/src/client/components/forgot-password.vue b/src/client/components/forgot-password.vue index 3b5ad6d6ba..cb2380f483 100644 --- a/src/client/components/forgot-password.vue +++ b/src/client/components/forgot-password.vue @@ -35,7 +35,7 @@ import { defineComponent } from 'vue'; import XModalWindow from '@client/components/ui/modal-window.vue'; import MkButton from '@client/components/ui/button.vue'; -import MkInput from '@client/components/ui/input.vue'; +import MkInput from '@client/components/form/input.vue'; import * as os from '@client/os'; export default defineComponent({ diff --git a/src/client/components/form-dialog.vue b/src/client/components/form-dialog.vue index e13592b488..6353b7287e 100644 --- a/src/client/components/form-dialog.vue +++ b/src/client/components/form-dialog.vue @@ -14,23 +14,23 @@ </template> <FormBase class="xkpnjxcv"> <template v-for="item in Object.keys(form).filter(item => !form[item].hidden)"> - <FormInput v-if="form[item].type === 'number'" v-model:value="values[item]" type="number" :step="form[item].step || 1"> + <FormInput v-if="form[item].type === 'number'" v-model="values[item]" type="number" :step="form[item].step || 1"> <span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ $ts.optional }})</span> <template v-if="form[item].description" #desc>{{ form[item].description }}</template> </FormInput> - <FormInput v-else-if="form[item].type === 'string' && !form[item].multiline" v-model:value="values[item]" type="text"> + <FormInput v-else-if="form[item].type === 'string' && !form[item].multiline" v-model="values[item]" type="text"> <span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ $ts.optional }})</span> <template v-if="form[item].description" #desc>{{ form[item].description }}</template> </FormInput> - <FormTextarea v-else-if="form[item].type === 'string' && form[item].multiline" v-model:value="values[item]"> + <FormTextarea v-else-if="form[item].type === 'string' && form[item].multiline" v-model="values[item]"> <span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ $ts.optional }})</span> <template v-if="form[item].description" #desc>{{ form[item].description }}</template> </FormTextarea> - <FormSwitch v-else-if="form[item].type === 'boolean'" v-model:value="values[item]"> + <FormSwitch v-else-if="form[item].type === 'boolean'" v-model="values[item]"> <span v-text="form[item].label || item"></span> <template v-if="form[item].description" #desc>{{ form[item].description }}</template> </FormSwitch> - <FormSelect v-else-if="form[item].type === 'enum'" v-model:value="values[item]"> + <FormSelect v-else-if="form[item].type === 'enum'" v-model="values[item]"> <template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ $ts.optional }})</span></template> <option v-for="item in form[item].enum" :value="item.value" :key="item.value">{{ item.label }}</option> </FormSelect> @@ -38,7 +38,7 @@ <template #desc><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ $ts.optional }})</span></template> <option v-for="item in form[item].options" :value="item.value" :key="item.value">{{ item.label }}</option> </FormRadios> - <FormRange v-else-if="form[item].type === 'range'" v-model:value="values[item]" :min="form[item].mim" :max="form[item].max" :step="form[item].step"> + <FormRange v-else-if="form[item].type === 'range'" v-model="values[item]" :min="form[item].mim" :max="form[item].max" :step="form[item].step"> <template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ $ts.optional }})</span></template> <template v-if="form[item].description" #desc>{{ form[item].description }}</template> </FormRange> @@ -53,14 +53,14 @@ <script lang="ts"> import { defineComponent } from 'vue'; import XModalWindow from '@client/components/ui/modal-window.vue'; -import FormBase from './form/base.vue'; -import FormInput from './form/input.vue'; -import FormTextarea from './form/textarea.vue'; -import FormSwitch from './form/switch.vue'; -import FormSelect from './form/select.vue'; -import FormRange from './form/range.vue'; -import FormButton from './form/button.vue'; -import FormRadios from './form/radios.vue'; +import FormBase from './debobigego/base.vue'; +import FormInput from './debobigego/input.vue'; +import FormTextarea from './debobigego/textarea.vue'; +import FormSwitch from './debobigego/switch.vue'; +import FormSelect from './debobigego/select.vue'; +import FormRange from './debobigego/range.vue'; +import FormButton from './debobigego/button.vue'; +import FormRadios from './debobigego/radios.vue'; export default defineComponent({ components: { diff --git a/src/client/components/form/input.vue b/src/client/components/form/input.vue index 942ac4dfd2..d7b6f77519 100644 --- a/src/client/components/form/input.vue +++ b/src/client/components/form/input.vue @@ -1,53 +1,49 @@ <template> -<FormGroup class="_formItem"> - <template #label><slot></slot></template> - <div class="ztzhwixg _formItem" :class="{ inline, disabled }"> - <div class="icon" ref="icon"><slot name="icon"></slot></div> - <div class="input _formPanel"> - <div class="prefix" ref="prefixEl"><slot name="prefix"></slot></div> - <input ref="inputEl" - :type="type" - v-model="v" - :disabled="disabled" - :required="required" - :readonly="readonly" - :placeholder="placeholder" - :pattern="pattern" - :autocomplete="autocomplete" - :spellcheck="spellcheck" - :step="step" - @focus="focused = true" - @blur="focused = false" - @keydown="onKeydown($event)" - @input="onInput" - :list="id" - > - <datalist :id="id" v-if="datalist"> - <option v-for="data in datalist" :value="data"/> - </datalist> - <div class="suffix" ref="suffixEl"><slot name="suffix"></slot></div> - </div> +<div class="matxzzsk"> + <div class="label" @click="focus"><slot name="label"></slot></div> + <div class="input" :class="{ inline, disabled, focused }"> + <div class="prefix" ref="prefixEl"><slot name="prefix"></slot></div> + <input ref="inputEl" + :type="type" + v-model="v" + :disabled="disabled" + :required="required" + :readonly="readonly" + :placeholder="placeholder" + :pattern="pattern" + :autocomplete="autocomplete" + :spellcheck="spellcheck" + :step="step" + @focus="focused = true" + @blur="focused = false" + @keydown="onKeydown($event)" + @input="onInput" + :list="id" + > + <datalist :id="id" v-if="datalist"> + <option v-for="data in datalist" :value="data"/> + </datalist> + <div class="suffix" ref="suffixEl"><slot name="suffix"></slot></div> </div> - <template #caption><slot name="desc"></slot></template> + <div class="caption"><slot name="caption"></slot></div> - <FormButton v-if="manualSave && changed" @click="updated" primary><i class="fas fa-save"></i> {{ $ts.save }}</FormButton> -</FormGroup> + <MkButton v-if="manualSave && changed" @click="updated" primary><i class="fas fa-save"></i> {{ $ts.save }}</MkButton> +</div> </template> <script lang="ts"> import { defineComponent, onMounted, onUnmounted, nextTick, ref, watch, computed, toRefs } from 'vue'; -import './form.scss'; -import FormButton from './button.vue'; -import FormGroup from './group.vue'; +import MkButton from '../ui/button.vue'; +import { debounce } from 'throttle-debounce'; export default defineComponent({ components: { - FormGroup, - FormButton, + MkButton, }, + props: { - value: { - required: false + modelValue: { + required: true }, type: { type: String, @@ -96,16 +92,23 @@ export default defineComponent({ required: false, default: false }, + debounce: { + type: Boolean, + required: false, + default: false + }, manualSave: { type: Boolean, required: false, default: false }, }, - emits: ['change', 'keydown', 'enter'], + + emits: ['change', 'keydown', 'enter', 'update:modelValue'], + setup(props, context) { - const { value, type, autofocus } = toRefs(props); - const v = ref(value.value); + const { modelValue, type, autofocus } = toRefs(props); + const v = ref(modelValue.value); const id = Math.random().toString(); // TODO: uuid? const focused = ref(false); const changed = ref(false); @@ -131,19 +134,25 @@ export default defineComponent({ const updated = () => { changed.value = false; if (type?.value === 'number') { - context.emit('update:value', parseFloat(v.value)); + context.emit('update:modelValue', parseFloat(v.value)); } else { - context.emit('update:value', v.value); + context.emit('update:modelValue', v.value); } }; - watch(value, newValue => { + const debouncedUpdated = debounce(1000, updated); + + watch(modelValue, newValue => { v.value = newValue; }); watch(v, newValue => { if (!props.manualSave) { - updated(); + if (props.debounce) { + debouncedUpdated(); + } else { + updated(); + } } invalid.value = inputEl.value.validity.badInput; @@ -196,59 +205,66 @@ export default defineComponent({ </script> <style lang="scss" scoped> -.ztzhwixg { - position: relative; +.matxzzsk { + > .label { + font-size: 0.85em; + padding: 0 0 8px 12px; + user-select: none; - > .icon { - position: absolute; - top: 0; - left: 0; - width: 24px; - text-align: center; - line-height: 32px; + &:empty { + display: none; + } + } - &:not(:empty) + .input { - margin-left: 28px; + > .caption { + font-size: 0.8em; + padding: 8px 0 0 12px; + color: var(--fgTransparentWeak); + + &:empty { + display: none; } } > .input { - $height: 48px; + $height: 42px; position: relative; > input { + appearance: none; + -webkit-appearance: none; display: block; height: $height; width: 100%; margin: 0; - padding: 0 16px; + padding: 0 12px; font: inherit; font-weight: normal; font-size: 1em; - line-height: $height; - color: var(--inputText); - background: transparent; - border: none; - border-radius: 0; + color: var(--fg); + background: var(--panel); + border: solid 0.5px var(--inputBorder); + border-radius: 6px; outline: none; box-shadow: none; box-sizing: border-box; + transition: border-color 0.1s ease-out; - &[type='file'] { - display: none; + &:hover { + border-color: var(--inputBorderHover); } } > .prefix, > .suffix { - display: block; + display: flex; + align-items: center; position: absolute; z-index: 1; top: 0; - padding: 0 16px; + padding: 0 12px; font-size: 1em; - line-height: $height; - color: var(--inputLabel); + height: $height; pointer-events: none; &:empty { @@ -267,25 +283,32 @@ export default defineComponent({ > .prefix { left: 0; - padding-right: 8px; + padding-right: 6px; } > .suffix { right: 0; - padding-left: 8px; + padding-left: 6px; } - } - &.inline { - display: inline-block; - margin: 0; - } + &.inline { + display: inline-block; + margin: 0; + } - &.disabled { - opacity: 0.7; + &.focused { + > input { + border-color: var(--accent); + //box-shadow: 0 0 0 4px var(--focus); + } + } - &, * { - cursor: not-allowed !important; + &.disabled { + opacity: 0.7; + + &, * { + cursor: not-allowed !important; + } } } } diff --git a/src/client/components/ui/radio.vue b/src/client/components/form/radio.vue similarity index 100% rename from src/client/components/ui/radio.vue rename to src/client/components/form/radio.vue diff --git a/src/client/components/form/radios.vue b/src/client/components/form/radios.vue index b660c37ace..1d3d80172a 100644 --- a/src/client/components/form/radios.vue +++ b/src/client/components/form/radios.vue @@ -1,7 +1,6 @@ <script lang="ts"> import { defineComponent, h } from 'vue'; -import MkRadio from '@client/components/ui/radio.vue'; -import './form.scss'; +import MkRadio from './radio.vue'; export default defineComponent({ components: { @@ -18,9 +17,6 @@ export default defineComponent({ } }, watch: { - modelValue() { - this.value = this.modelValue; - }, value() { this.$emit('update:modelValue', this.value); } @@ -33,80 +29,38 @@ export default defineComponent({ if (options.length === 1 && options[0].props == null) options = options[0].children; return h('div', { - class: 'cnklmpwm _formItem' + class: 'novjtcto' }, [ - h('div', { - class: '_formLabel', - }, label), - ...options.map(option => h('button', { - class: '_button _formPanel _formClickable', + h('div', { class: 'label' }, label), + ...options.map(option => h(MkRadio, { key: option.key, - onClick: () => this.value = option.props.value, - }, [h('span', { - class: ['check', { checked: this.value === option.props.value }], - }), option.children])) + value: option.props.value, + modelValue: this.value, + 'onUpdate:modelValue': value => this.value = value, + }, option.children)) ]); } }); </script> <style lang="scss"> -.cnklmpwm { - > button { - display: block; - width: 100%; - box-sizing: border-box; - padding: 14px 18px; - text-align: left; +.novjtcto { + > .label { + font-size: 0.85em; + padding: 0 0 8px 12px; + user-select: none; - &:not(:first-of-type) { - border-top: none !important; - border-top-left-radius: 0; - border-top-right-radius: 0; + &:empty { + display: none; } + } - &:not(:last-of-type) { - border-bottom: solid 0.5px var(--divider); - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; - } + &:first-child { + margin-top: 0; + } - > .check { - display: inline-block; - vertical-align: bottom; - position: relative; - width: 16px; - height: 16px; - margin-right: 8px; - background: none; - border: 2px solid var(--inputBorder); - border-radius: 100%; - transition: inherit; - - &:after { - content: ""; - display: block; - position: absolute; - top: 3px; - right: 3px; - bottom: 3px; - left: 3px; - border-radius: 100%; - opacity: 0; - transform: scale(0); - transition: .4s cubic-bezier(.25,.8,.25,1); - } - - &.checked { - border-color: var(--accent); - - &:after { - background-color: var(--accent); - transform: scale(1); - opacity: 1; - } - } - } + &:last-child { + margin-bottom: 0; } } </style> diff --git a/src/client/components/form/range.vue b/src/client/components/form/range.vue index 65d665c70a..4cfe66a8fc 100644 --- a/src/client/components/form/range.vue +++ b/src/client/components/form/range.vue @@ -1,21 +1,20 @@ <template> -<div class="ifitouly _formItem" :class="{ focused, disabled }"> - <div class="_formLabel"><slot name="label"></slot></div> - <div class="_formPanel main"> - <input - type="range" - ref="input" - v-model="v" - :disabled="disabled" - :min="min" - :max="max" - :step="step" - @focus="focused = true" - @blur="focused = false" - @input="$emit('update:value', $event.target.value)" - /> - </div> - <div class="_formCaption"><slot name="caption"></slot></div> +<div class="timctyfi" :class="{ focused, disabled }"> + <div class="icon"><slot name="icon"></slot></div> + <span class="label"><slot name="label"></slot></span> + <input + type="range" + ref="input" + v-model="v" + :disabled="disabled" + :min="min" + :max="max" + :step="step" + :autofocus="autofocus" + @focus="focused = true" + @blur="focused = false" + @input="$emit('update:value', $event.target.value)" + /> </div> </template> @@ -49,6 +48,10 @@ export default defineComponent({ required: false, default: 1 }, + autofocus: { + type: Boolean, + required: false + } }, data() { return { @@ -61,61 +64,75 @@ export default defineComponent({ this.v = parseFloat(v); } }, + mounted() { + if (this.autofocus) { + this.$nextTick(() => { + this.$refs.input.focus(); + }); + } + } }); </script> <style lang="scss" scoped> -.ifitouly { +.timctyfi { position: relative; + margin: 8px; - > .main { - padding: 22px 16px; + > .icon { + display: inline-block; + width: 24px; + text-align: center; + } - > input { - display: block; + > .title { + pointer-events: none; + font-size: 16px; + color: var(--inputLabel); + overflow: hidden; + } + + > input { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background: var(--X10); + height: 7px; + margin: 0 8px; + outline: 0; + border: 0; + border-radius: 7px; + + &.disabled { + opacity: 0.6; + cursor: not-allowed; + } + + &::-webkit-slider-thumb { -webkit-appearance: none; + appearance: none; + cursor: pointer; + width: 20px; + height: 20px; + display: block; + border-radius: 50%; + border: none; + background: var(--accent); + box-shadow: 0 0 6px rgba(0, 0, 0, 0.3); + box-sizing: content-box; + } + + &::-moz-range-thumb { -moz-appearance: none; appearance: none; - background: var(--X10); - height: 4px; - width: 100%; - box-sizing: border-box; - margin: 0; - outline: 0; - border: 0; - border-radius: 7px; - - &.disabled { - opacity: 0.6; - cursor: not-allowed; - } - - &::-webkit-slider-thumb { - -webkit-appearance: none; - appearance: none; - cursor: pointer; - width: 20px; - height: 20px; - display: block; - border-radius: 50%; - border: none; - background: var(--accent); - box-shadow: 0 0 6px rgba(0, 0, 0, 0.3); - box-sizing: content-box; - } - - &::-moz-range-thumb { - -moz-appearance: none; - appearance: none; - cursor: pointer; - width: 20px; - height: 20px; - display: block; - border-radius: 50%; - border: none; - background: var(--accent); - box-shadow: 0 0 6px rgba(0, 0, 0, 0.3); - } + cursor: pointer; + width: 20px; + height: 20px; + display: block; + border-radius: 50%; + border: none; + background: var(--accent); + box-shadow: 0 0 6px rgba(0, 0, 0, 0.3); } } } diff --git a/src/client/components/form/section.vue b/src/client/components/form/section.vue new file mode 100644 index 0000000000..8eac40a0db --- /dev/null +++ b/src/client/components/form/section.vue @@ -0,0 +1,31 @@ +<template> +<div class="vrtktovh" v-size="{ max: [500] }" v-sticky-container> + <div class="label"><slot name="label"></slot></div> + <div class="main"> + <slot></slot> + </div> +</div> +</template> + +<script lang="ts"> +import { defineComponent } from 'vue'; + +export default defineComponent({ + +}); +</script> + +<style lang="scss" scoped> +.vrtktovh { + border-top: solid 0.5px var(--divider); + + > .label { + font-weight: bold; + padding: 24px 0 16px 0; + } + + > .main { + margin-bottom: 32px; + } +} +</style> diff --git a/src/client/components/form/select.vue b/src/client/components/form/select.vue index 1c5a473451..257e2cc990 100644 --- a/src/client/components/form/select.vue +++ b/src/client/components/form/select.vue @@ -1,125 +1,216 @@ <template> -<div class="yrtfrpux _formItem" :class="{ disabled, inline }"> - <div class="_formLabel"><slot name="label"></slot></div> - <div class="icon" ref="icon"><slot name="icon"></slot></div> - <div class="input _formPanel _formClickable" @click="focus"> - <div class="prefix" ref="prefix"><slot name="prefix"></slot></div> - <select ref="input" +<div class="vblkjoeq"> + <div class="label" @click="focus"><slot name="label"></slot></div> + <div class="input" :class="{ inline, disabled, focused }"> + <div class="prefix" ref="prefixEl"><slot name="prefix"></slot></div> + <select ref="inputEl" v-model="v" - :required="required" :disabled="disabled" + :required="required" + :readonly="readonly" + :placeholder="placeholder" @focus="focused = true" @blur="focused = false" + @input="onInput" > <slot></slot> </select> - <div class="suffix"> - <i class="fas fa-chevron-down"></i> - </div> + <div class="suffix" ref="suffixEl"><i class="fas fa-chevron-down"></i></div> </div> - <div class="_formCaption"><slot name="caption"></slot></div> + <div class="caption"><slot name="caption"></slot></div> + + <MkButton v-if="manualSave && changed" @click="updated" primary><i class="fas fa-save"></i> {{ $ts.save }}</MkButton> </div> </template> <script lang="ts"> -import { defineComponent } from 'vue'; -import './form.scss'; +import { defineComponent, onMounted, onUnmounted, nextTick, ref, watch, computed, toRefs } from 'vue'; +import MkButton from '../ui/button.vue'; export default defineComponent({ + components: { + MkButton, + }, + props: { - value: { - required: false + modelValue: { + required: true }, required: { type: Boolean, required: false }, + readonly: { + type: Boolean, + required: false + }, disabled: { type: Boolean, required: false }, + placeholder: { + type: String, + required: false + }, + autofocus: { + type: Boolean, + required: false, + default: false + }, inline: { type: Boolean, required: false, default: false }, - }, - data() { - return { - }; - }, - computed: { - v: { - get() { - return this.value; - }, - set(v) { - this.$emit('update:value', v); - } + manualSave: { + type: Boolean, + required: false, + default: false }, }, - methods: { - focus() { - this.$refs.input.focus(); - } - } + + emits: ['change', 'update:modelValue'], + + setup(props, context) { + const { modelValue, autofocus } = toRefs(props); + const v = ref(modelValue.value); + const focused = ref(false); + const changed = ref(false); + const invalid = ref(false); + const filled = computed(() => v.value !== '' && v.value != null); + const inputEl = ref(null); + const prefixEl = ref(null); + const suffixEl = ref(null); + + const focus = () => inputEl.value.focus(); + const onInput = (ev) => { + changed.value = true; + context.emit('change', ev); + }; + + const updated = () => { + changed.value = false; + context.emit('update:modelValue', v.value); + }; + + watch(modelValue, newValue => { + v.value = newValue; + }); + + watch(v, newValue => { + if (!props.manualSave) { + updated(); + } + + invalid.value = inputEl.value.validity.badInput; + }); + + onMounted(() => { + nextTick(() => { + if (autofocus.value) { + focus(); + } + + // このコンポーネントが作成された時、非表示状態である場合がある + // 非表示状態だと要素の幅などは0になってしまうので、定期的に計算する + const clock = setInterval(() => { + if (prefixEl.value) { + if (prefixEl.value.offsetWidth) { + inputEl.value.style.paddingLeft = prefixEl.value.offsetWidth + 'px'; + } + } + if (suffixEl.value) { + if (suffixEl.value.offsetWidth) { + inputEl.value.style.paddingRight = suffixEl.value.offsetWidth + 'px'; + } + } + }, 100); + + onUnmounted(() => { + clearInterval(clock); + }); + }); + }); + + return { + v, + focused, + invalid, + changed, + filled, + inputEl, + prefixEl, + suffixEl, + focus, + onInput, + updated, + }; + }, }); </script> <style lang="scss" scoped> -.yrtfrpux { - position: relative; +.vblkjoeq { + > .label { + font-size: 0.85em; + padding: 0 0 8px 12px; + user-select: none; - > .icon { - position: absolute; - top: 0; - left: 0; - width: 24px; - text-align: center; - line-height: 32px; + &:empty { + display: none; + } + } - &:not(:empty) + .input { - margin-left: 28px; + > .caption { + font-size: 0.8em; + padding: 8px 0 0 12px; + color: var(--fgTransparentWeak); + + &:empty { + display: none; } } > .input { - display: flex; + $height: 42px; position: relative; > select { + appearance: none; + -webkit-appearance: none; display: block; - flex: 1; + height: $height; width: 100%; - padding: 0 16px; + margin: 0; + padding: 0 12px; font: inherit; font-weight: normal; font-size: 1em; - height: 48px; - background: none; - border: none; - border-radius: 0; + color: var(--fg); + background: var(--panel); + border: solid 1px var(--inputBorder); + border-radius: 6px; outline: none; box-shadow: none; - appearance: none; - -webkit-appearance: none; - color: var(--fg); + box-sizing: border-box; + cursor: pointer; + transition: border-color 0.1s ease-out; - option, - optgroup { - color: var(--fg); - background: var(--bg); + &:hover { + border-color: var(--inputBorderHover); } } > .prefix, > .suffix { - display: block; - align-self: center; - justify-self: center; + display: flex; + align-items: center; + position: absolute; + z-index: 1; + top: 0; + padding: 0 12px; font-size: 1em; - line-height: 32px; - color: var(--inputLabel); + height: $height; pointer-events: none; &:empty { @@ -127,18 +218,42 @@ export default defineComponent({ } > * { - display: block; + display: inline-block; min-width: 16px; + max-width: 150px; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; } } > .prefix { - padding-right: 4px; + left: 0; + padding-right: 6px; } > .suffix { - padding: 0 16px 0 0; + right: 0; + padding-left: 6px; + } + + &.inline { + display: inline-block; + margin: 0; + } + + &.focused { + > select { + border-color: var(--accent); + } + } + + &.disabled { opacity: 0.7; + + &, * { + cursor: not-allowed !important; + } } } } diff --git a/src/client/components/form/slot.vue b/src/client/components/form/slot.vue new file mode 100644 index 0000000000..8580c1307d --- /dev/null +++ b/src/client/components/form/slot.vue @@ -0,0 +1,50 @@ +<template> +<div class="adhpbeou"> + <div class="label" @click="focus"><slot name="label"></slot></div> + <div class="content"> + <slot></slot> + </div> + <div class="caption"><slot name="caption"></slot></div> +</div> +</template> + +<script lang="ts"> +import { defineComponent } from 'vue'; + +export default defineComponent({ + +}); +</script> + +<style lang="scss" scoped> +.adhpbeou { + margin: 1.5em 0; + + > .label { + font-size: 0.85em; + padding: 0 0 8px 12px; + user-select: none; + + &:empty { + display: none; + } + } + + > .caption { + font-size: 0.8em; + padding: 8px 0 0 12px; + color: var(--fgTransparentWeak); + + &:empty { + display: none; + } + } + + > .content { + position: relative; + background: var(--panel); + border: solid 0.5px var(--inputBorder); + border-radius: 6px; + } +} +</style> diff --git a/src/client/components/form/switch.vue b/src/client/components/form/switch.vue index e7ef714c49..85f8b7c870 100644 --- a/src/client/components/form/switch.vue +++ b/src/client/components/form/switch.vue @@ -1,35 +1,34 @@ <template> -<div class="ijnpvmgr _formItem"> - <div class="main _formPanel _formClickable" - :class="{ disabled, checked }" - :aria-checked="checked" - :aria-disabled="disabled" - @click.prevent="toggle" +<div + class="ziffeoms" + :class="{ disabled, checked }" + role="switch" + :aria-checked="checked" + :aria-disabled="disabled" + @click.prevent="toggle" +> + <input + type="checkbox" + ref="input" + :disabled="disabled" + @keydown.enter="toggle" > - <input - type="checkbox" - ref="input" - :disabled="disabled" - @keydown.enter="toggle" - > - <span class="button"> - <span></span> - </span> - <span class="label"> - <span><slot></slot></span> - </span> - </div> - <div class="_formCaption"><slot name="desc"></slot></div> + <span class="button" v-tooltip="checked ? $ts.itsOn : $ts.itsOff"> + <span class="handle"></span> + </span> + <span class="label"> + <span><slot></slot></span> + <p><slot name="caption"></slot></p> + </span> </div> </template> <script lang="ts"> import { defineComponent } from 'vue'; -import './form.scss'; export default defineComponent({ props: { - value: { + modelValue: { type: Boolean, default: false }, @@ -40,91 +39,110 @@ export default defineComponent({ }, computed: { checked(): boolean { - return this.value; + return this.modelValue; } }, methods: { toggle() { if (this.disabled) return; - this.$emit('update:value', !this.checked); + this.$emit('update:modelValue', !this.checked); } } }); </script> <style lang="scss" scoped> -.ijnpvmgr { - > .main { +.ziffeoms { + position: relative; + display: flex; + cursor: pointer; + transition: all 0.3s; + + &:first-child { + margin-top: 0; + } + + &:last-child { + margin-bottom: 0; + } + + > * { + user-select: none; + } + + > input { + position: absolute; + width: 0; + height: 0; + opacity: 0; + margin: 0; + } + + > .button { position: relative; - display: flex; - padding: 14px 16px; - cursor: pointer; + display: inline-block; + flex-shrink: 0; + margin: 0; + width: 36px; + height: 26px; + background: var(--switchBg); + outline: none; + border-radius: 999px; + transition: inherit; - > * { - user-select: none; - } - - &.disabled { - opacity: 0.6; - cursor: not-allowed; - } - - &.checked { - > .button { - background-color: var(--X10); - border-color: var(--X10); - - > * { - background-color: var(--accent); - transform: translateX(14px); - } - } - } - - > input { + > .handle { position: absolute; - width: 0; - height: 0; - opacity: 0; - margin: 0; + top: 0; + bottom: 0; + left: 5px; + margin: auto 0; + border-radius: 100%; + transition: background-color 0.3s, transform 0.3s; + width: 16px; + height: 16px; + background-color: #fff; } + } - > .button { - position: relative; - display: inline-block; - flex-shrink: 0; - margin: 3px 0 0 0; - width: 34px; - height: 14px; - background: var(--X6); - outline: none; - border-radius: 14px; - transition: all 0.3s; - cursor: pointer; + > .label { + margin-left: 16px; + margin-top: 2px; + display: block; + cursor: pointer; + transition: inherit; + color: var(--fg); - > * { - position: absolute; - top: -3px; - left: 0; - border-radius: 100%; - transition: background-color 0.3s, transform 0.3s; - width: 20px; - height: 20px; - background-color: #fff; - box-shadow: 0 2px 1px -1px rgba(#000, 0.2), 0 1px 1px 0 rgba(#000, 0.14), 0 1px 3px 0 rgba(#000, 0.12); - } - } - - > .label { - margin-left: 12px; + > span { display: block; + line-height: 20px; transition: inherit; - color: var(--fg); + } - > span { - display: block; - line-height: 20px; - transition: inherit; + > p { + margin: 0; + color: var(--fgTransparentWeak); + font-size: 90%; + } + } + + &:hover { + > .button { + background-color: var(--accentedBg); + } + } + + &.disabled { + opacity: 0.6; + cursor: not-allowed; + } + + &.checked { + > .button { + background-color: var(--accent); + border-color: var(--accent); + + > .handle { + transform: translateX(10px); } } } diff --git a/src/client/components/form/textarea.vue b/src/client/components/form/textarea.vue index 8f42581a9b..50be69f930 100644 --- a/src/client/components/form/textarea.vue +++ b/src/client/components/form/textarea.vue @@ -1,40 +1,45 @@ <template> -<FormGroup class="_formItem"> - <template #label><slot></slot></template> - <div class="rivhosbp _formItem" :class="{ tall, pre }"> - <div class="input _formPanel"> - <textarea ref="input" :class="{ code, _monospace: code }" - v-model="v" - :required="required" - :readonly="readonly" - :pattern="pattern" - :autocomplete="autocomplete" - :spellcheck="!code" - @input="onInput" - @focus="focused = true" - @blur="focused = false" - ></textarea> - </div> +<div class="adhpbeos"> + <div class="label" @click="focus"><slot name="label"></slot></div> + <div class="input" :class="{ disabled, focused, tall, pre }"> + <textarea ref="inputEl" + :class="{ code, _monospace: code }" + v-model="v" + :disabled="disabled" + :required="required" + :readonly="readonly" + :placeholder="placeholder" + :pattern="pattern" + :autocomplete="autocomplete" + :spellcheck="spellcheck" + @focus="focused = true" + @blur="focused = false" + @keydown="onKeydown($event)" + @input="onInput" + ></textarea> </div> - <template #caption><slot name="desc"></slot></template> + <div class="caption"><slot name="caption"></slot></div> - <FormButton v-if="manualSave && changed" @click="updated" primary><i class="fas fa-save"></i> {{ $ts.save }}</FormButton> -</FormGroup> + <MkButton v-if="manualSave && changed" @click="updated" primary><i class="fas fa-save"></i> {{ $ts.save }}</MkButton> +</div> </template> <script lang="ts"> -import { defineComponent, ref, toRefs, watch } from 'vue'; -import './form.scss'; -import FormButton from './button.vue'; -import FormGroup from './group.vue'; +import { defineComponent, onMounted, onUnmounted, nextTick, ref, watch, computed, toRefs } from 'vue'; +import MkButton from '../ui/button.vue'; +import { debounce } from 'throttle-debounce'; export default defineComponent({ components: { - FormGroup, - FormButton, + MkButton, }, + props: { - value: { + modelValue: { + required: true + }, + type: { + type: String, required: false }, required: { @@ -45,14 +50,29 @@ export default defineComponent({ type: Boolean, required: false }, + disabled: { + type: Boolean, + required: false + }, pattern: { type: String, required: false }, - autocomplete: { + placeholder: { type: String, required: false }, + autofocus: { + type: Boolean, + required: false, + default: false + }, + autocomplete: { + required: false + }, + spellcheck: { + required: false + }, code: { type: Boolean, required: false @@ -67,91 +87,162 @@ export default defineComponent({ required: false, default: false }, + debounce: { + type: Boolean, + required: false, + default: false + }, manualSave: { type: Boolean, required: false, default: false }, }, + + emits: ['change', 'keydown', 'enter', 'update:modelValue'], + setup(props, context) { - const { value } = toRefs(props); - const v = ref(value.value); + const { modelValue, autofocus } = toRefs(props); + const v = ref(modelValue.value); + const focused = ref(false); const changed = ref(false); + const invalid = ref(false); + const filled = computed(() => v.value !== '' && v.value != null); const inputEl = ref(null); + const focus = () => inputEl.value.focus(); const onInput = (ev) => { changed.value = true; context.emit('change', ev); }; + const onKeydown = (ev: KeyboardEvent) => { + context.emit('keydown', ev); + + if (ev.code === 'Enter') { + context.emit('enter'); + } + }; const updated = () => { changed.value = false; - context.emit('update:value', v.value); + context.emit('update:modelValue', v.value); }; - watch(value, newValue => { + const debouncedUpdated = debounce(1000, updated); + + watch(modelValue, newValue => { v.value = newValue; }); watch(v, newValue => { if (!props.manualSave) { - updated(); + if (props.debounce) { + debouncedUpdated(); + } else { + updated(); + } } + + invalid.value = inputEl.value.validity.badInput; }); - + + onMounted(() => { + nextTick(() => { + if (autofocus.value) { + focus(); + } + }); + }); + return { v, - updated, + focused, + invalid, changed, + filled, + inputEl, focus, onInput, + onKeydown, + updated, }; - } + }, }); </script> <style lang="scss" scoped> -.rivhosbp { - position: relative; +.adhpbeos { + > .label { + font-size: 0.85em; + padding: 0 0 8px 12px; + user-select: none; + + &:empty { + display: none; + } + } + + > .caption { + font-size: 0.8em; + padding: 8px 0 0 12px; + color: var(--fgTransparentWeak); + + &:empty { + display: none; + } + } > .input { position: relative; - + > textarea { + appearance: none; + -webkit-appearance: none; display: block; width: 100%; min-width: 100%; max-width: 100%; min-height: 130px; margin: 0; - padding: 16px; - box-sizing: border-box; + padding: 12px; font: inherit; font-weight: normal; font-size: 1em; - background: transparent; - border: none; - border-radius: 0; + color: var(--fg); + background: var(--panel); + border: solid 0.5px var(--inputBorder); + border-radius: 6px; outline: none; box-shadow: none; - color: var(--fg); + box-sizing: border-box; + transition: border-color 0.1s ease-out; - &.code { - tab-size: 2; + &:hover { + border-color: var(--inputBorderHover); } } - } - &.tall { - > .input { + &.focused { + > textarea { + border-color: var(--accent); + } + } + + &.disabled { + opacity: 0.7; + + &, * { + cursor: not-allowed !important; + } + } + + &.tall { > textarea { min-height: 200px; } } - } - &.pre { - > .input { + &.pre { > textarea { white-space: pre; } diff --git a/src/client/components/instance-stats.vue b/src/client/components/instance-stats.vue index 78044f0b16..5e7c71ea65 100644 --- a/src/client/components/instance-stats.vue +++ b/src/client/components/instance-stats.vue @@ -36,7 +36,7 @@ <script lang="ts"> import { defineComponent, markRaw } from 'vue'; import Chart from 'chart.js'; -import MkSelect from './ui/select.vue'; +import MkSelect from './form/select.vue'; import number from '@client/filters/number'; const sum = (...arr) => arr.reduce((r, a) => r.map((b, i) => a[i] + b)); diff --git a/src/client/components/note-detailed.vue b/src/client/components/note-detailed.vue index e7f116d1fd..68e7c87f2e 100644 --- a/src/client/components/note-detailed.vue +++ b/src/client/components/note-detailed.vue @@ -59,7 +59,7 @@ <div class="body"> <p v-if="appearNote.cw != null" class="cw"> <Mfm v-if="appearNote.cw != ''" class="text" :text="appearNote.cw" :author="appearNote.user" :i="$i" :custom-emojis="appearNote.emojis"/> - <XCwButton v-model:value="showContent" :note="appearNote"/> + <XCwButton v-model="showContent" :note="appearNote"/> </p> <div class="content" v-show="appearNote.cw == null || showContent"> <div class="text"> diff --git a/src/client/components/note-preview.vue b/src/client/components/note-preview.vue index 4248c2bb1d..406a475cd9 100644 --- a/src/client/components/note-preview.vue +++ b/src/client/components/note-preview.vue @@ -6,7 +6,7 @@ <div class="body"> <p v-if="note.cw != null" class="cw"> <span class="text" v-if="note.cw != ''">{{ note.cw }}</span> - <XCwButton v-model:value="showContent" :note="note"/> + <XCwButton v-model="showContent" :note="note"/> </p> <div class="content" v-show="note.cw == null || showContent"> <XSubNote-content class="text" :note="note"/> diff --git a/src/client/components/note.sub.vue b/src/client/components/note.sub.vue index 899c4b2f16..157b65ec5c 100644 --- a/src/client/components/note.sub.vue +++ b/src/client/components/note.sub.vue @@ -7,7 +7,7 @@ <div class="body"> <p v-if="note.cw != null" class="cw"> <Mfm v-if="note.cw != ''" class="text" :text="note.cw" :author="note.user" :i="$i" :custom-emojis="note.emojis" /> - <XCwButton v-model:value="showContent" :note="note"/> + <XCwButton v-model="showContent" :note="note"/> </p> <div class="content" v-show="note.cw == null || showContent"> <XSubNote-content class="text" :note="note"/> diff --git a/src/client/components/note.vue b/src/client/components/note.vue index 38b529dd91..9fa986836d 100644 --- a/src/client/components/note.vue +++ b/src/client/components/note.vue @@ -43,7 +43,7 @@ <div class="body"> <p v-if="appearNote.cw != null" class="cw"> <Mfm v-if="appearNote.cw != ''" class="text" :text="appearNote.cw" :author="appearNote.user" :i="$i" :custom-emojis="appearNote.emojis"/> - <XCwButton v-model:value="showContent" :note="appearNote"/> + <XCwButton v-model="showContent" :note="appearNote"/> </p> <div class="content" :class="{ collapsed }" v-show="appearNote.cw == null || showContent"> <div class="text"> diff --git a/src/client/components/notification-setting-window.vue b/src/client/components/notification-setting-window.vue index c33106ae15..0b41672170 100644 --- a/src/client/components/notification-setting-window.vue +++ b/src/client/components/notification-setting-window.vue @@ -29,7 +29,7 @@ <script lang="ts"> import { defineComponent, PropType } from 'vue'; import XModalWindow from '@client/components/ui/modal-window.vue'; -import MkSwitch from './ui/switch.vue'; +import MkSwitch from './form/switch.vue'; import MkInfo from './ui/info.vue'; import MkButton from './ui/button.vue'; import { notificationTypes } from '../../types'; diff --git a/src/client/components/page/page.number-input.vue b/src/client/components/page/page.number-input.vue index 9c4a537e15..5d9168f130 100644 --- a/src/client/components/page/page.number-input.vue +++ b/src/client/components/page/page.number-input.vue @@ -8,7 +8,7 @@ <script lang="ts"> import { computed, defineComponent, PropType } from 'vue'; -import MkInput from '../ui/input.vue'; +import MkInput from '../form/input.vue'; import * as os from '@client/os'; import { Hpml } from '@client/scripts/hpml/evaluator'; import { NumberInputVarBlock } from '@client/scripts/hpml/block'; diff --git a/src/client/components/page/page.post.vue b/src/client/components/page/page.post.vue index 7b061d8cda..c20d7cade1 100644 --- a/src/client/components/page/page.post.vue +++ b/src/client/components/page/page.post.vue @@ -10,7 +10,7 @@ <script lang="ts"> import { defineComponent, PropType } from 'vue'; -import MkTextarea from '../ui/textarea.vue'; +import MkTextarea from '../form/textarea.vue'; import MkButton from '../ui/button.vue'; import { apiUrl } from '@client/config'; import * as os from '@client/os'; diff --git a/src/client/components/page/page.radio-button.vue b/src/client/components/page/page.radio-button.vue index f6f146b52f..590e59d706 100644 --- a/src/client/components/page/page.radio-button.vue +++ b/src/client/components/page/page.radio-button.vue @@ -7,7 +7,7 @@ <script lang="ts"> import { computed, defineComponent, PropType } from 'vue'; -import MkRadio from '../ui/radio.vue'; +import MkRadio from '../form/radio.vue'; import * as os from '@client/os'; import { Hpml } from '@client/scripts/hpml/evaluator'; import { RadioButtonVarBlock } from '@client/scripts/hpml/block'; diff --git a/src/client/components/page/page.switch.vue b/src/client/components/page/page.switch.vue index 8818e6cbcf..4d74e5df39 100644 --- a/src/client/components/page/page.switch.vue +++ b/src/client/components/page/page.switch.vue @@ -6,7 +6,7 @@ <script lang="ts"> import { computed, defineComponent, PropType } from 'vue'; -import MkSwitch from '../ui/switch.vue'; +import MkSwitch from '../form/switch.vue'; import * as os from '@client/os'; import { Hpml } from '@client/scripts/hpml/evaluator'; import { SwitchVarBlock } from '@client/scripts/hpml/block'; diff --git a/src/client/components/page/page.text-input.vue b/src/client/components/page/page.text-input.vue index 752d3d7257..6e9ac0b543 100644 --- a/src/client/components/page/page.text-input.vue +++ b/src/client/components/page/page.text-input.vue @@ -8,7 +8,7 @@ <script lang="ts"> import { computed, defineComponent, PropType } from 'vue'; -import MkInput from '../ui/input.vue'; +import MkInput from '../form/input.vue'; import * as os from '@client/os'; import { Hpml } from '@client/scripts/hpml/evaluator'; import { TextInputVarBlock } from '@client/scripts/hpml/block'; diff --git a/src/client/components/page/page.textarea-input.vue b/src/client/components/page/page.textarea-input.vue index e6cf5117f9..dfcb398937 100644 --- a/src/client/components/page/page.textarea-input.vue +++ b/src/client/components/page/page.textarea-input.vue @@ -8,7 +8,7 @@ <script lang="ts"> import { computed, defineComponent, PropType } from 'vue'; -import MkTextarea from '../ui/textarea.vue'; +import MkTextarea from '../form/textarea.vue'; import * as os from '@client/os'; import { Hpml } from '@client/scripts/hpml/evaluator'; import { HpmlTextInput } from '@client/scripts/hpml'; diff --git a/src/client/components/page/page.textarea.vue b/src/client/components/page/page.textarea.vue index 974c7f2c57..cf953bf041 100644 --- a/src/client/components/page/page.textarea.vue +++ b/src/client/components/page/page.textarea.vue @@ -6,7 +6,7 @@ import { TextBlock } from '@client/scripts/hpml/block'; import { Hpml } from '@client/scripts/hpml/evaluator'; import { defineComponent, PropType } from 'vue'; -import MkTextarea from '../ui/textarea.vue'; +import MkTextarea from '../form/textarea.vue'; export default defineComponent({ components: { diff --git a/src/client/components/poll-editor.vue b/src/client/components/poll-editor.vue index 0a9a1c6a03..b28a1c8baa 100644 --- a/src/client/components/poll-editor.vue +++ b/src/client/components/poll-editor.vue @@ -51,9 +51,9 @@ import { defineComponent } from 'vue'; import { addTime } from '../../prelude/time'; import { formatDateTimeString } from '@/misc/format-time-string'; -import MkInput from './ui/input.vue'; -import MkSelect from './ui/select.vue'; -import MkSwitch from './ui/switch.vue'; +import MkInput from './form/input.vue'; +import MkSelect from './form/select.vue'; +import MkSwitch from './form/switch.vue'; import MkButton from './ui/button.vue'; export default defineComponent({ diff --git a/src/client/components/sample.vue b/src/client/components/sample.vue index bce02466f6..c8b46a80e7 100644 --- a/src/client/components/sample.vue +++ b/src/client/components/sample.vue @@ -30,10 +30,10 @@ <script lang="ts"> import { defineComponent } from 'vue'; import MkButton from '@client/components/ui/button.vue'; -import MkInput from '@client/components/ui/input.vue'; -import MkSwitch from '@client/components/ui/switch.vue'; -import MkTextarea from '@client/components/ui/textarea.vue'; -import MkRadio from '@client/components/ui/radio.vue'; +import MkInput from '@client/components/form/input.vue'; +import MkSwitch from '@client/components/form/switch.vue'; +import MkTextarea from '@client/components/form/textarea.vue'; +import MkRadio from '@client/components/form/radio.vue'; import * as os from '@client/os'; import * as config from '@client/config'; diff --git a/src/client/components/signin.vue b/src/client/components/signin.vue index 69f527b7d6..d6e1ee8b68 100755 --- a/src/client/components/signin.vue +++ b/src/client/components/signin.vue @@ -1,17 +1,17 @@ <template> <form class="eppvobhk _monolithic_" :class="{ signing, totpLogin }" @submit.prevent="onSubmit"> - <div class="auth _section"> + <div class="auth _section _formRoot"> <div class="avatar" :style="{ backgroundImage: user ? `url('${ user.avatarUrl }')` : null }" v-show="withAvatar"></div> <div class="normal-signin" v-if="!totpLogin"> - <MkInput v-model="username" :placeholder="$ts.username" type="text" pattern="^[a-zA-Z0-9_]+$" spellcheck="false" autofocus required @update:modelValue="onUsernameChange" data-cy-signin-username> + <MkInput class="_formBlock" v-model="username" :placeholder="$ts.username" type="text" pattern="^[a-zA-Z0-9_]+$" spellcheck="false" autofocus required @update:modelValue="onUsernameChange" data-cy-signin-username> <template #prefix>@</template> <template #suffix>@{{ host }}</template> </MkInput> - <MkInput v-model="password" :placeholder="$ts.password" type="password" :with-password-toggle="true" v-if="!user || user && !user.usePasswordLessLogin" required data-cy-signin-password> + <MkInput class="_formBlock" v-model="password" :placeholder="$ts.password" type="password" :with-password-toggle="true" v-if="!user || user && !user.usePasswordLessLogin" required data-cy-signin-password> <template #prefix><i class="fas fa-lock"></i></template> <template #caption><button class="_textButton" @click="resetPassword" type="button">{{ $ts.forgotPassword }}</button></template> </MkInput> - <MkButton type="submit" primary :disabled="signing" style="margin: 0 auto;">{{ signing ? $ts.loggingIn : $ts.login }}</MkButton> + <MkButton class="_formBlock" type="submit" primary :disabled="signing" style="margin: 0 auto;">{{ signing ? $ts.loggingIn : $ts.login }}</MkButton> </div> <div class="2fa-signin" v-if="totpLogin" :class="{ securityKeys: user && user.securityKeys }"> <div v-if="user && user.securityKeys" class="twofa-group tap-group"> @@ -49,7 +49,7 @@ import { defineComponent } from 'vue'; import { toUnicode } from 'punycode/'; import MkButton from '@client/components/ui/button.vue'; -import MkInput from '@client/components/ui/input.vue'; +import MkInput from '@client/components/form/input.vue'; import { apiUrl, host } from '@client/config'; import { byteify, hexify } from '@client/scripts/2fa'; import * as os from '@client/os'; diff --git a/src/client/components/signup.vue b/src/client/components/signup.vue index d332274111..f555c1df6d 100644 --- a/src/client/components/signup.vue +++ b/src/client/components/signup.vue @@ -1,11 +1,11 @@ <template> -<form class="qlvuhzng" @submit.prevent="onSubmit" :autocomplete="Math.random()"> +<form class="qlvuhzng _formRoot" @submit.prevent="onSubmit" :autocomplete="Math.random()"> <template v-if="meta"> - <MkInput class="_inputNoTopMargin" v-if="meta.disableRegistration" v-model="invitationCode" type="text" :autocomplete="Math.random()" spellcheck="false" required> + <MkInput class="_formBlock" v-if="meta.disableRegistration" v-model="invitationCode" type="text" :autocomplete="Math.random()" spellcheck="false" required> <template #label>{{ $ts.invitationCode }}</template> <template #prefix><i class="fas fa-key"></i></template> </MkInput> - <MkInput class="_inputNoTopMargin" v-model="username" type="text" pattern="^[a-zA-Z0-9_]{1,20}$" :autocomplete="Math.random()" spellcheck="false" required @update:modelValue="onChangeUsername" data-cy-signup-username> + <MkInput class="_formBlock" v-model="username" type="text" pattern="^[a-zA-Z0-9_]{1,20}$" :autocomplete="Math.random()" spellcheck="false" required @update:modelValue="onChangeUsername" data-cy-signup-username> <template #label>{{ $ts.username }} <div class="_button _help" v-tooltip:dialog="$ts.usernameInfo"><i class="far fa-question-circle"></i></div></template> <template #prefix>@</template> <template #suffix>@{{ host }}</template> @@ -19,7 +19,7 @@ <span v-if="usernameState == 'max-range'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.tooLong }}</span> </template> </MkInput> - <MkInput v-model="password" type="password" :autocomplete="Math.random()" required @update:modelValue="onChangePassword" data-cy-signup-password> + <MkInput class="_formBlock" v-model="password" type="password" :autocomplete="Math.random()" required @update:modelValue="onChangePassword" data-cy-signup-password> <template #label>{{ $ts.password }}</template> <template #prefix><i class="fas fa-lock"></i></template> <template #caption> @@ -28,7 +28,7 @@ <span v-if="passwordStrength == 'high'" style="color: var(--success)"><i class="fas fa-check fa-fw"></i> {{ $ts.strongPassword }}</span> </template> </MkInput> - <MkInput v-model="retypedPassword" type="password" :autocomplete="Math.random()" required @update:modelValue="onChangePasswordRetype" data-cy-signup-password-retype> + <MkInput class="_formBlock" v-model="retypedPassword" type="password" :autocomplete="Math.random()" required @update:modelValue="onChangePasswordRetype" data-cy-signup-password-retype> <template #label>{{ $ts.password }} ({{ $ts.retype }})</template> <template #prefix><i class="fas fa-lock"></i></template> <template #caption> @@ -36,7 +36,7 @@ <span v-if="passwordRetypeState == 'not-match'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.passwordNotMatched }}</span> </template> </MkInput> - <label v-if="meta.tosUrl" class="tou"> + <label v-if="meta.tosUrl" class="_formBlock tou"> <input type="checkbox" v-model="ToSAgreement"> <I18n :src="$ts.agreeTo"> <template #0> @@ -44,9 +44,9 @@ </template> </I18n> </label> - <captcha v-if="meta.enableHcaptcha" class="captcha" provider="hcaptcha" ref="hcaptcha" v-model:value="hCaptchaResponse" :sitekey="meta.hcaptchaSiteKey"/> - <captcha v-if="meta.enableRecaptcha" class="captcha" provider="recaptcha" ref="recaptcha" v-model:value="reCaptchaResponse" :sitekey="meta.recaptchaSiteKey"/> - <MkButton type="submit" :disabled="shouldDisableSubmitting" primary data-cy-signup-submit>{{ $ts.start }}</MkButton> + <captcha v-if="meta.enableHcaptcha" class="_formBlock captcha" provider="hcaptcha" ref="hcaptcha" v-model="hCaptchaResponse" :sitekey="meta.hcaptchaSiteKey"/> + <captcha v-if="meta.enableRecaptcha" class="_formBlock captcha" provider="recaptcha" ref="recaptcha" v-model="reCaptchaResponse" :sitekey="meta.recaptchaSiteKey"/> + <MkButton class="_formBlock" type="submit" :disabled="shouldDisableSubmitting" primary data-cy-signup-submit>{{ $ts.start }}</MkButton> </template> </form> </template> @@ -57,8 +57,8 @@ const getPasswordStrength = require('syuilo-password-strength'); import { toUnicode } from 'punycode/'; import { host, url } from '@client/config'; import MkButton from './ui/button.vue'; -import MkInput from './ui/input.vue'; -import MkSwitch from './ui/switch.vue'; +import MkInput from './form/input.vue'; +import MkSwitch from './form/switch.vue'; import * as os from '@client/os'; import { login } from '@client/account'; diff --git a/src/client/components/tab.vue b/src/client/components/tab.vue index 3902b7f98f..7705fc3d6d 100644 --- a/src/client/components/tab.vue +++ b/src/client/components/tab.vue @@ -3,7 +3,7 @@ import { defineComponent, h, resolveDirective, withDirectives } from 'vue'; export default defineComponent({ props: { - value: { + modelValue: { required: true, }, }, @@ -13,11 +13,11 @@ export default defineComponent({ return withDirectives(h('div', { class: 'pxhvhrfw', }, options.map(option => withDirectives(h('button', { - class: ['_button', { active: this.value === option.props.value }], + class: ['_button', { active: this.modelValue === option.props.modelValue }], key: option.key, - disabled: this.value === option.props.value, + disabled: this.modelValue === option.props.modelValue, onClick: () => { - this.$emit('update:value', option.props.value); + this.$emit('update:modelValue', option.props.modelValue); } }, option.children), [ [resolveDirective('click-anime')] diff --git a/src/client/components/taskmanager.api-window.vue b/src/client/components/taskmanager.api-window.vue index c9b2c43413..807e4a0075 100644 --- a/src/client/components/taskmanager.api-window.vue +++ b/src/client/components/taskmanager.api-window.vue @@ -9,7 +9,7 @@ <template #header>Req Viewer</template> <div class="rlkneywz"> - <MkTab v-model:value="tab" style="border-bottom: solid 0.5px var(--divider);"> + <MkTab v-model="tab" style="border-bottom: solid 0.5px var(--divider);"> <option value="req">Request</option> <option value="res">Response</option> </MkTab> diff --git a/src/client/components/taskmanager.vue b/src/client/components/taskmanager.vue index cb8cb78748..6f3d1b0354 100644 --- a/src/client/components/taskmanager.vue +++ b/src/client/components/taskmanager.vue @@ -4,7 +4,7 @@ <i class="fas fa-terminal" style="margin-right: 0.5em;"></i>Task Manager </template> <div class="qljqmnzj _monospace"> - <MkTab v-model:value="tab" style="border-bottom: solid 0.5px var(--divider);"> + <MkTab v-model="tab" style="border-bottom: solid 0.5px var(--divider);"> <option value="windows">Windows</option> <option value="stream">Stream</option> <option value="streamPool">Stream (Pool)</option> diff --git a/src/client/components/token-generate-window.vue b/src/client/components/token-generate-window.vue index fe61f61efa..86312564cc 100644 --- a/src/client/components/token-generate-window.vue +++ b/src/client/components/token-generate-window.vue @@ -31,9 +31,9 @@ import { defineComponent } from 'vue'; import { kinds } from '@/misc/api-permissions'; import XModalWindow from '@client/components/ui/modal-window.vue'; -import MkInput from './ui/input.vue'; -import MkTextarea from './ui/textarea.vue'; -import MkSwitch from './ui/switch.vue'; +import MkInput from './form/input.vue'; +import MkTextarea from './form/textarea.vue'; +import MkSwitch from './form/switch.vue'; import MkButton from './ui/button.vue'; import MkInfo from './ui/info.vue'; diff --git a/src/client/components/ui/button.vue b/src/client/components/ui/button.vue index d6ac42994f..6e3cd485c8 100644 --- a/src/client/components/ui/button.vue +++ b/src/client/components/ui/button.vue @@ -181,14 +181,6 @@ export default defineComponent({ outline-offset: 2px; } - &.inline + .bghgjjyj { - margin-left: 12px; - } - - &:not(.inline) + .bghgjjyj { - margin-top: 16px; - } - &.inline { display: inline-block; width: auto; diff --git a/src/client/components/ui/radios.vue b/src/client/components/ui/radios.vue deleted file mode 100644 index 8a62b87683..0000000000 --- a/src/client/components/ui/radios.vue +++ /dev/null @@ -1,58 +0,0 @@ -<script lang="ts"> -import { defineComponent, h } from 'vue'; -import MkRadio from '@client/components/ui/radio.vue'; - -export default defineComponent({ - components: { - MkRadio - }, - props: { - modelValue: { - required: false - }, - }, - data() { - return { - value: this.modelValue, - } - }, - watch: { - value() { - this.$emit('update:modelValue', this.value); - } - }, - render() { - const label = this.$slots.desc(); - let options = this.$slots.default(); - - // なぜかFragmentになることがあるため - if (options.length === 1 && options[0].props == null) options = options[0].children; - - return h('div', { - class: 'novjtcto' - }, [ - h('div', label), - ...options.map(option => h(MkRadio, { - key: option.key, - value: option.props.value, - modelValue: this.value, - 'onUpdate:modelValue': value => this.value = value, - }, option.children)) - ]); - } -}); -</script> - -<style lang="scss"> -.novjtcto { - margin: 32px 0; - - &:first-child { - margin-top: 0; - } - - &:last-child { - margin-bottom: 0; - } -} -</style> diff --git a/src/client/components/ui/range.vue b/src/client/components/ui/range.vue deleted file mode 100644 index 4cfe66a8fc..0000000000 --- a/src/client/components/ui/range.vue +++ /dev/null @@ -1,139 +0,0 @@ -<template> -<div class="timctyfi" :class="{ focused, disabled }"> - <div class="icon"><slot name="icon"></slot></div> - <span class="label"><slot name="label"></slot></span> - <input - type="range" - ref="input" - v-model="v" - :disabled="disabled" - :min="min" - :max="max" - :step="step" - :autofocus="autofocus" - @focus="focused = true" - @blur="focused = false" - @input="$emit('update:value', $event.target.value)" - /> -</div> -</template> - -<script lang="ts"> -import { defineComponent } from 'vue'; - -export default defineComponent({ - props: { - value: { - type: Number, - required: false, - default: 0 - }, - disabled: { - type: Boolean, - required: false, - default: false - }, - min: { - type: Number, - required: false, - default: 0 - }, - max: { - type: Number, - required: false, - default: 100 - }, - step: { - type: Number, - required: false, - default: 1 - }, - autofocus: { - type: Boolean, - required: false - } - }, - data() { - return { - v: this.value, - focused: false - }; - }, - watch: { - value(v) { - this.v = parseFloat(v); - } - }, - mounted() { - if (this.autofocus) { - this.$nextTick(() => { - this.$refs.input.focus(); - }); - } - } -}); -</script> - -<style lang="scss" scoped> -.timctyfi { - position: relative; - margin: 8px; - - > .icon { - display: inline-block; - width: 24px; - text-align: center; - } - - > .title { - pointer-events: none; - font-size: 16px; - color: var(--inputLabel); - overflow: hidden; - } - - > input { - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - background: var(--X10); - height: 7px; - margin: 0 8px; - outline: 0; - border: 0; - border-radius: 7px; - - &.disabled { - opacity: 0.6; - cursor: not-allowed; - } - - &::-webkit-slider-thumb { - -webkit-appearance: none; - appearance: none; - cursor: pointer; - width: 20px; - height: 20px; - display: block; - border-radius: 50%; - border: none; - background: var(--accent); - box-shadow: 0 0 6px rgba(0, 0, 0, 0.3); - box-sizing: content-box; - } - - &::-moz-range-thumb { - -moz-appearance: none; - appearance: none; - cursor: pointer; - width: 20px; - height: 20px; - display: block; - border-radius: 50%; - border: none; - background: var(--accent); - box-shadow: 0 0 6px rgba(0, 0, 0, 0.3); - } - } -} -</style> diff --git a/src/client/components/ui/select.vue b/src/client/components/ui/select.vue deleted file mode 100644 index e9d43d8a64..0000000000 --- a/src/client/components/ui/select.vue +++ /dev/null @@ -1,262 +0,0 @@ -<template> -<div class="vblkjoeq"> - <div class="label" @click="focus"><slot name="label"></slot></div> - <div class="input" :class="{ inline, disabled, focused }"> - <div class="prefix" ref="prefixEl"><slot name="prefix"></slot></div> - <select ref="inputEl" - v-model="v" - :disabled="disabled" - :required="required" - :readonly="readonly" - :placeholder="placeholder" - @focus="focused = true" - @blur="focused = false" - @input="onInput" - > - <slot></slot> - </select> - <div class="suffix" ref="suffixEl"><i class="fas fa-chevron-down"></i></div> - </div> - <div class="caption"><slot name="caption"></slot></div> - - <MkButton v-if="manualSave && changed" @click="updated" primary><i class="fas fa-save"></i> {{ $ts.save }}</MkButton> -</div> -</template> - -<script lang="ts"> -import { defineComponent, onMounted, onUnmounted, nextTick, ref, watch, computed, toRefs } from 'vue'; -import MkButton from './button.vue'; - -export default defineComponent({ - components: { - MkButton, - }, - - props: { - modelValue: { - required: true - }, - required: { - type: Boolean, - required: false - }, - readonly: { - type: Boolean, - required: false - }, - disabled: { - type: Boolean, - required: false - }, - placeholder: { - type: String, - required: false - }, - autofocus: { - type: Boolean, - required: false, - default: false - }, - inline: { - type: Boolean, - required: false, - default: false - }, - manualSave: { - type: Boolean, - required: false, - default: false - }, - }, - - emits: ['change', 'update:modelValue'], - - setup(props, context) { - const { modelValue, autofocus } = toRefs(props); - const v = ref(modelValue.value); - const focused = ref(false); - const changed = ref(false); - const invalid = ref(false); - const filled = computed(() => v.value !== '' && v.value != null); - const inputEl = ref(null); - const prefixEl = ref(null); - const suffixEl = ref(null); - - const focus = () => inputEl.value.focus(); - const onInput = (ev) => { - changed.value = true; - context.emit('change', ev); - }; - - const updated = () => { - changed.value = false; - context.emit('update:modelValue', v.value); - }; - - watch(modelValue, newValue => { - v.value = newValue; - }); - - watch(v, newValue => { - if (!props.manualSave) { - updated(); - } - - invalid.value = inputEl.value.validity.badInput; - }); - - onMounted(() => { - nextTick(() => { - if (autofocus.value) { - focus(); - } - - // このコンポーネントが作成された時、非表示状態である場合がある - // 非表示状態だと要素の幅などは0になってしまうので、定期的に計算する - const clock = setInterval(() => { - if (prefixEl.value) { - if (prefixEl.value.offsetWidth) { - inputEl.value.style.paddingLeft = prefixEl.value.offsetWidth + 'px'; - } - } - if (suffixEl.value) { - if (suffixEl.value.offsetWidth) { - inputEl.value.style.paddingRight = suffixEl.value.offsetWidth + 'px'; - } - } - }, 100); - - onUnmounted(() => { - clearInterval(clock); - }); - }); - }); - - return { - v, - focused, - invalid, - changed, - filled, - inputEl, - prefixEl, - suffixEl, - focus, - onInput, - updated, - }; - }, -}); -</script> - -<style lang="scss" scoped> -.vblkjoeq { - margin: 1.5em 0; - - > .label { - font-size: 0.85em; - padding: 0 0 8px 12px; - user-select: none; - - &:empty { - display: none; - } - } - - > .caption { - font-size: 0.8em; - padding: 8px 0 0 12px; - color: var(--fgTransparentWeak); - - &:empty { - display: none; - } - } - - > .input { - $height: 42px; - position: relative; - - > select { - appearance: none; - -webkit-appearance: none; - display: block; - height: $height; - width: 100%; - margin: 0; - padding: 0 12px; - font: inherit; - font-weight: normal; - font-size: 1em; - color: var(--fg); - background: var(--panel); - border: solid 1px var(--inputBorder); - border-radius: 6px; - outline: none; - box-shadow: none; - box-sizing: border-box; - cursor: pointer; - transition: border-color 0.1s ease-out; - - &:hover { - border-color: var(--inputBorderHover); - } - } - - > .prefix, - > .suffix { - display: flex; - align-items: center; - position: absolute; - z-index: 1; - top: 0; - padding: 0 12px; - font-size: 1em; - height: $height; - pointer-events: none; - - &:empty { - display: none; - } - - > * { - display: inline-block; - min-width: 16px; - max-width: 150px; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - } - } - - > .prefix { - left: 0; - padding-right: 6px; - } - - > .suffix { - right: 0; - padding-left: 6px; - } - - &.inline { - display: inline-block; - margin: 0; - } - - &.focused { - > select { - border-color: var(--accent); - } - } - - &.disabled { - opacity: 0.7; - - &, * { - cursor: not-allowed !important; - } - } - } -} -</style> diff --git a/src/client/components/ui/switch.vue b/src/client/components/ui/switch.vue deleted file mode 100644 index 7aa9c0619d..0000000000 --- a/src/client/components/ui/switch.vue +++ /dev/null @@ -1,144 +0,0 @@ -<template> -<div - class="ziffeoms" - :class="{ disabled, checked }" - role="switch" - :aria-checked="checked" - :aria-disabled="disabled" - @click.prevent="toggle" -> - <input - type="checkbox" - ref="input" - :disabled="disabled" - @keydown.enter="toggle" - > - <span class="button"> - <span></span> - </span> - <span class="label"> - <span><slot></slot></span> - <p><slot name="caption"></slot></p> - </span> -</div> -</template> - -<script lang="ts"> -import { defineComponent } from 'vue'; - -export default defineComponent({ - props: { - modelValue: { - type: Boolean, - default: false - }, - disabled: { - type: Boolean, - default: false - } - }, - computed: { - checked(): boolean { - return this.modelValue; - } - }, - methods: { - toggle() { - if (this.disabled) return; - this.$emit('update:modelValue', !this.checked); - } - } -}); -</script> - -<style lang="scss" scoped> -.ziffeoms { - position: relative; - display: flex; - margin: 32px 0; - cursor: pointer; - transition: all 0.3s; - - &:first-child { - margin-top: 0; - } - - &:last-child { - margin-bottom: 0; - } - - > * { - user-select: none; - } - - &.disabled { - opacity: 0.6; - cursor: not-allowed; - } - - &.checked { - > .button { - background-color: var(--X10); - border-color: var(--X10); - - > * { - background-color: var(--accent); - transform: translateX(14px); - } - } - } - - > input { - position: absolute; - width: 0; - height: 0; - opacity: 0; - margin: 0; - } - - > .button { - position: relative; - display: inline-block; - flex-shrink: 0; - margin: 3px 0 0 0; - width: 34px; - height: 14px; - background: var(--X6); - outline: none; - border-radius: 14px; - transition: inherit; - - > * { - position: absolute; - top: -3px; - left: 0; - border-radius: 100%; - transition: background-color 0.3s, transform 0.3s; - width: 20px; - height: 20px; - background-color: #fff; - box-shadow: 0 2px 1px -1px rgba(#000, 0.2), 0 1px 1px 0 rgba(#000, 0.14), 0 1px 3px 0 rgba(#000, 0.12); - } - } - - > .label { - margin-left: 8px; - display: block; - cursor: pointer; - transition: inherit; - color: var(--fg); - - > span { - display: block; - line-height: 20px; - transition: inherit; - } - - > p { - margin: 0; - color: var(--fgTransparentWeak); - font-size: 90%; - } - } -} -</style> diff --git a/src/client/components/ui/textarea.vue b/src/client/components/ui/textarea.vue deleted file mode 100644 index 08ac3182a9..0000000000 --- a/src/client/components/ui/textarea.vue +++ /dev/null @@ -1,254 +0,0 @@ -<template> -<div class="adhpbeos"> - <div class="label" @click="focus"><slot name="label"></slot></div> - <div class="input" :class="{ disabled, focused, tall, pre }"> - <textarea ref="inputEl" - :class="{ code, _monospace: code }" - v-model="v" - :disabled="disabled" - :required="required" - :readonly="readonly" - :placeholder="placeholder" - :pattern="pattern" - :autocomplete="autocomplete" - :spellcheck="spellcheck" - @focus="focused = true" - @blur="focused = false" - @keydown="onKeydown($event)" - @input="onInput" - ></textarea> - </div> - <div class="caption"><slot name="caption"></slot></div> - - <MkButton v-if="manualSave && changed" @click="updated" primary><i class="fas fa-save"></i> {{ $ts.save }}</MkButton> -</div> -</template> - -<script lang="ts"> -import { defineComponent, onMounted, onUnmounted, nextTick, ref, watch, computed, toRefs } from 'vue'; -import MkButton from './button.vue'; -import { debounce } from 'throttle-debounce'; - -export default defineComponent({ - components: { - MkButton, - }, - - props: { - modelValue: { - required: true - }, - type: { - type: String, - required: false - }, - required: { - type: Boolean, - required: false - }, - readonly: { - type: Boolean, - required: false - }, - disabled: { - type: Boolean, - required: false - }, - pattern: { - type: String, - required: false - }, - placeholder: { - type: String, - required: false - }, - autofocus: { - type: Boolean, - required: false, - default: false - }, - autocomplete: { - required: false - }, - spellcheck: { - required: false - }, - code: { - type: Boolean, - required: false - }, - tall: { - type: Boolean, - required: false, - default: false - }, - pre: { - type: Boolean, - required: false, - default: false - }, - debounce: { - type: Boolean, - required: false, - default: false - }, - manualSave: { - type: Boolean, - required: false, - default: false - }, - }, - - emits: ['change', 'keydown', 'enter', 'update:modelValue'], - - setup(props, context) { - const { modelValue, autofocus } = toRefs(props); - const v = ref(modelValue.value); - const focused = ref(false); - const changed = ref(false); - const invalid = ref(false); - const filled = computed(() => v.value !== '' && v.value != null); - const inputEl = ref(null); - - const focus = () => inputEl.value.focus(); - const onInput = (ev) => { - changed.value = true; - context.emit('change', ev); - }; - const onKeydown = (ev: KeyboardEvent) => { - context.emit('keydown', ev); - - if (ev.code === 'Enter') { - context.emit('enter'); - } - }; - - const updated = () => { - changed.value = false; - context.emit('update:modelValue', v.value); - }; - - const debouncedUpdated = debounce(1000, updated); - - watch(modelValue, newValue => { - v.value = newValue; - }); - - watch(v, newValue => { - if (!props.manualSave) { - if (props.debounce) { - debouncedUpdated(); - } else { - updated(); - } - } - - invalid.value = inputEl.value.validity.badInput; - }); - - onMounted(() => { - nextTick(() => { - if (autofocus.value) { - focus(); - } - }); - }); - - return { - v, - focused, - invalid, - changed, - filled, - inputEl, - focus, - onInput, - onKeydown, - updated, - }; - }, -}); -</script> - -<style lang="scss" scoped> -.adhpbeos { - margin: 1.5em 0; - - > .label { - font-size: 0.85em; - padding: 0 0 8px 12px; - user-select: none; - - &:empty { - display: none; - } - } - - > .caption { - font-size: 0.8em; - padding: 8px 0 0 12px; - color: var(--fgTransparentWeak); - - &:empty { - display: none; - } - } - - > .input { - position: relative; - - > textarea { - appearance: none; - -webkit-appearance: none; - display: block; - width: 100%; - min-width: 100%; - max-width: 100%; - min-height: 130px; - margin: 0; - padding: 12px; - font: inherit; - font-weight: normal; - font-size: 1em; - color: var(--fg); - background: var(--panel); - border: solid 0.5px var(--inputBorder); - border-radius: 6px; - outline: none; - box-shadow: none; - box-sizing: border-box; - transition: border-color 0.1s ease-out; - - &:hover { - border-color: var(--inputBorderHover); - } - } - - &.focused { - > textarea { - border-color: var(--accent); - } - } - - &.disabled { - opacity: 0.7; - - &, * { - cursor: not-allowed !important; - } - } - - &.tall { - > textarea { - min-height: 200px; - } - } - - &.pre { - > textarea { - white-space: pre; - } - } - } -} -</style> diff --git a/src/client/components/user-select-dialog.vue b/src/client/components/user-select-dialog.vue index 87c32dab25..0f3ee2a126 100644 --- a/src/client/components/user-select-dialog.vue +++ b/src/client/components/user-select-dialog.vue @@ -10,7 +10,7 @@ <template #header>{{ $ts.selectUser }}</template> <div class="tbhwbxda _monolithic_"> <div class="_section"> - <div class="_inputSplit _inputNoTopMargin _inputNoBottomMargin"> + <div class="_inputSplit"> <MkInput v-model="username" class="input" @update:modelValue="search" ref="username"> <template #label>{{ $ts.username }}</template> <template #prefix>@</template> @@ -52,7 +52,7 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import MkInput from './ui/input.vue'; +import MkInput from './form/input.vue'; import XModalWindow from '@client/components/ui/modal-window.vue'; import * as os from '@client/os'; diff --git a/src/client/components/widgets.vue b/src/client/components/widgets.vue index 150d61c027..aef5de453c 100644 --- a/src/client/components/widgets.vue +++ b/src/client/components/widgets.vue @@ -30,7 +30,7 @@ <script lang="ts"> import { defineComponent, defineAsyncComponent } from 'vue'; import { v4 as uuid } from 'uuid'; -import MkSelect from '@client/components/ui/select.vue'; +import MkSelect from '@client/components/form/select.vue'; import MkButton from '@client/components/ui/button.vue'; import { widgets as widgetDefs } from '@client/widgets'; diff --git a/src/client/pages/about-misskey.vue b/src/client/pages/about-misskey.vue index 384c7e8ccb..d2c0ec0550 100644 --- a/src/client/pages/about-misskey.vue +++ b/src/client/pages/about-misskey.vue @@ -2,15 +2,15 @@ <div style="overflow: clip;"> <FormBase class="znqjceqz"> <div id="debug"></div> - <section class="_formItem about"> - <div class="_formPanel panel" :class="{ playing: easterEggEngine != null }" ref="about"> + <section class="_debobigegoItem about"> + <div class="_debobigegoPanel panel" :class="{ playing: easterEggEngine != null }" ref="about"> <img src="/static-assets/client/about-icon.png" alt="" class="icon" @load="iconLoaded" draggable="false" @click="gravity"/> <div class="misskey">Misskey</div> <div class="version">v{{ version }}</div> <span class="emoji" v-for="emoji in easterEggEmojis" :key="emoji.id" :data-physics-x="emoji.left" :data-physics-y="emoji.top" :class="{ _physics_circle_: !emoji.emoji.startsWith(':') }"><MkEmoji class="emoji" :emoji="emoji.emoji" :custom-emojis="$instance.emojis" :is-reaction="false" :normal="true" :no-style="true"/></span> </div> </section> - <section class="_formItem" style="text-align: center; padding: 0 16px;"> + <section class="_debobigegoItem" style="text-align: center; padding: 0 16px;"> {{ $ts._aboutMisskey.about }}<br><MkA class="_link" to="/docs/general/misskey">{{ $ts.learnMore }}</MkA> </section> <FormGroup> @@ -55,10 +55,10 @@ <script lang="ts"> import { defineComponent } from 'vue'; import { version } from '@client/config'; -import FormLink from '@client/components/form/link.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormGroup from '@client/components/form/group.vue'; -import FormKeyValueView from '@client/components/form/key-value-view.vue'; +import FormLink from '@client/components/debobigego/link.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; +import FormKeyValueView from '@client/components/debobigego/key-value-view.vue'; import MkLink from '@client/components/link.vue'; import { physics } from '@client/scripts/physics'; import * as symbols from '@client/symbols'; diff --git a/src/client/pages/about.vue b/src/client/pages/about.vue index bdd4c78827..2c580c293a 100644 --- a/src/client/pages/about.vue +++ b/src/client/pages/about.vue @@ -1,7 +1,7 @@ <template> <FormBase> - <div class="_formItem"> - <div class="_formPanel fwhjspax"> + <div class="_debobigegoItem"> + <div class="_debobigegoPanel fwhjspax"> <img :src="$instance.iconUrl || $instance.faviconUrl || '/favicon.ico'" alt="" class="icon"/> <span class="name">{{ $instance.name || host }}</span> </div> @@ -59,12 +59,12 @@ <script lang="ts"> import { defineComponent } from 'vue'; import { version, instanceName } from '@client/config'; -import FormLink from '@client/components/form/link.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormGroup from '@client/components/form/group.vue'; -import FormKeyValueView from '@client/components/form/key-value-view.vue'; -import FormTextarea from '@client/components/form/textarea.vue'; -import FormSuspense from '@client/components/form/suspense.vue'; +import FormLink from '@client/components/debobigego/link.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; +import FormKeyValueView from '@client/components/debobigego/key-value-view.vue'; +import FormTextarea from '@client/components/debobigego/textarea.vue'; +import FormSuspense from '@client/components/debobigego/suspense.vue'; import * as os from '@client/os'; import number from '@client/filters/number'; import * as symbols from '@client/symbols'; diff --git a/src/client/pages/advanced-theme-editor.vue b/src/client/pages/advanced-theme-editor.vue index c03d88b82d..8a63d74887 100644 --- a/src/client/pages/advanced-theme-editor.vue +++ b/src/client/pages/advanced-theme-editor.vue @@ -4,7 +4,7 @@ <div class="_content"> <details> <summary>{{ $ts.import }}</summary> - <MkTextarea v-model:value="themeToImport"> + <MkTextarea v-model="themeToImport"> {{ $ts._theme.importInfo }} </MkTextarea> <MkButton :disabled="!themeToImport.trim()" @click="importTheme">{{ $ts.import }}</MkButton> @@ -14,9 +14,9 @@ <section class="_section"> <div class="_content _card _gap"> <div class="_content"> - <MkInput v-model:value="name" required><span>{{ $ts.name }}</span></MkInput> - <MkInput v-model:value="author" required><span>{{ $ts.author }}</span></MkInput> - <MkTextarea v-model:value="description"><span>{{ $ts.description }}</span></MkTextarea> + <MkInput v-model="name" required><span>{{ $ts.name }}</span></MkInput> + <MkInput v-model="author" required><span>{{ $ts.author }}</span></MkInput> + <MkTextarea v-model="description"><span>{{ $ts.description }}</span></MkTextarea> <div class="_inputs"> <div v-text="$ts._theme.base" /> <MkRadio v-model="baseTheme" value="light">{{ $ts.light }}</MkRadio> @@ -41,31 +41,31 @@ <!-- color --> <div v-else-if="typeof v === 'string'" class="color"> <input type="color" :value="v" @input="colorChanged($event.target.value, i)"/> - <MkInput class="select" :value="v" @update:value="colorChanged($event, i)"/> + <MkInput class="select" :value="v" @update:modelValue="colorChanged($event, i)"/> </div> <!-- ref const --> - <MkInput v-else-if="v.type === 'refConst'" v-model:value="v.key"> + <MkInput v-else-if="v.type === 'refConst'" v-model="v.key"> <template #prefix>$</template> <span>{{ $ts.name }}</span> </MkInput> <!-- ref props --> - <MkSelect class="select" v-else-if="v.type === 'refProp'" v-model:value="v.key"> + <MkSelect class="select" v-else-if="v.type === 'refProp'" v-model="v.key"> <option v-for="key in themeProps" :value="key" :key="key">{{ $t('_theme.keys.' + key) }}</option> </MkSelect> <!-- func --> <template v-else-if="v.type === 'func'"> - <MkSelect class="select" v-model:value="v.name"> + <MkSelect class="select" v-model="v.name"> <template #label>{{ $ts._theme.funcKind }}</template> <option v-for="n in ['alpha', 'darken', 'lighten']" :value="n" :key="n">{{ $t('_theme.' + n) }}</option> </MkSelect> - <MkInput type="number" v-model:value="v.arg"><span>{{ $ts._theme.argument }}</span></MkInput> - <MkSelect class="select" v-model:value="v.value"> + <MkInput type="number" v-model="v.arg"><span>{{ $ts._theme.argument }}</span></MkInput> + <MkSelect class="select" v-model="v.value"> <template #label>{{ $ts._theme.basedProp }}</template> <option v-for="key in themeProps" :value="key" :key="key">{{ $t('_theme.keys.' + key) }}</option> </MkSelect> </template> <!-- CSS --> - <MkInput v-else-if="v.type === 'css'" v-model:value="v.value"> + <MkInput v-else-if="v.type === 'css'" v-model="v.value"> <span>CSS</span> </MkInput> </div> @@ -95,11 +95,11 @@ import { defineComponent } from 'vue'; import * as JSON5 from 'json5'; import { toUnicode } from 'punycode/'; -import MkRadio from '@client/components/ui/radio.vue'; +import MkRadio from '@client/components/form/radio.vue'; import MkButton from '@client/components/ui/button.vue'; -import MkInput from '@client/components/ui/input.vue'; -import MkTextarea from '@client/components/ui/textarea.vue'; -import MkSelect from '@client/components/ui/select.vue'; +import MkInput from '@client/components/form/input.vue'; +import MkTextarea from '@client/components/form/textarea.vue'; +import MkSelect from '@client/components/form/select.vue'; import MkSample from '@client/components/sample.vue'; import { convertToMisskeyTheme, ThemeValue, convertToViewModel, ThemeViewModel } from '@client/scripts/theme-editor'; diff --git a/src/client/pages/api-console.vue b/src/client/pages/api-console.vue index c6d459fd6d..9aa7d4ea4d 100644 --- a/src/client/pages/api-console.vue +++ b/src/client/pages/api-console.vue @@ -1,7 +1,7 @@ <template> <div class="_root"> <div class="_block" style="padding: 24px;"> - <MkInput v-model="endpoint" :datalist="endpoints" @update:modelValue="onEndpointChange()" class="_inputNoTopMargin"> + <MkInput v-model="endpoint" :datalist="endpoints" @update:modelValue="onEndpointChange()" class=""> <template #label>Endpoint</template> </MkInput> <MkTextarea v-model="body" code> @@ -27,9 +27,9 @@ import { defineComponent } from 'vue'; import * as JSON5 from 'json5'; import MkButton from '@client/components/ui/button.vue'; -import MkInput from '@client/components/ui/input.vue'; -import MkTextarea from '@client/components/ui/textarea.vue'; -import MkSwitch from '@client/components/ui/switch.vue'; +import MkInput from '@client/components/form/input.vue'; +import MkTextarea from '@client/components/form/textarea.vue'; +import MkSwitch from '@client/components/form/switch.vue'; import * as os from '@client/os'; import * as symbols from '@client/symbols'; diff --git a/src/client/pages/channel-editor.vue b/src/client/pages/channel-editor.vue index eeea0b70aa..67e27896ce 100644 --- a/src/client/pages/channel-editor.vue +++ b/src/client/pages/channel-editor.vue @@ -27,9 +27,9 @@ <script lang="ts"> import { computed, defineComponent } from 'vue'; -import MkTextarea from '@client/components/ui/textarea.vue'; +import MkTextarea from '@client/components/form/textarea.vue'; import MkButton from '@client/components/ui/button.vue'; -import MkInput from '@client/components/ui/input.vue'; +import MkInput from '@client/components/form/input.vue'; import { selectFile } from '@client/scripts/select-file'; import * as os from '@client/os'; import * as symbols from '@client/symbols'; diff --git a/src/client/pages/channels.vue b/src/client/pages/channels.vue index 7e3302959b..fd1408c253 100644 --- a/src/client/pages/channels.vue +++ b/src/client/pages/channels.vue @@ -1,7 +1,7 @@ <template> <div> <div class="_section" style="padding: 0;" v-if="$i"> - <MkTab class="_content" v-model:value="tab"> + <MkTab class="_content" v-model="tab"> <option value="featured"><i class="fas fa-fire-alt"></i> {{ $ts._channel.featured }}</option> <option value="following"><i class="fas fa-heart"></i> {{ $ts._channel.following }}</option> <option value="owned"><i class="fas fa-edit"></i> {{ $ts._channel.owned }}</option> diff --git a/src/client/pages/docs.vue b/src/client/pages/docs.vue index be4d4255db..629dc2be53 100644 --- a/src/client/pages/docs.vue +++ b/src/client/pages/docs.vue @@ -2,7 +2,7 @@ <div class="vtaihdtm"> <div class="body"> <div class="search"> - <MkInput v-model="query" :debounce="true" type="search" class="_inputNoTopMargin _inputNoBottomMargin" :placeholder="$ts.search"> + <MkInput v-model="query" :debounce="true" type="search" class="" :placeholder="$ts.search"> <template #prefix><i class="fas fa-search"></i></template> </MkInput> </div> @@ -57,7 +57,7 @@ import { defineComponent } from 'vue'; import { url, lang } from '@client/config'; import * as symbols from '@client/symbols'; import MkFolder from '@client/components/ui/folder.vue'; -import MkInput from '@client/components/ui/input.vue'; +import MkInput from '@client/components/form/input.vue'; export default defineComponent({ components: { diff --git a/src/client/pages/emojis.category.vue b/src/client/pages/emojis.category.vue index 7fee792854..d7737523d2 100644 --- a/src/client/pages/emojis.category.vue +++ b/src/client/pages/emojis.category.vue @@ -1,7 +1,7 @@ <template> <div class="driuhtrh"> <div class="query"> - <MkInput v-model="q" class="_inputNoTopMargin _inputNoBottomMargin" :placeholder="$ts.search"> + <MkInput v-model="q" class="" :placeholder="$ts.search"> <template #prefix><i class="fas fa-search"></i></template> </MkInput> @@ -31,8 +31,8 @@ <script lang="ts"> import { defineComponent, computed } from 'vue'; import MkButton from '@client/components/ui/button.vue'; -import MkInput from '@client/components/ui/input.vue'; -import MkSelect from '@client/components/ui/select.vue'; +import MkInput from '@client/components/form/input.vue'; +import MkSelect from '@client/components/form/select.vue'; import MkFolder from '@client/components/ui/folder.vue'; import MkTab from '@client/components/tab.vue'; import * as os from '@client/os'; diff --git a/src/client/pages/explore.vue b/src/client/pages/explore.vue index 7054940a1a..7e0e8ea27b 100644 --- a/src/client/pages/explore.vue +++ b/src/client/pages/explore.vue @@ -75,7 +75,7 @@ import { computed, defineComponent } from 'vue'; import XUserList from '@client/components/user-list.vue'; import MkFolder from '@client/components/ui/folder.vue'; -import MkInput from '@client/components/ui/input.vue'; +import MkInput from '@client/components/form/input.vue'; import number from '@client/filters/number'; import * as os from '@client/os'; import * as symbols from '@client/symbols'; diff --git a/src/client/pages/federation.vue b/src/client/pages/federation.vue index 2afe70eea6..aac94e00af 100644 --- a/src/client/pages/federation.vue +++ b/src/client/pages/federation.vue @@ -1,11 +1,11 @@ <template> <div class="taeiyria"> <div class="query"> - <MkInput v-model="host" :debounce="true" class="_inputNoTopMargin"> + <MkInput v-model="host" :debounce="true" class=""> <template #prefix><i class="fas fa-search"></i></template> <template #label>{{ $ts.host }}</template> </MkInput> - <div class="_inputSplit _inputNoBottomMargin"> + <div class="_inputSplit"> <MkSelect v-model="state"> <template #label>{{ $ts.state }}</template> <option value="all">{{ $ts.all }}</option> @@ -96,8 +96,8 @@ <script lang="ts"> import { defineComponent } from 'vue'; import MkButton from '@client/components/ui/button.vue'; -import MkInput from '@client/components/ui/input.vue'; -import MkSelect from '@client/components/ui/select.vue'; +import MkInput from '@client/components/form/input.vue'; +import MkSelect from '@client/components/form/select.vue'; import MkPagination from '@client/components/ui/pagination.vue'; import * as os from '@client/os'; import * as symbols from '@client/symbols'; diff --git a/src/client/pages/gallery/edit.vue b/src/client/pages/gallery/edit.vue index cd6a0defdd..8e74b068ef 100644 --- a/src/client/pages/gallery/edit.vue +++ b/src/client/pages/gallery/edit.vue @@ -1,23 +1,23 @@ <template> <FormBase> <FormSuspense :p="init"> - <FormInput v-model:value="title"> + <FormInput v-model="title"> <span>{{ $ts.title }}</span> </FormInput> - <FormTextarea v-model:value="description" :max="500"> + <FormTextarea v-model="description" :max="500"> <span>{{ $ts.description }}</span> </FormTextarea> <FormGroup> - <div v-for="file in files" :key="file.id" class="_formItem _formPanel wqugxsfx" :style="{ backgroundImage: file ? `url(${ file.thumbnailUrl })` : null }"> + <div v-for="file in files" :key="file.id" class="_debobigegoItem _debobigegoPanel wqugxsfx" :style="{ backgroundImage: file ? `url(${ file.thumbnailUrl })` : null }"> <div class="name">{{ file.name }}</div> <button class="remove _button" @click="remove(file)" v-tooltip="$ts.remove"><i class="fas fa-times"></i></button> </div> <FormButton @click="selectFile" primary><i class="fas fa-plus"></i> {{ $ts.attachFile }}</FormButton> </FormGroup> - <FormSwitch v-model:value="isSensitive">{{ $ts.markAsSensitive }}</FormSwitch> + <FormSwitch v-model="isSensitive">{{ $ts.markAsSensitive }}</FormSwitch> <FormButton v-if="postId" @click="save" primary><i class="fas fa-save"></i> {{ $ts.save }}</FormButton> <FormButton v-else @click="save" primary><i class="fas fa-save"></i> {{ $ts.publish }}</FormButton> @@ -29,14 +29,14 @@ <script lang="ts"> import { computed, defineComponent } from 'vue'; -import FormButton from '@client/components/form/button.vue'; -import FormInput from '@client/components/form/input.vue'; -import FormTextarea from '@client/components/form/textarea.vue'; -import FormSwitch from '@client/components/form/switch.vue'; -import FormTuple from '@client/components/form/tuple.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormGroup from '@client/components/form/group.vue'; -import FormSuspense from '@client/components/form/suspense.vue'; +import FormButton from '@client/components/debobigego/button.vue'; +import FormInput from '@client/components/debobigego/input.vue'; +import FormTextarea from '@client/components/debobigego/textarea.vue'; +import FormSwitch from '@client/components/debobigego/switch.vue'; +import FormTuple from '@client/components/debobigego/tuple.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; +import FormSuspense from '@client/components/debobigego/suspense.vue'; import { selectFile } from '@client/scripts/select-file'; import * as os from '@client/os'; import * as symbols from '@client/symbols'; diff --git a/src/client/pages/gallery/index.vue b/src/client/pages/gallery/index.vue index 9e726e70f2..ffc599513e 100644 --- a/src/client/pages/gallery/index.vue +++ b/src/client/pages/gallery/index.vue @@ -1,6 +1,6 @@ <template> <div class="xprsixdl _root"> - <MkTab v-model:value="tab" v-if="$i"> + <MkTab v-model="tab" v-if="$i"> <option value="explore"><i class="fas fa-icons"></i> {{ $ts.gallery }}</option> <option value="liked"><i class="fas fa-heart"></i> {{ $ts._gallery.liked }}</option> <option value="my"><i class="fas fa-edit"></i> {{ $ts._gallery.my }}</option> @@ -46,7 +46,7 @@ import { computed, defineComponent } from 'vue'; import XUserList from '@client/components/user-list.vue'; import MkFolder from '@client/components/ui/folder.vue'; -import MkInput from '@client/components/ui/input.vue'; +import MkInput from '@client/components/form/input.vue'; import MkButton from '@client/components/ui/button.vue'; import MkTab from '@client/components/tab.vue'; import MkPagination from '@client/components/ui/pagination.vue'; diff --git a/src/client/pages/instance-info.vue b/src/client/pages/instance-info.vue index 7d03c0847d..4fbf104f0c 100644 --- a/src/client/pages/instance-info.vue +++ b/src/client/pages/instance-info.vue @@ -3,8 +3,8 @@ <FormGroup v-if="instance"> <template #label>{{ instance.host }}</template> <FormGroup> - <div class="_formItem"> - <div class="_formPanel fnfelxur"> + <div class="_debobigegoItem"> + <div class="_debobigegoPanel fnfelxur"> <img :src="instance.iconUrl || instance.faviconUrl" alt="" class="icon"/> </div> </div> @@ -60,9 +60,9 @@ <template #value>{{ instance.openRegistrations ? $ts.yes : $ts.no }}</template> </FormKeyValueView> </FormGroup> - <div class="_formItem"> - <div class="_formLabel">{{ $ts.statistics }}</div> - <div class="_formPanel cmhjzshl"> + <div class="_debobigegoItem"> + <div class="_debobigegoLabel">{{ $ts.statistics }}</div> + <div class="_debobigegoPanel cmhjzshl"> <div class="selects"> <MkSelect v-model="chartSrc" style="margin: 0; flex: 1;"> <option value="requests">{{ $ts._instanceCharts.requests }}</option> @@ -136,15 +136,15 @@ <script lang="ts"> import { defineAsyncComponent, defineComponent } from 'vue'; import Chart from 'chart.js'; -import FormObjectView from '@client/components/form/object-view.vue'; -import FormTextarea from '@client/components/form/textarea.vue'; -import FormLink from '@client/components/form/link.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormGroup from '@client/components/form/group.vue'; -import FormButton from '@client/components/form/button.vue'; -import FormKeyValueView from '@client/components/form/key-value-view.vue'; -import FormSuspense from '@client/components/form/suspense.vue'; -import MkSelect from '@client/components/ui/select.vue'; +import FormObjectView from '@client/components/debobigego/object-view.vue'; +import FormTextarea from '@client/components/debobigego/textarea.vue'; +import FormLink from '@client/components/debobigego/link.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; +import FormButton from '@client/components/debobigego/button.vue'; +import FormKeyValueView from '@client/components/debobigego/key-value-view.vue'; +import FormSuspense from '@client/components/debobigego/suspense.vue'; +import MkSelect from '@client/components/form/select.vue'; import * as os from '@client/os'; import number from '@client/filters/number'; import bytes from '@client/filters/bytes'; diff --git a/src/client/pages/instance/abuses.vue b/src/client/pages/instance/abuses.vue index ac20ebabe5..a66847938d 100644 --- a/src/client/pages/instance/abuses.vue +++ b/src/client/pages/instance/abuses.vue @@ -24,10 +24,10 @@ </div> <!-- TODO <div class="inputs" style="display: flex; padding-top: 1.2em;"> - <MkInput v-model:value="searchUsername" style="margin: 0; flex: 1;" type="text" spellcheck="false" @update:value="$refs.reports.reload()"> + <MkInput v-model="searchUsername" style="margin: 0; flex: 1;" type="text" spellcheck="false" @update:modelValue="$refs.reports.reload()"> <span>{{ $ts.username }}</span> </MkInput> - <MkInput v-model:value="searchHost" style="margin: 0; flex: 1;" type="text" spellcheck="false" @update:value="$refs.reports.reload()" :disabled="pagination.params().origin === 'local'"> + <MkInput v-model="searchHost" style="margin: 0; flex: 1;" type="text" spellcheck="false" @update:modelValue="$refs.reports.reload()" :disabled="pagination.params().origin === 'local'"> <span>{{ $ts.host }}</span> </MkInput> </div> @@ -65,8 +65,8 @@ import { defineComponent } from 'vue'; import { parseAcct } from '@/misc/acct'; import MkButton from '@client/components/ui/button.vue'; -import MkInput from '@client/components/ui/input.vue'; -import MkSelect from '@client/components/ui/select.vue'; +import MkInput from '@client/components/form/input.vue'; +import MkSelect from '@client/components/form/select.vue'; import MkPagination from '@client/components/ui/pagination.vue'; import { acct } from '@client/filters/user'; import * as os from '@client/os'; diff --git a/src/client/pages/instance/ads.vue b/src/client/pages/instance/ads.vue index 50c8c29cbf..8742d0bda1 100644 --- a/src/client/pages/instance/ads.vue +++ b/src/client/pages/instance/ads.vue @@ -44,9 +44,9 @@ <script lang="ts"> import { defineComponent } from 'vue'; import MkButton from '@client/components/ui/button.vue'; -import MkInput from '@client/components/ui/input.vue'; -import MkTextarea from '@client/components/ui/textarea.vue'; -import MkRadio from '@client/components/ui/radio.vue'; +import MkInput from '@client/components/form/input.vue'; +import MkTextarea from '@client/components/form/textarea.vue'; +import MkRadio from '@client/components/form/radio.vue'; import * as os from '@client/os'; import * as symbols from '@client/symbols'; diff --git a/src/client/pages/instance/announcements.vue b/src/client/pages/instance/announcements.vue index d48e3737ad..35d676cf28 100644 --- a/src/client/pages/instance/announcements.vue +++ b/src/client/pages/instance/announcements.vue @@ -25,8 +25,8 @@ <script lang="ts"> import { defineComponent } from 'vue'; import MkButton from '@client/components/ui/button.vue'; -import MkInput from '@client/components/ui/input.vue'; -import MkTextarea from '@client/components/ui/textarea.vue'; +import MkInput from '@client/components/form/input.vue'; +import MkTextarea from '@client/components/form/textarea.vue'; import * as os from '@client/os'; import * as symbols from '@client/symbols'; diff --git a/src/client/pages/instance/bot-protection.vue b/src/client/pages/instance/bot-protection.vue index 449b8a233d..731f114cc2 100644 --- a/src/client/pages/instance/bot-protection.vue +++ b/src/client/pages/instance/bot-protection.vue @@ -9,43 +9,43 @@ </FormRadios> <template v-if="provider === 'hcaptcha'"> - <div class="_formItem _formNoConcat" v-sticky-container> - <div class="_formLabel">hCaptcha</div> + <div class="_debobigegoItem _debobigegoNoConcat" v-sticky-container> + <div class="_debobigegoLabel">hCaptcha</div> <div class="main"> - <FormInput v-model:value="hcaptchaSiteKey"> + <FormInput v-model="hcaptchaSiteKey"> <template #prefix><i class="fas fa-key"></i></template> <span>{{ $ts.hcaptchaSiteKey }}</span> </FormInput> - <FormInput v-model:value="hcaptchaSecretKey"> + <FormInput v-model="hcaptchaSecretKey"> <template #prefix><i class="fas fa-key"></i></template> <span>{{ $ts.hcaptchaSecretKey }}</span> </FormInput> </div> </div> - <div class="_formItem _formNoConcat" v-sticky-container> - <div class="_formLabel">{{ $ts.preview }}</div> - <div class="_formPanel" style="padding: var(--formContentHMargin);"> + <div class="_debobigegoItem _debobigegoNoConcat" v-sticky-container> + <div class="_debobigegoLabel">{{ $ts.preview }}</div> + <div class="_debobigegoPanel" style="padding: var(--debobigegoContentHMargin);"> <MkCaptcha provider="hcaptcha" :sitekey="hcaptchaSiteKey || '10000000-ffff-ffff-ffff-000000000001'"/> </div> </div> </template> <template v-else-if="provider === 'recaptcha'"> - <div class="_formItem _formNoConcat" v-sticky-container> - <div class="_formLabel">reCAPTCHA</div> + <div class="_debobigegoItem _debobigegoNoConcat" v-sticky-container> + <div class="_debobigegoLabel">reCAPTCHA</div> <div class="main"> - <FormInput v-model:value="recaptchaSiteKey"> + <FormInput v-model="recaptchaSiteKey"> <template #prefix><i class="fas fa-key"></i></template> <span>{{ $ts.recaptchaSiteKey }}</span> </FormInput> - <FormInput v-model:value="recaptchaSecretKey"> + <FormInput v-model="recaptchaSecretKey"> <template #prefix><i class="fas fa-key"></i></template> <span>{{ $ts.recaptchaSecretKey }}</span> </FormInput> </div> </div> - <div v-if="recaptchaSiteKey" class="_formItem _formNoConcat" v-sticky-container> - <div class="_formLabel">{{ $ts.preview }}</div> - <div class="_formPanel" style="padding: var(--formContentHMargin);"> + <div v-if="recaptchaSiteKey" class="_debobigegoItem _debobigegoNoConcat" v-sticky-container> + <div class="_debobigegoLabel">{{ $ts.preview }}</div> + <div class="_debobigegoPanel" style="padding: var(--debobigegoContentHMargin);"> <MkCaptcha provider="recaptcha" :sitekey="recaptchaSiteKey"/> </div> </div> @@ -58,13 +58,13 @@ <script lang="ts"> import { defineAsyncComponent, defineComponent } from 'vue'; -import FormRadios from '@client/components/form/radios.vue'; -import FormInput from '@client/components/form/input.vue'; -import FormButton from '@client/components/form/button.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormGroup from '@client/components/form/group.vue'; -import FormInfo from '@client/components/form/info.vue'; -import FormSuspense from '@client/components/form/suspense.vue'; +import FormRadios from '@client/components/debobigego/radios.vue'; +import FormInput from '@client/components/debobigego/input.vue'; +import FormButton from '@client/components/debobigego/button.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; +import FormInfo from '@client/components/debobigego/info.vue'; +import FormSuspense from '@client/components/debobigego/suspense.vue'; import * as os from '@client/os'; import * as symbols from '@client/symbols'; import { fetchInstance } from '@client/instance'; diff --git a/src/client/pages/instance/database.vue b/src/client/pages/instance/database.vue index a41d61ce2b..a8a1e9a54a 100644 --- a/src/client/pages/instance/database.vue +++ b/src/client/pages/instance/database.vue @@ -18,11 +18,11 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import FormSuspense from '@client/components/form/suspense.vue'; -import FormKeyValueView from '@client/components/form/key-value-view.vue'; -import FormLink from '@client/components/form/link.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormGroup from '@client/components/form/group.vue'; +import FormSuspense from '@client/components/debobigego/suspense.vue'; +import FormKeyValueView from '@client/components/debobigego/key-value-view.vue'; +import FormLink from '@client/components/debobigego/link.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; import * as os from '@client/os'; import * as symbols from '@client/symbols'; import bytes from '@client/filters/bytes'; diff --git a/src/client/pages/instance/email-settings.vue b/src/client/pages/instance/email-settings.vue index 9965a1420f..251354a43a 100644 --- a/src/client/pages/instance/email-settings.vue +++ b/src/client/pages/instance/email-settings.vue @@ -1,30 +1,30 @@ <template> <FormBase> <FormSuspense :p="init"> - <FormSwitch v-model:value="enableEmail">{{ $ts.enableEmail }}<template #desc>{{ $ts.emailConfigInfo }}</template></FormSwitch> + <FormSwitch v-model="enableEmail">{{ $ts.enableEmail }}<template #desc>{{ $ts.emailConfigInfo }}</template></FormSwitch> <template v-if="enableEmail"> - <FormInput v-model:value="email" type="email"> + <FormInput v-model="email" type="email"> <span>{{ $ts.emailAddress }}</span> </FormInput> - <div class="_formItem _formNoConcat" v-sticky-container> - <div class="_formLabel">{{ $ts.smtpConfig }}</div> + <div class="_debobigegoItem _debobigegoNoConcat" v-sticky-container> + <div class="_debobigegoLabel">{{ $ts.smtpConfig }}</div> <div class="main"> - <FormInput v-model:value="smtpHost"> + <FormInput v-model="smtpHost"> <span>{{ $ts.smtpHost }}</span> </FormInput> - <FormInput v-model:value="smtpPort" type="number"> + <FormInput v-model="smtpPort" type="number"> <span>{{ $ts.smtpPort }}</span> </FormInput> - <FormInput v-model:value="smtpUser"> + <FormInput v-model="smtpUser"> <span>{{ $ts.smtpUser }}</span> </FormInput> - <FormInput v-model:value="smtpPass" type="password"> + <FormInput v-model="smtpPass" type="password"> <span>{{ $ts.smtpPass }}</span> </FormInput> <FormInfo>{{ $ts.emptyToDisableSmtpAuth }}</FormInfo> - <FormSwitch v-model:value="smtpSecure">{{ $ts.smtpSecure }}<template #desc>{{ $ts.smtpSecureInfo }}</template></FormSwitch> + <FormSwitch v-model="smtpSecure">{{ $ts.smtpSecure }}<template #desc>{{ $ts.smtpSecureInfo }}</template></FormSwitch> </div> </div> @@ -38,13 +38,13 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import FormSwitch from '@client/components/form/switch.vue'; -import FormInput from '@client/components/form/input.vue'; -import FormButton from '@client/components/form/button.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormGroup from '@client/components/form/group.vue'; -import FormInfo from '@client/components/form/info.vue'; -import FormSuspense from '@client/components/form/suspense.vue'; +import FormSwitch from '@client/components/debobigego/switch.vue'; +import FormInput from '@client/components/debobigego/input.vue'; +import FormButton from '@client/components/debobigego/button.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; +import FormInfo from '@client/components/debobigego/info.vue'; +import FormSuspense from '@client/components/debobigego/suspense.vue'; import * as os from '@client/os'; import * as symbols from '@client/symbols'; import { fetchInstance } from '@client/instance'; diff --git a/src/client/pages/instance/emoji-edit-dialog.vue b/src/client/pages/instance/emoji-edit-dialog.vue index 7e9bdc80dd..bd8b2061ae 100644 --- a/src/client/pages/instance/emoji-edit-dialog.vue +++ b/src/client/pages/instance/emoji-edit-dialog.vue @@ -31,7 +31,7 @@ import { defineComponent } from 'vue'; import XModalWindow from '@client/components/ui/modal-window.vue'; import MkButton from '@client/components/ui/button.vue'; -import MkInput from '@client/components/ui/input.vue'; +import MkInput from '@client/components/form/input.vue'; import * as os from '@client/os'; import { unique } from '../../../prelude/array'; diff --git a/src/client/pages/instance/emojis.vue b/src/client/pages/instance/emojis.vue index 7badc9da02..6118d869e9 100644 --- a/src/client/pages/instance/emojis.vue +++ b/src/client/pages/instance/emojis.vue @@ -1,6 +1,6 @@ <template> <div class="ogwlenmc"> - <MkTab v-model:value="tab"> + <MkTab v-model="tab"> <option value="local">{{ $ts.local }}</option> <option value="remote">{{ $ts.remote }}</option> </MkTab> @@ -56,7 +56,7 @@ <script lang="ts"> import { computed, defineComponent } from 'vue'; import MkButton from '@client/components/ui/button.vue'; -import MkInput from '@client/components/ui/input.vue'; +import MkInput from '@client/components/form/input.vue'; import MkPagination from '@client/components/ui/pagination.vue'; import MkTab from '@client/components/tab.vue'; import { selectFile } from '@client/scripts/select-file'; diff --git a/src/client/pages/instance/file-dialog.vue b/src/client/pages/instance/file-dialog.vue index 8a03a11de7..02d83e5022 100644 --- a/src/client/pages/instance/file-dialog.vue +++ b/src/client/pages/instance/file-dialog.vue @@ -37,7 +37,7 @@ <script lang="ts"> import { computed, defineComponent } from 'vue'; import MkButton from '@client/components/ui/button.vue'; -import MkSwitch from '@client/components/ui/switch.vue'; +import MkSwitch from '@client/components/form/switch.vue'; import XModalWindow from '@client/components/ui/modal-window.vue'; import MkDriveFileThumbnail from '@client/components/drive-file-thumbnail.vue'; import Progress from '@client/scripts/loading'; diff --git a/src/client/pages/instance/files-settings.vue b/src/client/pages/instance/files-settings.vue index 614c7d4dbb..8bf4613a76 100644 --- a/src/client/pages/instance/files-settings.vue +++ b/src/client/pages/instance/files-settings.vue @@ -1,23 +1,23 @@ <template> <FormBase> <FormSuspense :p="init"> - <FormSwitch v-model:value="cacheRemoteFiles"> + <FormSwitch v-model="cacheRemoteFiles"> {{ $ts.cacheRemoteFiles }} <template #desc>{{ $ts.cacheRemoteFilesDescription }}</template> </FormSwitch> - <FormSwitch v-model:value="proxyRemoteFiles"> + <FormSwitch v-model="proxyRemoteFiles"> {{ $ts.proxyRemoteFiles }} <template #desc>{{ $ts.proxyRemoteFilesDescription }}</template> </FormSwitch> - <FormInput v-model:value="localDriveCapacityMb" type="number"> + <FormInput v-model="localDriveCapacityMb" type="number"> <span>{{ $ts.driveCapacityPerLocalAccount }}</span> <template #suffix>MB</template> <template #desc>{{ $ts.inMb }}</template> </FormInput> - <FormInput v-model:value="remoteDriveCapacityMb" type="number" :disabled="!cacheRemoteFiles"> + <FormInput v-model="remoteDriveCapacityMb" type="number" :disabled="!cacheRemoteFiles"> <span>{{ $ts.driveCapacityPerRemoteAccount }}</span> <template #suffix>MB</template> <template #desc>{{ $ts.inMb }}</template> @@ -30,12 +30,12 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import FormSwitch from '@client/components/form/switch.vue'; -import FormInput from '@client/components/form/input.vue'; -import FormButton from '@client/components/form/button.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormGroup from '@client/components/form/group.vue'; -import FormSuspense from '@client/components/form/suspense.vue'; +import FormSwitch from '@client/components/debobigego/switch.vue'; +import FormInput from '@client/components/debobigego/input.vue'; +import FormButton from '@client/components/debobigego/button.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; +import FormSuspense from '@client/components/debobigego/suspense.vue'; import * as os from '@client/os'; import * as symbols from '@client/symbols'; import { fetchInstance } from '@client/instance'; diff --git a/src/client/pages/instance/files.vue b/src/client/pages/instance/files.vue index b7f472b7c8..df2431ad02 100644 --- a/src/client/pages/instance/files.vue +++ b/src/client/pages/instance/files.vue @@ -63,8 +63,8 @@ <script lang="ts"> import { defineComponent } from 'vue'; import MkButton from '@client/components/ui/button.vue'; -import MkInput from '@client/components/ui/input.vue'; -import MkSelect from '@client/components/ui/select.vue'; +import MkInput from '@client/components/form/input.vue'; +import MkSelect from '@client/components/form/select.vue'; import MkPagination from '@client/components/ui/pagination.vue'; import MkDriveFileThumbnail from '@client/components/drive-file-thumbnail.vue'; import bytes from '@client/filters/bytes'; diff --git a/src/client/pages/instance/index.vue b/src/client/pages/instance/index.vue index 612bfa762a..657a654e51 100644 --- a/src/client/pages/instance/index.vue +++ b/src/client/pages/instance/index.vue @@ -3,8 +3,8 @@ <div class="nav" v-if="!narrow || page == null"> <FormBase> <FormGroup> - <div class="_formItem"> - <div class="_formPanel lxpfedzu"> + <div class="_debobigegoItem"> + <div class="_debobigegoPanel lxpfedzu"> <img :src="$instance.iconUrl || '/favicon.ico'" alt="" class="icon"/> </div> </div> @@ -56,10 +56,10 @@ <script lang="ts"> import { computed, defineAsyncComponent, defineComponent, nextTick, onMounted, reactive, ref, watch } from 'vue'; import { i18n } from '@client/i18n'; -import FormLink from '@client/components/form/link.vue'; -import FormGroup from '@client/components/form/group.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormButton from '@client/components/form/button.vue'; +import FormLink from '@client/components/debobigego/link.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormButton from '@client/components/debobigego/button.vue'; import { scroll } from '@client/scripts/scroll'; import * as symbols from '@client/symbols'; import * as os from '@client/os'; diff --git a/src/client/pages/instance/instance-block.vue b/src/client/pages/instance/instance-block.vue index ed5740f339..a00970d85b 100644 --- a/src/client/pages/instance/instance-block.vue +++ b/src/client/pages/instance/instance-block.vue @@ -1,7 +1,7 @@ <template> <FormBase> <FormSuspense :p="init"> - <FormTextarea v-model:value="blockedHosts"> + <FormTextarea v-model="blockedHosts"> <span>{{ $ts.blockedInstances }}</span> <template #desc>{{ $ts.blockedInstancesDescription }}</template> </FormTextarea> @@ -13,14 +13,14 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import FormSwitch from '@client/components/form/switch.vue'; -import FormInput from '@client/components/form/input.vue'; -import FormButton from '@client/components/form/button.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormGroup from '@client/components/form/group.vue'; -import FormTextarea from '@client/components/form/textarea.vue'; -import FormInfo from '@client/components/form/info.vue'; -import FormSuspense from '@client/components/form/suspense.vue'; +import FormSwitch from '@client/components/debobigego/switch.vue'; +import FormInput from '@client/components/debobigego/input.vue'; +import FormButton from '@client/components/debobigego/button.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; +import FormTextarea from '@client/components/debobigego/textarea.vue'; +import FormInfo from '@client/components/debobigego/info.vue'; +import FormSuspense from '@client/components/debobigego/suspense.vue'; import * as os from '@client/os'; import * as symbols from '@client/symbols'; import { fetchInstance } from '@client/instance'; diff --git a/src/client/pages/instance/instance.vue b/src/client/pages/instance/instance.vue index c39f0d1ecb..6117f090de 100644 --- a/src/client/pages/instance/instance.vue +++ b/src/client/pages/instance/instance.vue @@ -127,9 +127,9 @@ import { defineComponent, markRaw } from 'vue'; import Chart from 'chart.js'; import XModalWindow from '@client/components/ui/modal-window.vue'; import MkUsersDialog from '@client/components/users-dialog.vue'; -import MkSelect from '@client/components/ui/select.vue'; +import MkSelect from '@client/components/form/select.vue'; import MkButton from '@client/components/ui/button.vue'; -import MkSwitch from '@client/components/ui/switch.vue'; +import MkSwitch from '@client/components/form/switch.vue'; import MkInfo from '@client/components/ui/info.vue'; import bytes from '@client/filters/bytes'; import number from '@client/filters/number'; diff --git a/src/client/pages/instance/integrations-discord.vue b/src/client/pages/instance/integrations-discord.vue index c7508918f8..c33b24f17f 100644 --- a/src/client/pages/instance/integrations-discord.vue +++ b/src/client/pages/instance/integrations-discord.vue @@ -1,19 +1,19 @@ <template> <FormBase> <FormSuspense :p="init"> - <FormSwitch v-model:value="enableDiscordIntegration"> + <FormSwitch v-model="enableDiscordIntegration"> {{ $ts.enable }} </FormSwitch> <template v-if="enableDiscordIntegration"> <FormInfo>Callback URL: {{ `${url}/api/dc/cb` }}</FormInfo> - <FormInput v-model:value="discordClientId"> + <FormInput v-model="discordClientId"> <template #prefix><i class="fas fa-key"></i></template> Client ID </FormInput> - <FormInput v-model:value="discordClientSecret"> + <FormInput v-model="discordClientSecret"> <template #prefix><i class="fas fa-key"></i></template> Client Secret </FormInput> @@ -26,12 +26,12 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import FormSwitch from '@client/components/form/switch.vue'; -import FormInput from '@client/components/form/input.vue'; -import FormButton from '@client/components/form/button.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormInfo from '@client/components/form/info.vue'; -import FormSuspense from '@client/components/form/suspense.vue'; +import FormSwitch from '@client/components/debobigego/switch.vue'; +import FormInput from '@client/components/debobigego/input.vue'; +import FormButton from '@client/components/debobigego/button.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormInfo from '@client/components/debobigego/info.vue'; +import FormSuspense from '@client/components/debobigego/suspense.vue'; import * as os from '@client/os'; import * as symbols from '@client/symbols'; import { fetchInstance } from '@client/instance'; diff --git a/src/client/pages/instance/integrations-github.vue b/src/client/pages/instance/integrations-github.vue index 16586b15b4..cdf85868ff 100644 --- a/src/client/pages/instance/integrations-github.vue +++ b/src/client/pages/instance/integrations-github.vue @@ -1,19 +1,19 @@ <template> <FormBase> <FormSuspense :p="init"> - <FormSwitch v-model:value="enableGithubIntegration"> + <FormSwitch v-model="enableGithubIntegration"> {{ $ts.enable }} </FormSwitch> <template v-if="enableGithubIntegration"> <FormInfo>Callback URL: {{ `${url}/api/gh/cb` }}</FormInfo> - <FormInput v-model:value="githubClientId"> + <FormInput v-model="githubClientId"> <template #prefix><i class="fas fa-key"></i></template> Client ID </FormInput> - <FormInput v-model:value="githubClientSecret"> + <FormInput v-model="githubClientSecret"> <template #prefix><i class="fas fa-key"></i></template> Client Secret </FormInput> @@ -26,12 +26,12 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import FormSwitch from '@client/components/form/switch.vue'; -import FormInput from '@client/components/form/input.vue'; -import FormButton from '@client/components/form/button.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormInfo from '@client/components/form/info.vue'; -import FormSuspense from '@client/components/form/suspense.vue'; +import FormSwitch from '@client/components/debobigego/switch.vue'; +import FormInput from '@client/components/debobigego/input.vue'; +import FormButton from '@client/components/debobigego/button.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormInfo from '@client/components/debobigego/info.vue'; +import FormSuspense from '@client/components/debobigego/suspense.vue'; import * as os from '@client/os'; import * as symbols from '@client/symbols'; import { fetchInstance } from '@client/instance'; diff --git a/src/client/pages/instance/integrations-twitter.vue b/src/client/pages/instance/integrations-twitter.vue index b08b7f40a5..ed7d097d0a 100644 --- a/src/client/pages/instance/integrations-twitter.vue +++ b/src/client/pages/instance/integrations-twitter.vue @@ -1,19 +1,19 @@ <template> <FormBase> <FormSuspense :p="init"> - <FormSwitch v-model:value="enableTwitterIntegration"> + <FormSwitch v-model="enableTwitterIntegration"> {{ $ts.enable }} </FormSwitch> <template v-if="enableTwitterIntegration"> <FormInfo>Callback URL: {{ `${url}/api/tw/cb` }}</FormInfo> - <FormInput v-model:value="twitterConsumerKey"> + <FormInput v-model="twitterConsumerKey"> <template #prefix><i class="fas fa-key"></i></template> Consumer Key </FormInput> - <FormInput v-model:value="twitterConsumerSecret"> + <FormInput v-model="twitterConsumerSecret"> <template #prefix><i class="fas fa-key"></i></template> Consumer Secret </FormInput> @@ -26,12 +26,12 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import FormSwitch from '@client/components/form/switch.vue'; -import FormInput from '@client/components/form/input.vue'; -import FormButton from '@client/components/form/button.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormInfo from '@client/components/form/info.vue'; -import FormSuspense from '@client/components/form/suspense.vue'; +import FormSwitch from '@client/components/debobigego/switch.vue'; +import FormInput from '@client/components/debobigego/input.vue'; +import FormButton from '@client/components/debobigego/button.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormInfo from '@client/components/debobigego/info.vue'; +import FormSuspense from '@client/components/debobigego/suspense.vue'; import * as os from '@client/os'; import * as symbols from '@client/symbols'; import { fetchInstance } from '@client/instance'; diff --git a/src/client/pages/instance/integrations.vue b/src/client/pages/instance/integrations.vue index 7debedc367..bfd9e2f349 100644 --- a/src/client/pages/instance/integrations.vue +++ b/src/client/pages/instance/integrations.vue @@ -19,14 +19,14 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import FormLink from '@client/components/form/link.vue'; -import FormInput from '@client/components/form/input.vue'; -import FormButton from '@client/components/form/button.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormGroup from '@client/components/form/group.vue'; -import FormTextarea from '@client/components/form/textarea.vue'; -import FormInfo from '@client/components/form/info.vue'; -import FormSuspense from '@client/components/form/suspense.vue'; +import FormLink from '@client/components/debobigego/link.vue'; +import FormInput from '@client/components/debobigego/input.vue'; +import FormButton from '@client/components/debobigego/button.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; +import FormTextarea from '@client/components/debobigego/textarea.vue'; +import FormInfo from '@client/components/debobigego/info.vue'; +import FormSuspense from '@client/components/debobigego/suspense.vue'; import * as os from '@client/os'; import * as symbols from '@client/symbols'; import { fetchInstance } from '@client/instance'; diff --git a/src/client/pages/instance/logs.vue b/src/client/pages/instance/logs.vue index 4eee816f96..74aea0fc45 100644 --- a/src/client/pages/instance/logs.vue +++ b/src/client/pages/instance/logs.vue @@ -31,9 +31,9 @@ <script lang="ts"> import { defineComponent } from 'vue'; import MkButton from '@client/components/ui/button.vue'; -import MkInput from '@client/components/ui/input.vue'; -import MkSelect from '@client/components/ui/select.vue'; -import MkTextarea from '@client/components/ui/textarea.vue'; +import MkInput from '@client/components/form/input.vue'; +import MkSelect from '@client/components/form/select.vue'; +import MkTextarea from '@client/components/form/textarea.vue'; import * as os from '@client/os'; import * as symbols from '@client/symbols'; diff --git a/src/client/pages/instance/metrics.vue b/src/client/pages/instance/metrics.vue index 283b5939f0..1606063aee 100644 --- a/src/client/pages/instance/metrics.vue +++ b/src/client/pages/instance/metrics.vue @@ -1,7 +1,7 @@ <template> -<div class="_formItem"> - <div class="_formLabel"><i class="fas fa-microchip"></i> {{ $ts.cpuAndMemory }}</div> - <div class="_formPanel xhexznfu"> +<div class="_debobigegoItem"> + <div class="_debobigegoLabel"><i class="fas fa-microchip"></i> {{ $ts.cpuAndMemory }}</div> + <div class="_debobigegoPanel xhexznfu"> <div> <canvas :ref="cpumem"></canvas> </div> @@ -16,9 +16,9 @@ </div> </div> </div> -<div class="_formItem"> - <div class="_formLabel"><i class="fas fa-hdd"></i> {{ $ts.disk }}</div> - <div class="_formPanel xhexznfu"> +<div class="_debobigegoItem"> + <div class="_debobigegoLabel"><i class="fas fa-hdd"></i> {{ $ts.disk }}</div> + <div class="_debobigegoPanel xhexznfu"> <div> <canvas :ref="disk"></canvas> </div> @@ -33,9 +33,9 @@ </div> </div> </div> -<div class="_formItem"> - <div class="_formLabel"><i class="fas fa-exchange-alt"></i> {{ $ts.network }}</div> - <div class="_formPanel xhexznfu"> +<div class="_debobigegoItem"> + <div class="_debobigegoLabel"><i class="fas fa-exchange-alt"></i> {{ $ts.network }}</div> + <div class="_debobigegoPanel xhexznfu"> <div> <canvas :ref="net"></canvas> </div> @@ -54,8 +54,8 @@ import { defineComponent, markRaw } from 'vue'; import Chart from 'chart.js'; import MkButton from '@client/components/ui/button.vue'; -import MkSelect from '@client/components/ui/select.vue'; -import MkInput from '@client/components/ui/input.vue'; +import MkSelect from '@client/components/form/select.vue'; +import MkInput from '@client/components/form/input.vue'; import MkContainer from '@client/components/ui/container.vue'; import MkFolder from '@client/components/ui/folder.vue'; import MkwFederation from '../../widgets/federation.vue'; diff --git a/src/client/pages/instance/object-storage.vue b/src/client/pages/instance/object-storage.vue index 814aeb6e48..ba6a249685 100644 --- a/src/client/pages/instance/object-storage.vue +++ b/src/client/pages/instance/object-storage.vue @@ -1,59 +1,59 @@ <template> <FormBase> <FormSuspense :p="init"> - <FormSwitch v-model:value="useObjectStorage">{{ $ts.useObjectStorage }}</FormSwitch> + <FormSwitch v-model="useObjectStorage">{{ $ts.useObjectStorage }}</FormSwitch> <template v-if="useObjectStorage"> - <FormInput v-model:value="objectStorageBaseUrl"> + <FormInput v-model="objectStorageBaseUrl"> <span>{{ $ts.objectStorageBaseUrl }}</span> <template #desc>{{ $ts.objectStorageBaseUrlDesc }}</template> </FormInput> - <FormInput v-model:value="objectStorageBucket"> + <FormInput v-model="objectStorageBucket"> <span>{{ $ts.objectStorageBucket }}</span> <template #desc>{{ $ts.objectStorageBucketDesc }}</template> </FormInput> - <FormInput v-model:value="objectStoragePrefix"> + <FormInput v-model="objectStoragePrefix"> <span>{{ $ts.objectStoragePrefix }}</span> <template #desc>{{ $ts.objectStoragePrefixDesc }}</template> </FormInput> - <FormInput v-model:value="objectStorageEndpoint"> + <FormInput v-model="objectStorageEndpoint"> <span>{{ $ts.objectStorageEndpoint }}</span> <template #desc>{{ $ts.objectStorageEndpointDesc }}</template> </FormInput> - <FormInput v-model:value="objectStorageRegion"> + <FormInput v-model="objectStorageRegion"> <span>{{ $ts.objectStorageRegion }}</span> <template #desc>{{ $ts.objectStorageRegionDesc }}</template> </FormInput> - <FormInput v-model:value="objectStorageAccessKey"> + <FormInput v-model="objectStorageAccessKey"> <template #prefix><i class="fas fa-key"></i></template> <span>Access key</span> </FormInput> - <FormInput v-model:value="objectStorageSecretKey"> + <FormInput v-model="objectStorageSecretKey"> <template #prefix><i class="fas fa-key"></i></template> <span>Secret key</span> </FormInput> - <FormSwitch v-model:value="objectStorageUseSSL"> + <FormSwitch v-model="objectStorageUseSSL"> {{ $ts.objectStorageUseSSL }} <template #desc>{{ $ts.objectStorageUseSSLDesc }}</template> </FormSwitch> - <FormSwitch v-model:value="objectStorageUseProxy"> + <FormSwitch v-model="objectStorageUseProxy"> {{ $ts.objectStorageUseProxy }} <template #desc>{{ $ts.objectStorageUseProxyDesc }}</template> </FormSwitch> - <FormSwitch v-model:value="objectStorageSetPublicRead"> + <FormSwitch v-model="objectStorageSetPublicRead"> {{ $ts.objectStorageSetPublicRead }} </FormSwitch> - <FormSwitch v-model:value="objectStorageS3ForcePathStyle"> + <FormSwitch v-model="objectStorageS3ForcePathStyle"> s3ForcePathStyle </FormSwitch> </template> @@ -65,12 +65,12 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import FormSwitch from '@client/components/form/switch.vue'; -import FormInput from '@client/components/form/input.vue'; -import FormButton from '@client/components/form/button.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormGroup from '@client/components/form/group.vue'; -import FormSuspense from '@client/components/form/suspense.vue'; +import FormSwitch from '@client/components/debobigego/switch.vue'; +import FormInput from '@client/components/debobigego/input.vue'; +import FormButton from '@client/components/debobigego/button.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; +import FormSuspense from '@client/components/debobigego/suspense.vue'; import * as os from '@client/os'; import * as symbols from '@client/symbols'; import { fetchInstance } from '@client/instance'; diff --git a/src/client/pages/instance/other-settings.vue b/src/client/pages/instance/other-settings.vue index 4fa80b2b2c..b9f9ce30f7 100644 --- a/src/client/pages/instance/other-settings.vue +++ b/src/client/pages/instance/other-settings.vue @@ -2,17 +2,17 @@ <FormBase> <FormSuspense :p="init"> <FormGroup> - <FormInput v-model:value="summalyProxy"> + <FormInput v-model="summalyProxy"> <template #prefix><i class="fas fa-link"></i></template> Summaly Proxy URL </FormInput> </FormGroup> <FormGroup> - <FormInput v-model:value="deeplAuthKey"> + <FormInput v-model="deeplAuthKey"> <template #prefix><i class="fas fa-key"></i></template> DeepL Auth Key </FormInput> - <FormSwitch v-model:value="deeplIsPro"> + <FormSwitch v-model="deeplIsPro"> Pro account </FormSwitch> </FormGroup> @@ -23,12 +23,12 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import FormSwitch from '@client/components/form/switch.vue'; -import FormInput from '@client/components/form/input.vue'; -import FormButton from '@client/components/form/button.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormGroup from '@client/components/form/group.vue'; -import FormSuspense from '@client/components/form/suspense.vue'; +import FormSwitch from '@client/components/debobigego/switch.vue'; +import FormInput from '@client/components/debobigego/input.vue'; +import FormButton from '@client/components/debobigego/button.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; +import FormSuspense from '@client/components/debobigego/suspense.vue'; import * as os from '@client/os'; import * as symbols from '@client/symbols'; import { fetchInstance } from '@client/instance'; diff --git a/src/client/pages/instance/overview.vue b/src/client/pages/instance/overview.vue index 0d7a5d1501..28dcfc03bf 100644 --- a/src/client/pages/instance/overview.vue +++ b/src/client/pages/instance/overview.vue @@ -17,8 +17,8 @@ </FormGroup> </FormSuspense> - <div class="_formItem"> - <div class="_formPanel"> + <div class="_debobigegoItem"> + <div class="_debobigegoPanel"> <MkInstanceStats :chart-limit="300" :detailed="true"/> </div> </div> @@ -47,18 +47,18 @@ <script lang="ts"> import { computed, defineComponent, markRaw } from 'vue'; -import FormKeyValueView from '@client/components/form/key-value-view.vue'; -import FormInput from '@client/components/form/input.vue'; -import FormButton from '@client/components/form/button.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormGroup from '@client/components/form/group.vue'; -import FormTextarea from '@client/components/form/textarea.vue'; -import FormInfo from '@client/components/form/info.vue'; -import FormSuspense from '@client/components/form/suspense.vue'; +import FormKeyValueView from '@client/components/debobigego/key-value-view.vue'; +import FormInput from '@client/components/debobigego/input.vue'; +import FormButton from '@client/components/debobigego/button.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; +import FormTextarea from '@client/components/debobigego/textarea.vue'; +import FormInfo from '@client/components/debobigego/info.vue'; +import FormSuspense from '@client/components/debobigego/suspense.vue'; import MkInstanceStats from '@client/components/instance-stats.vue'; import MkButton from '@client/components/ui/button.vue'; -import MkSelect from '@client/components/ui/select.vue'; -import MkInput from '@client/components/ui/input.vue'; +import MkSelect from '@client/components/form/select.vue'; +import MkInput from '@client/components/form/input.vue'; import MkContainer from '@client/components/ui/container.vue'; import MkFolder from '@client/components/ui/folder.vue'; import { version, url } from '@client/config'; diff --git a/src/client/pages/instance/proxy-account.vue b/src/client/pages/instance/proxy-account.vue index 3e2df8dcb4..a80ecccb05 100644 --- a/src/client/pages/instance/proxy-account.vue +++ b/src/client/pages/instance/proxy-account.vue @@ -16,14 +16,14 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import FormKeyValueView from '@client/components/form/key-value-view.vue'; -import FormInput from '@client/components/form/input.vue'; -import FormButton from '@client/components/form/button.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormGroup from '@client/components/form/group.vue'; -import FormTextarea from '@client/components/form/textarea.vue'; -import FormInfo from '@client/components/form/info.vue'; -import FormSuspense from '@client/components/form/suspense.vue'; +import FormKeyValueView from '@client/components/debobigego/key-value-view.vue'; +import FormInput from '@client/components/debobigego/input.vue'; +import FormButton from '@client/components/debobigego/button.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; +import FormTextarea from '@client/components/debobigego/textarea.vue'; +import FormInfo from '@client/components/debobigego/info.vue'; +import FormSuspense from '@client/components/debobigego/suspense.vue'; import * as os from '@client/os'; import * as symbols from '@client/symbols'; import { fetchInstance } from '@client/instance'; diff --git a/src/client/pages/instance/queue.chart.vue b/src/client/pages/instance/queue.chart.vue index 53d790598a..887fe9a574 100644 --- a/src/client/pages/instance/queue.chart.vue +++ b/src/client/pages/instance/queue.chart.vue @@ -1,7 +1,7 @@ <template> -<div class="_formItem"> - <div class="_formLabel"><slot name="title"></slot></div> - <div class="_formPanel pumxzjhg"> +<div class="_debobigegoItem"> + <div class="_debobigegoLabel"><slot name="title"></slot></div> + <div class="_debobigegoPanel pumxzjhg"> <div class="_table status"> <div class="_row"> <div class="_cell"><div class="_label">Process</div>{{ number(activeSincePrevTick) }}</div> diff --git a/src/client/pages/instance/queue.vue b/src/client/pages/instance/queue.vue index e8ec0bc97d..031bda2bed 100644 --- a/src/client/pages/instance/queue.vue +++ b/src/client/pages/instance/queue.vue @@ -14,8 +14,8 @@ import { defineComponent, markRaw } from 'vue'; import MkButton from '@client/components/ui/button.vue'; import XQueue from './queue.chart.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormButton from '@client/components/form/button.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormButton from '@client/components/debobigego/button.vue'; import * as os from '@client/os'; import * as symbols from '@client/symbols'; diff --git a/src/client/pages/instance/relays.vue b/src/client/pages/instance/relays.vue index a3e4e7d1da..cb9b27a625 100644 --- a/src/client/pages/instance/relays.vue +++ b/src/client/pages/instance/relays.vue @@ -2,8 +2,8 @@ <FormBase class="relaycxt"> <FormButton @click="addRelay" primary><i class="fas fa-plus"></i> {{ $ts.addRelay }}</FormButton> - <div class="_formItem" v-for="relay in relays" :key="relay.inbox"> - <div class="_formPanel" style="padding: 16px;"> + <div class="_debobigegoItem" v-for="relay in relays" :key="relay.inbox"> + <div class="_debobigegoPanel" style="padding: 16px;"> <div>{{ relay.inbox }}</div> <div>{{ $t(`_relayStatus.${relay.status}`) }}</div> <MkButton class="button" inline danger @click="remove(relay.inbox)"><i class="fas fa-trash-alt"></i> {{ $ts.remove }}</MkButton> @@ -15,9 +15,9 @@ <script lang="ts"> import { defineComponent } from 'vue'; import MkButton from '@client/components/ui/button.vue'; -import MkInput from '@client/components/ui/input.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormButton from '@client/components/form/button.vue'; +import MkInput from '@client/components/form/input.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormButton from '@client/components/debobigego/button.vue'; import * as os from '@client/os'; import * as symbols from '@client/symbols'; diff --git a/src/client/pages/instance/security.vue b/src/client/pages/instance/security.vue index e3397a113b..53f923643a 100644 --- a/src/client/pages/instance/security.vue +++ b/src/client/pages/instance/security.vue @@ -8,7 +8,7 @@ <template #suffix v-else>{{ $ts.none }} ({{ $ts.notRecommended }})</template> </FormLink> - <FormSwitch v-model:value="enableRegistration">{{ $ts.enableRegistration }}</FormSwitch> + <FormSwitch v-model="enableRegistration">{{ $ts.enableRegistration }}</FormSwitch> <FormButton @click="save" primary><i class="fas fa-save"></i> {{ $ts.save }}</FormButton> </FormSuspense> @@ -17,13 +17,13 @@ <script lang="ts"> import { defineAsyncComponent, defineComponent } from 'vue'; -import FormLink from '@client/components/form/link.vue'; -import FormSwitch from '@client/components/form/switch.vue'; -import FormButton from '@client/components/form/button.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormGroup from '@client/components/form/group.vue'; -import FormInfo from '@client/components/form/info.vue'; -import FormSuspense from '@client/components/form/suspense.vue'; +import FormLink from '@client/components/debobigego/link.vue'; +import FormSwitch from '@client/components/debobigego/switch.vue'; +import FormButton from '@client/components/debobigego/button.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; +import FormInfo from '@client/components/debobigego/info.vue'; +import FormSuspense from '@client/components/debobigego/suspense.vue'; import * as os from '@client/os'; import * as symbols from '@client/symbols'; import { fetchInstance } from '@client/instance'; diff --git a/src/client/pages/instance/service-worker.vue b/src/client/pages/instance/service-worker.vue index a52932bb75..9fa10def07 100644 --- a/src/client/pages/instance/service-worker.vue +++ b/src/client/pages/instance/service-worker.vue @@ -1,18 +1,18 @@ <template> <FormBase> <FormSuspense :p="init"> - <FormSwitch v-model:value="enableServiceWorker"> + <FormSwitch v-model="enableServiceWorker"> {{ $ts.enableServiceworker }} <template #desc>{{ $ts.serviceworkerInfo }}</template> </FormSwitch> <template v-if="enableServiceWorker"> - <FormInput v-model:value="swPublicKey"> + <FormInput v-model="swPublicKey"> <template #prefix><i class="fas fa-key"></i></template> Public key </FormInput> - <FormInput v-model:value="swPrivateKey"> + <FormInput v-model="swPrivateKey"> <template #prefix><i class="fas fa-key"></i></template> Private key </FormInput> @@ -25,12 +25,12 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import FormSwitch from '@client/components/form/switch.vue'; -import FormInput from '@client/components/form/input.vue'; -import FormButton from '@client/components/form/button.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormGroup from '@client/components/form/group.vue'; -import FormSuspense from '@client/components/form/suspense.vue'; +import FormSwitch from '@client/components/debobigego/switch.vue'; +import FormInput from '@client/components/debobigego/input.vue'; +import FormButton from '@client/components/debobigego/button.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; +import FormSuspense from '@client/components/debobigego/suspense.vue'; import * as os from '@client/os'; import * as symbols from '@client/symbols'; import { fetchInstance } from '@client/instance'; diff --git a/src/client/pages/instance/settings.vue b/src/client/pages/instance/settings.vue index b68d784897..2ce9361d08 100644 --- a/src/client/pages/instance/settings.vue +++ b/src/client/pages/instance/settings.vue @@ -1,50 +1,50 @@ <template> <FormBase> <FormSuspense :p="init"> - <FormInput v-model:value="name"> + <FormInput v-model="name"> <span>{{ $ts.instanceName }}</span> </FormInput> - <FormTextarea v-model:value="description"> + <FormTextarea v-model="description"> <span>{{ $ts.instanceDescription }}</span> </FormTextarea> - <FormInput v-model:value="iconUrl"> + <FormInput v-model="iconUrl"> <template #prefix><i class="fas fa-link"></i></template> <span>{{ $ts.iconUrl }}</span> </FormInput> - <FormInput v-model:value="bannerUrl"> + <FormInput v-model="bannerUrl"> <template #prefix><i class="fas fa-link"></i></template> <span>{{ $ts.bannerUrl }}</span> </FormInput> - <FormInput v-model:value="backgroundImageUrl"> + <FormInput v-model="backgroundImageUrl"> <template #prefix><i class="fas fa-link"></i></template> <span>{{ $ts.backgroundImageUrl }}</span> </FormInput> - <FormInput v-model:value="tosUrl"> + <FormInput v-model="tosUrl"> <template #prefix><i class="fas fa-link"></i></template> <span>{{ $ts.tosUrl }}</span> </FormInput> - <FormInput v-model:value="maintainerName"> + <FormInput v-model="maintainerName"> <span>{{ $ts.maintainerName }}</span> </FormInput> - <FormInput v-model:value="maintainerEmail" type="email"> + <FormInput v-model="maintainerEmail" type="email"> <template #prefix><i class="fas fa-envelope"></i></template> <span>{{ $ts.maintainerEmail }}</span> </FormInput> - <FormInput v-model:value="maxNoteTextLength" type="number"> + <FormInput v-model="maxNoteTextLength" type="number"> <template #prefix><i class="fas fa-pencil-alt"></i></template> <span>{{ $ts.maxNoteTextLength }}</span> </FormInput> - <FormSwitch v-model:value="enableLocalTimeline">{{ $ts.enableLocalTimeline }}</FormSwitch> - <FormSwitch v-model:value="enableGlobalTimeline">{{ $ts.enableGlobalTimeline }}</FormSwitch> + <FormSwitch v-model="enableLocalTimeline">{{ $ts.enableLocalTimeline }}</FormSwitch> + <FormSwitch v-model="enableGlobalTimeline">{{ $ts.enableGlobalTimeline }}</FormSwitch> <FormInfo>{{ $ts.disablingTimelinesInfo }}</FormInfo> <FormButton @click="save" primary><i class="fas fa-save"></i> {{ $ts.save }}</FormButton> @@ -54,14 +54,14 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import FormSwitch from '@client/components/form/switch.vue'; -import FormInput from '@client/components/form/input.vue'; -import FormButton from '@client/components/form/button.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormGroup from '@client/components/form/group.vue'; -import FormTextarea from '@client/components/form/textarea.vue'; -import FormInfo from '@client/components/form/info.vue'; -import FormSuspense from '@client/components/form/suspense.vue'; +import FormSwitch from '@client/components/debobigego/switch.vue'; +import FormInput from '@client/components/debobigego/input.vue'; +import FormButton from '@client/components/debobigego/button.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; +import FormTextarea from '@client/components/debobigego/textarea.vue'; +import FormInfo from '@client/components/debobigego/info.vue'; +import FormSuspense from '@client/components/debobigego/suspense.vue'; import * as os from '@client/os'; import * as symbols from '@client/symbols'; import { fetchInstance } from '@client/instance'; diff --git a/src/client/pages/instance/users.vue b/src/client/pages/instance/users.vue index 8db62683ba..7671387f01 100644 --- a/src/client/pages/instance/users.vue +++ b/src/client/pages/instance/users.vue @@ -67,8 +67,8 @@ <script lang="ts"> import { defineComponent } from 'vue'; import MkButton from '@client/components/ui/button.vue'; -import MkInput from '@client/components/ui/input.vue'; -import MkSelect from '@client/components/ui/select.vue'; +import MkInput from '@client/components/form/input.vue'; +import MkSelect from '@client/components/form/select.vue'; import MkPagination from '@client/components/ui/pagination.vue'; import { acct } from '@client/filters/user'; import * as os from '@client/os'; diff --git a/src/client/pages/mfm-cheat-sheet.vue b/src/client/pages/mfm-cheat-sheet.vue index 314b5e2a5f..5ff4317627 100644 --- a/src/client/pages/mfm-cheat-sheet.vue +++ b/src/client/pages/mfm-cheat-sheet.vue @@ -286,7 +286,7 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import MkTextarea from '@client/components/ui/textarea.vue'; +import MkTextarea from '@client/components/form/textarea.vue'; import * as symbols from '@client/symbols'; export default defineComponent({ diff --git a/src/client/pages/my-antennas/editor.vue b/src/client/pages/my-antennas/editor.vue index 882d48e643..972a4e0243 100644 --- a/src/client/pages/my-antennas/editor.vue +++ b/src/client/pages/my-antennas/editor.vue @@ -1,7 +1,7 @@ <template> <div class="shaynizk"> <div class="form"> - <MkInput v-model="name" class="_inputNoTopMargin"> + <MkInput v-model="name" class=""> <template #label>{{ $ts.name }}</template> </MkInput> <MkSelect v-model="src"> @@ -47,10 +47,10 @@ <script lang="ts"> import { defineComponent } from 'vue'; import MkButton from '@client/components/ui/button.vue'; -import MkInput from '@client/components/ui/input.vue'; -import MkTextarea from '@client/components/ui/textarea.vue'; -import MkSelect from '@client/components/ui/select.vue'; -import MkSwitch from '@client/components/ui/switch.vue'; +import MkInput from '@client/components/form/input.vue'; +import MkTextarea from '@client/components/form/textarea.vue'; +import MkSelect from '@client/components/form/select.vue'; +import MkSwitch from '@client/components/form/switch.vue'; import { getAcct } from '@/misc/acct'; import * as os from '@client/os'; diff --git a/src/client/pages/my-groups/index.vue b/src/client/pages/my-groups/index.vue index 9f153ff9cc..34f82f8a71 100644 --- a/src/client/pages/my-groups/index.vue +++ b/src/client/pages/my-groups/index.vue @@ -1,7 +1,7 @@ <template> <div class=""> <div class="_section" style="padding: 0;"> - <MkTab v-model:value="tab"> + <MkTab v-model="tab"> <option value="owned">{{ $ts.ownedGroups }}</option> <option value="joined">{{ $ts.joinedGroups }}</option> <option value="invites"><i class="fas fa-envelope-open-text"></i> {{ $ts.invites }}</option> diff --git a/src/client/pages/page-editor/els/page-editor.el.button.vue b/src/client/pages/page-editor/els/page-editor.el.button.vue index 3a43817cf6..85e9d7e711 100644 --- a/src/client/pages/page-editor/els/page-editor.el.button.vue +++ b/src/client/pages/page-editor/els/page-editor.el.button.vue @@ -40,9 +40,9 @@ <script lang="ts"> import { defineComponent } from 'vue'; import XContainer from '../page-editor.container.vue'; -import MkSelect from '@client/components/ui/select.vue'; -import MkInput from '@client/components/ui/input.vue'; -import MkSwitch from '@client/components/ui/switch.vue'; +import MkSelect from '@client/components/form/select.vue'; +import MkInput from '@client/components/form/input.vue'; +import MkSwitch from '@client/components/form/switch.vue'; import * as os from '@client/os'; export default defineComponent({ diff --git a/src/client/pages/page-editor/els/page-editor.el.canvas.vue b/src/client/pages/page-editor/els/page-editor.el.canvas.vue index d8d5b990ca..c40d69a7c1 100644 --- a/src/client/pages/page-editor/els/page-editor.el.canvas.vue +++ b/src/client/pages/page-editor/els/page-editor.el.canvas.vue @@ -22,7 +22,7 @@ <script lang="ts"> import { defineComponent } from 'vue'; import XContainer from '../page-editor.container.vue'; -import MkInput from '@client/components/ui/input.vue'; +import MkInput from '@client/components/form/input.vue'; import * as os from '@client/os'; export default defineComponent({ diff --git a/src/client/pages/page-editor/els/page-editor.el.counter.vue b/src/client/pages/page-editor/els/page-editor.el.counter.vue index 973de50fc2..de7994e3ba 100644 --- a/src/client/pages/page-editor/els/page-editor.el.counter.vue +++ b/src/client/pages/page-editor/els/page-editor.el.counter.vue @@ -20,7 +20,7 @@ <script lang="ts"> import { defineComponent } from 'vue'; import XContainer from '../page-editor.container.vue'; -import MkInput from '@client/components/ui/input.vue'; +import MkInput from '@client/components/form/input.vue'; import * as os from '@client/os'; export default defineComponent({ diff --git a/src/client/pages/page-editor/els/page-editor.el.if.vue b/src/client/pages/page-editor/els/page-editor.el.if.vue index 6eb0c7709f..52f4dac22e 100644 --- a/src/client/pages/page-editor/els/page-editor.el.if.vue +++ b/src/client/pages/page-editor/els/page-editor.el.if.vue @@ -19,7 +19,7 @@ </optgroup> </MkSelect> - <XBlocks class="children" v-model:value="value.children" :hpml="hpml"/> + <XBlocks class="children" v-model="value.children" :hpml="hpml"/> </section> </XContainer> </template> @@ -28,7 +28,7 @@ import { defineComponent, defineAsyncComponent } from 'vue'; import { v4 as uuid } from 'uuid'; import XContainer from '../page-editor.container.vue'; -import MkSelect from '@client/components/ui/select.vue'; +import MkSelect from '@client/components/form/select.vue'; import * as os from '@client/os'; export default defineComponent({ diff --git a/src/client/pages/page-editor/els/page-editor.el.note.vue b/src/client/pages/page-editor/els/page-editor.el.note.vue index 5766564c1a..9feec395b7 100644 --- a/src/client/pages/page-editor/els/page-editor.el.note.vue +++ b/src/client/pages/page-editor/els/page-editor.el.note.vue @@ -18,8 +18,8 @@ <script lang="ts"> import { defineComponent } from 'vue'; import XContainer from '../page-editor.container.vue'; -import MkInput from '@client/components/ui/input.vue'; -import MkSwitch from '@client/components/ui/switch.vue'; +import MkInput from '@client/components/form/input.vue'; +import MkSwitch from '@client/components/form/switch.vue'; import XNote from '@client/components/note.vue'; import XNoteDetailed from '@client/components/note-detailed.vue'; import * as os from '@client/os'; diff --git a/src/client/pages/page-editor/els/page-editor.el.number-input.vue b/src/client/pages/page-editor/els/page-editor.el.number-input.vue index 892e7e1caa..57b1397824 100644 --- a/src/client/pages/page-editor/els/page-editor.el.number-input.vue +++ b/src/client/pages/page-editor/els/page-editor.el.number-input.vue @@ -20,7 +20,7 @@ <script lang="ts"> import { defineComponent } from 'vue'; import XContainer from '../page-editor.container.vue'; -import MkInput from '@client/components/ui/input.vue'; +import MkInput from '@client/components/form/input.vue'; import * as os from '@client/os'; export default defineComponent({ diff --git a/src/client/pages/page-editor/els/page-editor.el.post.vue b/src/client/pages/page-editor/els/page-editor.el.post.vue index 4215b159d3..e21ccfd345 100644 --- a/src/client/pages/page-editor/els/page-editor.el.post.vue +++ b/src/client/pages/page-editor/els/page-editor.el.post.vue @@ -13,9 +13,9 @@ <script lang="ts"> import { defineComponent } from 'vue'; import XContainer from '../page-editor.container.vue'; -import MkTextarea from '@client/components/ui/textarea.vue'; -import MkInput from '@client/components/ui/input.vue'; -import MkSwitch from '@client/components/ui/switch.vue'; +import MkTextarea from '@client/components/form/textarea.vue'; +import MkInput from '@client/components/form/input.vue'; +import MkSwitch from '@client/components/form/switch.vue'; import * as os from '@client/os'; export default defineComponent({ diff --git a/src/client/pages/page-editor/els/page-editor.el.radio-button.vue b/src/client/pages/page-editor/els/page-editor.el.radio-button.vue index 88be96f35d..62fb231f79 100644 --- a/src/client/pages/page-editor/els/page-editor.el.radio-button.vue +++ b/src/client/pages/page-editor/els/page-editor.el.radio-button.vue @@ -14,8 +14,8 @@ <script lang="ts"> import { defineComponent } from 'vue'; import XContainer from '../page-editor.container.vue'; -import MkTextarea from '@client/components/ui/textarea.vue'; -import MkInput from '@client/components/ui/input.vue'; +import MkTextarea from '@client/components/form/textarea.vue'; +import MkInput from '@client/components/form/input.vue'; import * as os from '@client/os'; export default defineComponent({ diff --git a/src/client/pages/page-editor/els/page-editor.el.section.vue b/src/client/pages/page-editor/els/page-editor.el.section.vue index 16ef2598f9..75bdf120c0 100644 --- a/src/client/pages/page-editor/els/page-editor.el.section.vue +++ b/src/client/pages/page-editor/els/page-editor.el.section.vue @@ -11,7 +11,7 @@ </template> <section class="ilrvjyvi"> - <XBlocks class="children" v-model:value="value.children" :hpml="hpml"/> + <XBlocks class="children" v-model="value.children" :hpml="hpml"/> </section> </XContainer> </template> diff --git a/src/client/pages/page-editor/els/page-editor.el.switch.vue b/src/client/pages/page-editor/els/page-editor.el.switch.vue index ade1291410..cf15f58c82 100644 --- a/src/client/pages/page-editor/els/page-editor.el.switch.vue +++ b/src/client/pages/page-editor/els/page-editor.el.switch.vue @@ -13,8 +13,8 @@ <script lang="ts"> import { defineComponent } from 'vue'; import XContainer from '../page-editor.container.vue'; -import MkSwitch from '@client/components/ui/switch.vue'; -import MkInput from '@client/components/ui/input.vue'; +import MkSwitch from '@client/components/form/switch.vue'; +import MkInput from '@client/components/form/input.vue'; import * as os from '@client/os'; export default defineComponent({ diff --git a/src/client/pages/page-editor/els/page-editor.el.text-input.vue b/src/client/pages/page-editor/els/page-editor.el.text-input.vue index 3c8fcc04af..210199befd 100644 --- a/src/client/pages/page-editor/els/page-editor.el.text-input.vue +++ b/src/client/pages/page-editor/els/page-editor.el.text-input.vue @@ -13,7 +13,7 @@ <script lang="ts"> import { defineComponent } from 'vue'; import XContainer from '../page-editor.container.vue'; -import MkInput from '@client/components/ui/input.vue'; +import MkInput from '@client/components/form/input.vue'; import * as os from '@client/os'; export default defineComponent({ diff --git a/src/client/pages/page-editor/els/page-editor.el.textarea-input.vue b/src/client/pages/page-editor/els/page-editor.el.textarea-input.vue index a4fbb08ffe..14f36db2a1 100644 --- a/src/client/pages/page-editor/els/page-editor.el.textarea-input.vue +++ b/src/client/pages/page-editor/els/page-editor.el.textarea-input.vue @@ -13,8 +13,8 @@ <script lang="ts"> import { defineComponent } from 'vue'; import XContainer from '../page-editor.container.vue'; -import MkTextarea from '@client/components/ui/textarea.vue'; -import MkInput from '@client/components/ui/input.vue'; +import MkTextarea from '@client/components/form/textarea.vue'; +import MkInput from '@client/components/form/input.vue'; import * as os from '@client/os'; export default defineComponent({ diff --git a/src/client/pages/page-editor/page-editor.blocks.vue b/src/client/pages/page-editor/page-editor.blocks.vue index 0065b16c8c..c27162a26e 100644 --- a/src/client/pages/page-editor/page-editor.blocks.vue +++ b/src/client/pages/page-editor/page-editor.blocks.vue @@ -32,7 +32,7 @@ export default defineComponent({ }, props: { - value: { + modelValue: { type: Array, required: true }, @@ -41,15 +41,15 @@ export default defineComponent({ }, }, - emits: ['update:value'], + emits: ['update:modelValue'], computed: { blocks: { get() { - return this.value; + return this.modelValue; }, set(value) { - this.$emit('update:value', value); + this.$emit('update:modelValue', value); } } }, @@ -62,17 +62,16 @@ export default defineComponent({ v, ...this.blocks.slice(i + 1) ]; - this.$emit('update:value', newValue); + this.$emit('update:modelValue', newValue); }, removeItem(el) { - console.log(el); const i = this.blocks.findIndex(x => x.id === el.id); const newValue = [ ...this.blocks.slice(0, i), ...this.blocks.slice(i + 1) ]; - this.$emit('update:value', newValue); + this.$emit('update:modelValue', newValue); }, } }); diff --git a/src/client/pages/page-editor/page-editor.script-block.vue b/src/client/pages/page-editor/page-editor.script-block.vue index fedcd7b317..3313fc1ba9 100644 --- a/src/client/pages/page-editor/page-editor.script-block.vue +++ b/src/client/pages/page-editor/page-editor.script-block.vue @@ -7,23 +7,23 @@ </button> </template> - <section v-if="value.type === null" class="pbglfege" @click="changeType()"> + <section v-if="modelValue.type === null" class="pbglfege" @click="changeType()"> {{ $ts._pages.script.emptySlot }} </section> - <section v-else-if="value.type === 'text'" class="tbwccoaw"> - <input v-model="value.value"/> + <section v-else-if="modelValue.type === 'text'" class="tbwccoaw"> + <input v-model="modelValue.value"/> </section> - <section v-else-if="value.type === 'multiLineText'" class="tbwccoaw"> - <textarea v-model="value.value"></textarea> + <section v-else-if="modelValue.type === 'multiLineText'" class="tbwccoaw"> + <textarea v-model="modelValue.value"></textarea> </section> - <section v-else-if="value.type === 'textList'" class="tbwccoaw"> - <textarea v-model="value.value" :placeholder="$ts._pages.script.blocks._textList.info"></textarea> + <section v-else-if="modelValue.type === 'textList'" class="tbwccoaw"> + <textarea v-model="modelValue.value" :placeholder="$ts._pages.script.blocks._textList.info"></textarea> </section> - <section v-else-if="value.type === 'number'" class="tbwccoaw"> - <input v-model="value.value" type="number"/> + <section v-else-if="modelValue.type === 'number'" class="tbwccoaw"> + <input v-model="modelValue.value" type="number"/> </section> - <section v-else-if="value.type === 'ref'" class="hpdwcrvs"> - <select v-model="value.value"> + <section v-else-if="modelValue.type === 'ref'" class="hpdwcrvs"> + <select v-model="modelValue.value"> <option v-for="v in hpml.getVarsByType(getExpectedType ? getExpectedType() : null).filter(x => x.name !== name)" :value="v.name">{{ v.name }}</option> <optgroup :label="$ts._pages.script.argVariables"> <option v-for="v in fnSlots" :value="v.name">{{ v.name }}</option> @@ -36,21 +36,21 @@ </optgroup> </select> </section> - <section v-else-if="value.type === 'aiScriptVar'" class="tbwccoaw"> - <input v-model="value.value"/> + <section v-else-if="modelValue.type === 'aiScriptVar'" class="tbwccoaw"> + <input v-model="modelValue.value"/> </section> - <section v-else-if="value.type === 'fn'" class="" style="padding:0 16px 16px 16px;"> + <section v-else-if="modelValue.type === 'fn'" class="" style="padding:0 16px 16px 16px;"> <MkTextarea v-model="slots"> <template #label>{{ $ts._pages.script.blocks._fn.slots }}</template> <template #caption>{{ $t('_pages.script.blocks._fn.slots-info') }}</template> </MkTextarea> - <XV v-if="value.value.expression" v-model:value="value.value.expression" :title="$t(`_pages.script.blocks._fn.arg1`)" :get-expected-type="() => null" :hpml="hpml" :fn-slots="value.value.slots" :name="name"/> + <XV v-if="modelValue.value.expression" v-model="modelValue.value.expression" :title="$t(`_pages.script.blocks._fn.arg1`)" :get-expected-type="() => null" :hpml="hpml" :fn-slots="value.value.slots" :name="name"/> </section> - <section v-else-if="value.type.startsWith('fn:')" class="" style="padding:16px;"> - <XV v-for="(x, i) in value.args" v-model:value="value.args[i]" :title="hpml.getVarByName(value.type.split(':')[1]).value.slots[i].name" :get-expected-type="() => null" :hpml="hpml" :name="name" :key="i"/> + <section v-else-if="modelValue.type.startsWith('fn:')" class="" style="padding:16px;"> + <XV v-for="(x, i) in modelValue.args" v-model="value.args[i]" :title="hpml.getVarByName(modelValue.type.split(':')[1]).value.slots[i].name" :get-expected-type="() => null" :hpml="hpml" :name="name" :key="i"/> </section> <section v-else class="" style="padding:16px;"> - <XV v-for="(x, i) in value.args" v-model:value="value.args[i]" :title="$t(`_pages.script.blocks._${value.type}.arg${i + 1}`)" :get-expected-type="() => _getExpectedType(i)" :hpml="hpml" :name="name" :fn-slots="fnSlots" :key="i"/> + <XV v-for="(x, i) in modelValue.args" v-model="modelValue.args[i]" :title="$t(`_pages.script.blocks._${modelValue.type}.arg${i + 1}`)" :get-expected-type="() => _getExpectedType(i)" :hpml="hpml" :name="name" :fn-slots="fnSlots" :key="i"/> </section> </XContainer> </template> @@ -59,7 +59,7 @@ import { defineAsyncComponent, defineComponent } from 'vue'; import { v4 as uuid } from 'uuid'; import XContainer from './page-editor.container.vue'; -import MkTextarea from '@client/components/ui/textarea.vue'; +import MkTextarea from '@client/components/form/textarea.vue'; import { blockDefs } from '@client/scripts/hpml/index'; import * as os from '@client/os'; import { isLiteralValue } from '@client/scripts/hpml/expr'; @@ -78,7 +78,7 @@ export default defineComponent({ required: false, default: null }, - value: { + modelValue: { required: true }, title: { @@ -113,21 +113,21 @@ export default defineComponent({ computed: { icon(): any { - if (this.value.type === null) return null; - if (this.value.type.startsWith('fn:')) return 'fas fa-plug'; - return blockDefs.find(x => x.type === this.value.type).icon; + if (this.modelValue.type === null) return null; + if (this.modelValue.type.startsWith('fn:')) return 'fas fa-plug'; + return blockDefs.find(x => x.type === this.modelValue.type).icon; }, typeText(): any { - if (this.value.type === null) return null; - if (this.value.type.startsWith('fn:')) return this.value.type.split(':')[1]; - return this.$t(`_pages.script.blocks.${this.value.type}`); + if (this.modelValue.type === null) return null; + if (this.modelValue.type.startsWith('fn:')) return this.modelValue.type.split(':')[1]; + return this.$t(`_pages.script.blocks.${this.modelValue.type}`); }, }, watch: { slots: { handler() { - this.value.value.slots = this.slots.split('\n').map(x => ({ + this.modelValue.value.slots = this.slots.split('\n').map(x => ({ name: x, type: null })); @@ -137,24 +137,24 @@ export default defineComponent({ }, created() { - if (this.value.value == null) this.value.value = null; + if (this.modelValue.value == null) this.modelValue.value = null; - if (this.value.value && this.value.value.slots) this.slots = this.value.value.slots.map(x => x.name).join('\n'); + if (this.modelValue.value && this.modelValue.value.slots) this.slots = this.modelValue.value.slots.map(x => x.name).join('\n'); - this.$watch(() => this.value.type, (t) => { + this.$watch(() => this.modelValue.type, (t) => { this.warn = null; - if (this.value.type === 'fn') { + if (this.modelValue.type === 'fn') { const id = uuid(); - this.value.value = { + this.modelValue.value = { slots: [], expression: { id, type: null } }; return; } - if (this.value.type && this.value.type.startsWith('fn:')) { - const fnName = this.value.type.split(':')[1]; + if (this.modelValue.type && this.modelValue.type.startsWith('fn:')) { + const fnName = this.modelValue.type.split(':')[1]; const fn = this.hpml.getVarByName(fnName); const empties = []; @@ -162,29 +162,29 @@ export default defineComponent({ const id = uuid(); empties.push({ id, type: null }); } - this.value.args = empties; + this.modelValue.args = empties; return; } - if (isLiteralValue(this.value)) return; + if (isLiteralValue(this.modelValue)) return; const empties = []; - for (let i = 0; i < funcDefs[this.value.type].in.length; i++) { + for (let i = 0; i < funcDefs[this.modelValue.type].in.length; i++) { const id = uuid(); empties.push({ id, type: null }); } - this.value.args = empties; + this.modelValue.args = empties; - for (let i = 0; i < funcDefs[this.value.type].in.length; i++) { - const inType = funcDefs[this.value.type].in[i]; + for (let i = 0; i < funcDefs[this.modelValue.type].in.length; i++) { + const inType = funcDefs[this.modelValue.type].in[i]; if (typeof inType !== 'number') { - if (inType === 'number') this.value.args[i].type = 'number'; - if (inType === 'string') this.value.args[i].type = 'text'; + if (inType === 'number') this.modelValue.args[i].type = 'number'; + if (inType === 'string') this.modelValue.args[i].type = 'text'; } } }); - this.$watch(() => this.value.args, (args) => { + this.$watch(() => this.modelValue.args, (args) => { if (args == null) { this.warn = null; return; @@ -202,8 +202,8 @@ export default defineComponent({ }); this.$watch(() => this.hpml.variables, () => { - if (this.type != null && this.value) { - this.error = this.hpml.typeCheck(this.value); + if (this.type != null && this.modelValue) { + this.error = this.hpml.typeCheck(this.modelValue); } }, { deep: true @@ -221,11 +221,11 @@ export default defineComponent({ showCancelButton: true }); if (canceled) return; - this.value.type = type; + this.modelValue.type = type; }, _getExpectedType(slot: number) { - return this.hpml.getExpectedType(this.value, slot); + return this.hpml.getExpectedType(this.modelValue, slot); } } }); diff --git a/src/client/pages/page-editor/page-editor.vue b/src/client/pages/page-editor/page-editor.vue index dc6896ba12..2d617bee75 100644 --- a/src/client/pages/page-editor/page-editor.vue +++ b/src/client/pages/page-editor/page-editor.vue @@ -47,7 +47,7 @@ <MkContainer :foldable="true" :expanded="true" class="_gap"> <template #header><i class="fas fa-sticky-note"></i> {{ $ts._pages.contents }}</template> <div style="padding: 16px;"> - <XBlocks class="content" v-model:value="content" :hpml="hpml"/> + <XBlocks class="content" v-model="content" :hpml="hpml"/> <MkButton @click="add()" v-if="!readonly"><i class="fas fa-plus"></i></MkButton> </div> @@ -94,12 +94,12 @@ import 'vue-prism-editor/dist/prismeditor.min.css'; import { v4 as uuid } from 'uuid'; import XVariable from './page-editor.script-block.vue'; import XBlocks from './page-editor.blocks.vue'; -import MkTextarea from '@client/components/ui/textarea.vue'; +import MkTextarea from '@client/components/form/textarea.vue'; import MkContainer from '@client/components/ui/container.vue'; import MkButton from '@client/components/ui/button.vue'; -import MkSelect from '@client/components/ui/select.vue'; -import MkSwitch from '@client/components/ui/switch.vue'; -import MkInput from '@client/components/ui/input.vue'; +import MkSelect from '@client/components/form/select.vue'; +import MkSwitch from '@client/components/form/switch.vue'; +import MkInput from '@client/components/form/input.vue'; import { blockDefs } from '@client/scripts/hpml/index'; import { HpmlTypeChecker } from '@client/scripts/hpml/type-checker'; import { url } from '@client/config'; diff --git a/src/client/pages/pages.vue b/src/client/pages/pages.vue index 52a860be13..80fc53a961 100644 --- a/src/client/pages/pages.vue +++ b/src/client/pages/pages.vue @@ -1,6 +1,6 @@ <template> <div> - <MkTab v-model:value="tab" v-if="$i"> + <MkTab v-model="tab" v-if="$i"> <option value="featured"><i class="fas fa-fire-alt"></i> {{ $ts._pages.featured }}</option> <option value="my"><i class="fas fa-edit"></i> {{ $ts._pages.my }}</option> <option value="liked"><i class="fas fa-heart"></i> {{ $ts._pages.liked }}</option> diff --git a/src/client/pages/reset-password.vue b/src/client/pages/reset-password.vue index c331382132..6dd9f24259 100644 --- a/src/client/pages/reset-password.vue +++ b/src/client/pages/reset-password.vue @@ -1,6 +1,6 @@ <template> <FormBase v-if="token"> - <FormInput v-model:value="password" type="password"> + <FormInput v-model="password" type="password"> <template #prefix><i class="fas fa-lock"></i></template> <span>{{ $ts.newPassword }}</span> </FormInput> @@ -11,11 +11,11 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import FormLink from '@client/components/form/link.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormGroup from '@client/components/form/group.vue'; -import FormInput from '@client/components/form/input.vue'; -import FormButton from '@client/components/form/button.vue'; +import FormLink from '@client/components/debobigego/link.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; +import FormInput from '@client/components/debobigego/input.vue'; +import FormButton from '@client/components/debobigego/button.vue'; import * as os from '@client/os'; import * as symbols from '@client/symbols'; diff --git a/src/client/pages/reversi/game.setting.vue b/src/client/pages/reversi/game.setting.vue index 1cc623b790..52757b0032 100644 --- a/src/client/pages/reversi/game.setting.vue +++ b/src/client/pages/reversi/game.setting.vue @@ -127,8 +127,8 @@ import { defineComponent } from 'vue'; import * as maps from '../../../games/reversi/maps'; import MkButton from '@client/components/ui/button.vue'; -import MkSwitch from '@client/components/ui/switch.vue'; -import MkRadio from '@client/components/ui/radio.vue'; +import MkSwitch from '@client/components/form/switch.vue'; +import MkRadio from '@client/components/form/radio.vue'; export default defineComponent({ components: { diff --git a/src/client/pages/room/room.vue b/src/client/pages/room/room.vue index 365ed5b803..671dca3577 100644 --- a/src/client/pages/room/room.vue +++ b/src/client/pages/room/room.vue @@ -57,7 +57,7 @@ import XPreview from './preview.vue'; const storeItems = require('@client/scripts/room/furnitures.json5'); import { query as urlQuery } from '../../../prelude/url'; import MkButton from '@client/components/ui/button.vue'; -import MkSelect from '@client/components/ui/select.vue'; +import MkSelect from '@client/components/form/select.vue'; import { selectFile } from '@client/scripts/select-file'; import * as os from '@client/os'; import { ColdDeviceStorage } from '@client/store'; diff --git a/src/client/pages/settings/2fa.vue b/src/client/pages/settings/2fa.vue index 48b06eaa24..386e7c635a 100644 --- a/src/client/pages/settings/2fa.vue +++ b/src/client/pages/settings/2fa.vue @@ -72,11 +72,11 @@ import { hostname } from '@client/config'; import { byteify, hexify, stringify } from '@client/scripts/2fa'; import MkButton from '@client/components/ui/button.vue'; import MkInfo from '@client/components/ui/info.vue'; -import MkInput from '@client/components/ui/input.vue'; -import MkSwitch from '@client/components/ui/switch.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormGroup from '@client/components/form/group.vue'; -import FormButton from '@client/components/form/button.vue'; +import MkInput from '@client/components/form/input.vue'; +import MkSwitch from '@client/components/form/switch.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; +import FormButton from '@client/components/debobigego/button.vue'; import * as os from '@client/os'; import * as symbols from '@client/symbols'; diff --git a/src/client/pages/settings/account-info.vue b/src/client/pages/settings/account-info.vue index 4d851b7b12..16ce91b12f 100644 --- a/src/client/pages/settings/account-info.vue +++ b/src/client/pages/settings/account-info.vue @@ -134,11 +134,11 @@ import { defineAsyncComponent, defineComponent } from 'vue'; import FormSwitch from '@client/components/form/switch.vue'; import FormSelect from '@client/components/form/select.vue'; -import FormLink from '@client/components/form/link.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormGroup from '@client/components/form/group.vue'; -import FormButton from '@client/components/form/button.vue'; -import FormKeyValueView from '@client/components/form/key-value-view.vue'; +import FormLink from '@client/components/debobigego/link.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; +import FormButton from '@client/components/debobigego/button.vue'; +import FormKeyValueView from '@client/components/debobigego/key-value-view.vue'; import * as os from '@client/os'; import number from '@client/filters/number'; import bytes from '@client/filters/bytes'; diff --git a/src/client/pages/settings/accounts.vue b/src/client/pages/settings/accounts.vue index ca6f53776a..d2966cc216 100644 --- a/src/client/pages/settings/accounts.vue +++ b/src/client/pages/settings/accounts.vue @@ -3,8 +3,8 @@ <FormSuspense :p="init"> <FormButton @click="addAccount" primary><i class="fas fa-plus"></i> {{ $ts.addAccount }}</FormButton> - <div class="_formItem _button" v-for="account in accounts" :key="account.id" @click="menu(account, $event)"> - <div class="_formPanel lcjjdxlm"> + <div class="_debobigegoItem _button" v-for="account in accounts" :key="account.id" @click="menu(account, $event)"> + <div class="_debobigegoPanel lcjjdxlm"> <div class="avatar"> <MkAvatar :user="account" class="avatar"/> </div> @@ -24,11 +24,11 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import FormSuspense from '@client/components/form/suspense.vue'; -import FormLink from '@client/components/form/link.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormGroup from '@client/components/form/group.vue'; -import FormButton from '@client/components/form/button.vue'; +import FormSuspense from '@client/components/debobigego/suspense.vue'; +import FormLink from '@client/components/debobigego/link.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; +import FormButton from '@client/components/debobigego/button.vue'; import * as os from '@client/os'; import * as symbols from '@client/symbols'; import { getAccounts, addAccount, login } from '@client/account'; @@ -47,6 +47,7 @@ export default defineComponent({ [symbols.PAGE_INFO]: { title: this.$ts.accounts, icon: 'fas fa-users', + bg: 'var(--bg)', }, storedAccounts: getAccounts().then(accounts => accounts.filter(x => x.id !== this.$i.id)), accounts: null, diff --git a/src/client/pages/settings/api.vue b/src/client/pages/settings/api.vue index 396d4405c3..5c7496e2f9 100644 --- a/src/client/pages/settings/api.vue +++ b/src/client/pages/settings/api.vue @@ -10,10 +10,10 @@ import { defineComponent } from 'vue'; import FormSwitch from '@client/components/form/switch.vue'; import FormSelect from '@client/components/form/select.vue'; -import FormLink from '@client/components/form/link.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormGroup from '@client/components/form/group.vue'; -import FormButton from '@client/components/form/button.vue'; +import FormLink from '@client/components/debobigego/link.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; +import FormButton from '@client/components/debobigego/button.vue'; import * as os from '@client/os'; import * as symbols from '@client/symbols'; @@ -30,7 +30,8 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: 'API', - icon: 'fas fa-key' + icon: 'fas fa-key', + bg: 'var(--bg)', }, isDesktop: window.innerWidth >= 1100, }; diff --git a/src/client/pages/settings/apps.vue b/src/client/pages/settings/apps.vue index c864920ce1..da4f672adf 100644 --- a/src/client/pages/settings/apps.vue +++ b/src/client/pages/settings/apps.vue @@ -8,7 +8,7 @@ </div> </template> <template #default="{items}"> - <div class="_formPanel bfomjevm" v-for="token in items" :key="token.id"> + <div class="_debobigegoPanel bfomjevm" v-for="token in items" :key="token.id"> <img class="icon" :src="token.iconUrl" alt="" v-if="token.iconUrl"/> <div class="body"> <div class="name">{{ token.name }}</div> @@ -39,12 +39,12 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import FormPagination from '@client/components/form/pagination.vue'; +import FormPagination from '@client/components/debobigego/pagination.vue'; import FormSelect from '@client/components/form/select.vue'; -import FormLink from '@client/components/form/link.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormGroup from '@client/components/form/group.vue'; -import FormButton from '@client/components/form/button.vue'; +import FormLink from '@client/components/debobigego/link.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; +import FormButton from '@client/components/debobigego/button.vue'; import * as os from '@client/os'; import * as symbols from '@client/symbols'; @@ -61,6 +61,7 @@ export default defineComponent({ [symbols.PAGE_INFO]: { title: this.$ts.installedApps, icon: 'fas fa-plug', + bg: 'var(--bg)', }, pagination: { endpoint: 'i/apps', diff --git a/src/client/pages/settings/custom-css.vue b/src/client/pages/settings/custom-css.vue index 0781eeebd7..fd473a11fa 100644 --- a/src/client/pages/settings/custom-css.vue +++ b/src/client/pages/settings/custom-css.vue @@ -2,7 +2,7 @@ <FormBase> <FormInfo warn>{{ $ts.customCssWarn }}</FormInfo> - <FormTextarea v-model:value="localCustomCss" manual-save tall class="_monospace" style="tab-size: 2;"> + <FormTextarea v-model="localCustomCss" manual-save tall class="_monospace" style="tab-size: 2;"> <span>{{ $ts.local }}</span> </FormTextarea> </FormBase> @@ -13,11 +13,11 @@ import { defineComponent } from 'vue'; import FormTextarea from '@client/components/form/textarea.vue'; import FormSelect from '@client/components/form/select.vue'; import FormRadios from '@client/components/form/radios.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormGroup from '@client/components/form/group.vue'; -import FormLink from '@client/components/form/link.vue'; -import FormButton from '@client/components/form/button.vue'; -import FormInfo from '@client/components/form/info.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; +import FormLink from '@client/components/debobigego/link.vue'; +import FormButton from '@client/components/debobigego/button.vue'; +import FormInfo from '@client/components/debobigego/info.vue'; import * as os from '@client/os'; import { ColdDeviceStorage } from '@client/store'; import { unisonReload } from '@client/scripts/unison-reload'; @@ -42,7 +42,8 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.customCss, - icon: 'fas fa-code' + icon: 'fas fa-code', + bg: 'var(--bg)', }, localCustomCss: localStorage.getItem('customCss') } diff --git a/src/client/pages/settings/deck.vue b/src/client/pages/settings/deck.vue index 05f3061ca1..2b49ef956c 100644 --- a/src/client/pages/settings/deck.vue +++ b/src/client/pages/settings/deck.vue @@ -2,10 +2,10 @@ <FormBase> <FormGroup> <template #label>{{ $ts.defaultNavigationBehaviour }}</template> - <FormSwitch v-model:value="navWindow">{{ $ts.openInWindow }}</FormSwitch> + <FormSwitch v-model="navWindow">{{ $ts.openInWindow }}</FormSwitch> </FormGroup> - <FormSwitch v-model:value="alwaysShowMainColumn">{{ $ts._deck.alwaysShowMainColumn }}</FormSwitch> + <FormSwitch v-model="alwaysShowMainColumn">{{ $ts._deck.alwaysShowMainColumn }}</FormSwitch> <FormRadios v-model="columnAlign"> <template #desc>{{ $ts._deck.columnAlign }}</template> @@ -20,7 +20,7 @@ <option :value="48">{{ $ts.wide }}</option> </FormRadios> - <FormInput v-model:value="columnMargin" type="number"> + <FormInput v-model="columnMargin" type="number"> <span>{{ $ts._deck.columnMargin }}</span> <template #suffix>px</template> </FormInput> @@ -32,11 +32,11 @@ <script lang="ts"> import { defineComponent } from 'vue'; import FormSwitch from '@client/components/form/switch.vue'; -import FormLink from '@client/components/form/link.vue'; +import FormLink from '@client/components/debobigego/link.vue'; import FormRadios from '@client/components/form/radios.vue'; import FormInput from '@client/components/form/input.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormGroup from '@client/components/form/group.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; import { deckStore } from '@client/ui/deck/deck-store'; import * as os from '@client/os'; import { unisonReload } from '@client/scripts/unison-reload'; @@ -58,7 +58,8 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.deck, - icon: 'fas fa-columns' + icon: 'fas fa-columns', + bg: 'var(--bg)', }, } }, diff --git a/src/client/pages/settings/delete-account.vue b/src/client/pages/settings/delete-account.vue index 3af1879857..6bac214e04 100644 --- a/src/client/pages/settings/delete-account.vue +++ b/src/client/pages/settings/delete-account.vue @@ -9,10 +9,10 @@ <script lang="ts"> import { defineAsyncComponent, defineComponent } from 'vue'; -import FormInfo from '@client/components/form/info.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormGroup from '@client/components/form/group.vue'; -import FormButton from '@client/components/form/button.vue'; +import FormInfo from '@client/components/debobigego/info.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; +import FormButton from '@client/components/debobigego/button.vue'; import * as os from '@client/os'; import { debug } from '@client/config'; import { signout } from '@client/account'; @@ -32,7 +32,8 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts._accountDelete.accountDelete, - icon: 'fas fa-exclamation-triangle' + icon: 'fas fa-exclamation-triangle', + bg: 'var(--bg)', }, debug, } diff --git a/src/client/pages/settings/drive.vue b/src/client/pages/settings/drive.vue index 83068a8335..177bf058f3 100644 --- a/src/client/pages/settings/drive.vue +++ b/src/client/pages/settings/drive.vue @@ -2,8 +2,8 @@ <FormBase class=""> <FormGroup v-if="!fetching"> <template #label>{{ $ts.usageAmount }}</template> - <div class="_formItem uawsfosz"> - <div class="_formPanel"> + <div class="_debobigegoItem uawsfosz"> + <div class="_debobigegoPanel"> <div class="meter"><div :style="meterStyle"></div></div> </div> </div> @@ -17,9 +17,9 @@ </FormKeyValueView> </FormGroup> - <div class="_formItem"> - <div class="_formLabel">{{ $ts.statistics }}</div> - <div class="_formPanel"> + <div class="_debobigegoItem"> + <div class="_debobigegoLabel">{{ $ts.statistics }}</div> + <div class="_debobigegoPanel"> <div ref="chart"></div> </div> </div> @@ -36,10 +36,10 @@ import { defineComponent } from 'vue'; import * as tinycolor from 'tinycolor2'; import ApexCharts from 'apexcharts'; -import FormButton from '@client/components/form/button.vue'; -import FormGroup from '@client/components/form/group.vue'; -import FormKeyValueView from '@client/components/form/key-value-view.vue'; -import FormBase from '@client/components/form/base.vue'; +import FormButton from '@client/components/debobigego/button.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; +import FormKeyValueView from '@client/components/debobigego/key-value-view.vue'; +import FormBase from '@client/components/debobigego/base.vue'; import * as os from '@client/os'; import bytes from '@client/filters/bytes'; import * as symbols from '@client/symbols'; @@ -58,7 +58,8 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.drive, - icon: 'fas fa-cloud' + icon: 'fas fa-cloud', + bg: 'var(--bg)', }, fetching: true, usage: null, diff --git a/src/client/pages/settings/email-address.vue b/src/client/pages/settings/email-address.vue index 28eeeb6b73..f98b22ada7 100644 --- a/src/client/pages/settings/email-address.vue +++ b/src/client/pages/settings/email-address.vue @@ -1,7 +1,7 @@ <template> <FormBase> <FormGroup> - <FormInput v-model:value="emailAddress" type="email"> + <FormInput v-model="emailAddress" type="email"> {{ $ts.emailAddress }} <template #desc v-if="$i.email && !$i.emailVerified">{{ $ts.verificationEmailSent }}</template> <template #desc v-else-if="emailAddress === $i.email && $i.emailVerified">{{ $ts.emailVerified }}</template> @@ -13,10 +13,10 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import FormButton from '@client/components/form/button.vue'; +import FormButton from '@client/components/debobigego/button.vue'; import FormInput from '@client/components/form/input.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormGroup from '@client/components/form/group.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; import * as os from '@client/os'; import * as symbols from '@client/symbols'; @@ -34,7 +34,8 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.emailAddress, - icon: 'fas fa-envelope' + icon: 'fas fa-envelope', + bg: 'var(--bg)', }, emailAddress: null, code: null, diff --git a/src/client/pages/settings/email-notification.vue b/src/client/pages/settings/email-notification.vue index ac3402568a..1b78621c3f 100644 --- a/src/client/pages/settings/email-notification.vue +++ b/src/client/pages/settings/email-notification.vue @@ -1,22 +1,22 @@ <template> <FormBase> <FormGroup> - <FormSwitch v-model:value="mention"> + <FormSwitch v-model="mention"> {{ $ts._notification._types.mention }} </FormSwitch> - <FormSwitch v-model:value="reply"> + <FormSwitch v-model="reply"> {{ $ts._notification._types.reply }} </FormSwitch> - <FormSwitch v-model:value="quote"> + <FormSwitch v-model="quote"> {{ $ts._notification._types.quote }} </FormSwitch> - <FormSwitch v-model:value="follow"> + <FormSwitch v-model="follow"> {{ $ts._notification._types.follow }} </FormSwitch> - <FormSwitch v-model:value="receiveFollowRequest"> + <FormSwitch v-model="receiveFollowRequest"> {{ $ts._notification._types.receiveFollowRequest }} </FormSwitch> - <FormSwitch v-model:value="groupInvited"> + <FormSwitch v-model="groupInvited"> {{ $ts._notification._types.groupInvited }} </FormSwitch> </FormGroup> @@ -25,10 +25,10 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import FormButton from '@client/components/form/button.vue'; +import FormButton from '@client/components/debobigego/button.vue'; import FormSwitch from '@client/components/form/switch.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormGroup from '@client/components/form/group.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; import * as os from '@client/os'; import * as symbols from '@client/symbols'; import * as symbols from '@client/symbols'; @@ -47,7 +47,8 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.emailNotification, - icon: 'fas fa-envelope' + icon: 'fas fa-envelope', + bg: 'var(--bg)', }, mention: this.$i.emailNotificationTypes.includes('mention'), diff --git a/src/client/pages/settings/email.vue b/src/client/pages/settings/email.vue index aa20d9d94e..adc62133ac 100644 --- a/src/client/pages/settings/email.vue +++ b/src/client/pages/settings/email.vue @@ -14,7 +14,7 @@ {{ $ts.emailNotification }} </FormLink> - <FormSwitch :value="$i.receiveAnnouncementEmail" @update:value="onChangeReceiveAnnouncementEmail"> + <FormSwitch :value="$i.receiveAnnouncementEmail" @update:modelValue="onChangeReceiveAnnouncementEmail"> {{ $ts.receiveAnnouncementFromInstance }} </FormSwitch> </FormBase> @@ -22,11 +22,11 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import FormButton from '@client/components/form/button.vue'; -import FormLink from '@client/components/form/link.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormGroup from '@client/components/form/group.vue'; -import FormSwitch from '@client/components/form/switch.vue'; +import FormButton from '@client/components/debobigego/button.vue'; +import FormLink from '@client/components/debobigego/link.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; +import FormSwitch from '@client/components/debobigego/switch.vue'; import * as os from '@client/os'; import * as symbols from '@client/symbols'; @@ -45,7 +45,8 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.email, - icon: 'fas fa-envelope' + icon: 'fas fa-envelope', + bg: 'var(--bg)', }, } }, diff --git a/src/client/pages/settings/experimental-features.vue b/src/client/pages/settings/experimental-features.vue index f8d5e419e9..971c45a628 100644 --- a/src/client/pages/settings/experimental-features.vue +++ b/src/client/pages/settings/experimental-features.vue @@ -8,11 +8,11 @@ import { defineAsyncComponent, defineComponent } from 'vue'; import FormSwitch from '@client/components/form/switch.vue'; import FormSelect from '@client/components/form/select.vue'; -import FormLink from '@client/components/form/link.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormGroup from '@client/components/form/group.vue'; -import FormButton from '@client/components/form/button.vue'; -import FormKeyValueView from '@client/components/form/key-value-view.vue'; +import FormLink from '@client/components/debobigego/link.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; +import FormButton from '@client/components/debobigego/button.vue'; +import FormKeyValueView from '@client/components/debobigego/key-value-view.vue'; import * as os from '@client/os'; import * as symbols from '@client/symbols'; diff --git a/src/client/pages/settings/general.vue b/src/client/pages/settings/general.vue index f8e8e6b24b..59dd251948 100644 --- a/src/client/pages/settings/general.vue +++ b/src/client/pages/settings/general.vue @@ -1,8 +1,8 @@ <template> <FormBase> - <FormSwitch v-model:value="showFixedPostForm">{{ $ts.showFixedPostForm }}</FormSwitch> + <FormSwitch v-model="showFixedPostForm">{{ $ts.showFixedPostForm }}</FormSwitch> - <FormSelect v-model:value="lang"> + <FormSelect v-model="lang"> <template #label>{{ $ts.uiLanguage }}</template> <option v-for="x in langs" :value="x[0]" :key="x[0]">{{ x[1] }}</option> <template #caption> @@ -16,13 +16,13 @@ <FormGroup> <template #label>{{ $ts.behavior }}</template> - <FormSwitch v-model:value="imageNewTab">{{ $ts.openImageInNewTab }}</FormSwitch> - <FormSwitch v-model:value="enableInfiniteScroll">{{ $ts.enableInfiniteScroll }}</FormSwitch> - <FormSwitch v-model:value="useReactionPickerForContextMenu">{{ $ts.useReactionPickerForContextMenu }}</FormSwitch> - <FormSwitch v-model:value="disablePagesScript">{{ $ts.disablePagesScript }}</FormSwitch> + <FormSwitch v-model="imageNewTab">{{ $ts.openImageInNewTab }}</FormSwitch> + <FormSwitch v-model="enableInfiniteScroll">{{ $ts.enableInfiniteScroll }}</FormSwitch> + <FormSwitch v-model="useReactionPickerForContextMenu">{{ $ts.useReactionPickerForContextMenu }}</FormSwitch> + <FormSwitch v-model="disablePagesScript">{{ $ts.disablePagesScript }}</FormSwitch> </FormGroup> - <FormSelect v-model:value="serverDisconnectedBehavior"> + <FormSelect v-model="serverDisconnectedBehavior"> <template #label>{{ $ts.whenServerDisconnected }}</template> <option value="reload">{{ $ts._serverDisconnectedBehavior.reload }}</option> <option value="dialog">{{ $ts._serverDisconnectedBehavior.dialog }}</option> @@ -31,22 +31,22 @@ <FormGroup> <template #label>{{ $ts.appearance }}</template> - <FormSwitch v-model:value="disableAnimatedMfm">{{ $ts.disableAnimatedMfm }}</FormSwitch> - <FormSwitch v-model:value="reduceAnimation">{{ $ts.reduceUiAnimation }}</FormSwitch> - <FormSwitch v-model:value="useBlurEffect">{{ $ts.useBlurEffect }}</FormSwitch> - <FormSwitch v-model:value="useBlurEffectForModal">{{ $ts.useBlurEffectForModal }}</FormSwitch> - <FormSwitch v-model:value="showGapBetweenNotesInTimeline">{{ $ts.showGapBetweenNotesInTimeline }}</FormSwitch> - <FormSwitch v-model:value="loadRawImages">{{ $ts.loadRawImages }}</FormSwitch> - <FormSwitch v-model:value="disableShowingAnimatedImages">{{ $ts.disableShowingAnimatedImages }}</FormSwitch> - <FormSwitch v-model:value="squareAvatars">{{ $ts.squareAvatars }}</FormSwitch> - <FormSwitch v-model:value="useSystemFont">{{ $ts.useSystemFont }}</FormSwitch> - <FormSwitch v-model:value="useOsNativeEmojis">{{ $ts.useOsNativeEmojis }} + <FormSwitch v-model="disableAnimatedMfm">{{ $ts.disableAnimatedMfm }}</FormSwitch> + <FormSwitch v-model="reduceAnimation">{{ $ts.reduceUiAnimation }}</FormSwitch> + <FormSwitch v-model="useBlurEffect">{{ $ts.useBlurEffect }}</FormSwitch> + <FormSwitch v-model="useBlurEffectForModal">{{ $ts.useBlurEffectForModal }}</FormSwitch> + <FormSwitch v-model="showGapBetweenNotesInTimeline">{{ $ts.showGapBetweenNotesInTimeline }}</FormSwitch> + <FormSwitch v-model="loadRawImages">{{ $ts.loadRawImages }}</FormSwitch> + <FormSwitch v-model="disableShowingAnimatedImages">{{ $ts.disableShowingAnimatedImages }}</FormSwitch> + <FormSwitch v-model="squareAvatars">{{ $ts.squareAvatars }}</FormSwitch> + <FormSwitch v-model="useSystemFont">{{ $ts.useSystemFont }}</FormSwitch> + <FormSwitch v-model="useOsNativeEmojis">{{ $ts.useOsNativeEmojis }} <div><Mfm text="🍮🍦🍭🍩🍰🍫🍬🥞🍪" :key="useOsNativeEmojis"/></div> </FormSwitch> </FormGroup> <FormGroup> - <FormSwitch v-model:value="aiChanMode">{{ $ts.aiChanMode }}</FormSwitch> + <FormSwitch v-model="aiChanMode">{{ $ts.aiChanMode }}</FormSwitch> </FormGroup> <FormRadios v-model="fontSize"> @@ -57,14 +57,14 @@ <option value="veryLarge"><span style="font-size: 20px;">Aa</span></option> </FormRadios> - <FormSelect v-model:value="instanceTicker"> + <FormSelect v-model="instanceTicker"> <template #label>{{ $ts.instanceTicker }}</template> <option value="none">{{ $ts._instanceTicker.none }}</option> <option value="remote">{{ $ts._instanceTicker.remote }}</option> <option value="always">{{ $ts._instanceTicker.always }}</option> </FormSelect> - <FormSelect v-model:value="nsfw"> + <FormSelect v-model="nsfw"> <template #label>{{ $ts.nsfw }}</template> <option value="respect">{{ $ts._nsfw.respect }}</option> <option value="ignore">{{ $ts._nsfw.ignore }}</option> @@ -73,10 +73,10 @@ <FormGroup> <template #label>{{ $ts.defaultNavigationBehaviour }}</template> - <FormSwitch v-model:value="defaultSideView">{{ $ts.openInSideView }}</FormSwitch> + <FormSwitch v-model="defaultSideView">{{ $ts.openInSideView }}</FormSwitch> </FormGroup> - <FormSelect v-model:value="chatOpenBehavior"> + <FormSelect v-model="chatOpenBehavior"> <template #label>{{ $ts.chatOpenBehavior }}</template> <option value="page">{{ $ts.showInPage }}</option> <option value="window">{{ $ts.openInWindow }}</option> @@ -91,13 +91,13 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import FormSwitch from '@client/components/form/switch.vue'; -import FormSelect from '@client/components/form/select.vue'; -import FormRadios from '@client/components/form/radios.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormGroup from '@client/components/form/group.vue'; -import FormLink from '@client/components/form/link.vue'; -import FormButton from '@client/components/form/button.vue'; +import FormSwitch from '@client/components/debobigego/switch.vue'; +import FormSelect from '@client/components/debobigego/select.vue'; +import FormRadios from '@client/components/debobigego/radios.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; +import FormLink from '@client/components/debobigego/link.vue'; +import FormButton from '@client/components/debobigego/button.vue'; import MkLink from '@client/components/link.vue'; import { langs } from '@client/config'; import { defaultStore } from '@client/store'; @@ -124,7 +124,8 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.general, - icon: 'fas fa-cogs' + icon: 'fas fa-cogs', + bg: 'var(--bg)' }, langs, lang: localStorage.getItem('lang'), diff --git a/src/client/pages/settings/import-export.vue b/src/client/pages/settings/import-export.vue index e77efb4429..5f1ed43340 100644 --- a/src/client/pages/settings/import-export.vue +++ b/src/client/pages/settings/import-export.vue @@ -28,9 +28,9 @@ <script lang="ts"> import { defineComponent } from 'vue'; import FormSelect from '@client/components/form/select.vue'; -import FormButton from '@client/components/form/button.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormGroup from '@client/components/form/group.vue'; +import FormButton from '@client/components/debobigego/button.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; import * as os from '@client/os'; import { selectFile } from '@client/scripts/select-file'; import * as symbols from '@client/symbols'; @@ -48,7 +48,8 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.importAndExport, - icon: 'fas fa-boxes' + icon: 'fas fa-boxes', + bg: 'var(--bg)', }, } }, diff --git a/src/client/pages/settings/index.link.vue b/src/client/pages/settings/index.link.vue new file mode 100644 index 0000000000..37d06bc22e --- /dev/null +++ b/src/client/pages/settings/index.link.vue @@ -0,0 +1,97 @@ +<template> +<div class="qmfkfnzj"> + <a class="main _button" :href="to" target="_blank" v-if="external"> + <span class="icon"><slot name="icon"></slot></span> + <span class="text"><slot></slot></span> + </a> + <MkA class="main _button" :class="{ active }" :to="to" :behavior="behavior" v-else> + <span class="icon"><slot name="icon"></slot></span> + <span class="text"><slot></slot></span> + </MkA> +</div> +</template> + +<script lang="ts"> +import { defineComponent } from 'vue'; + +export default defineComponent({ + props: { + to: { + type: String, + required: true + }, + active: { + type: Boolean, + required: false + }, + external: { + type: Boolean, + required: false + }, + behavior: { + type: String, + required: false, + }, + }, + data() { + return { + }; + } +}); +</script> + +<style lang="scss" scoped> +.qmfkfnzj { + > .main { + display: flex; + align-items: center; + width: 100%; + box-sizing: border-box; + padding: 10px 16px 10px 14px; + border-radius: 999px; + font-size: 0.9em; + + &:hover { + text-decoration: none; + background: var(--panelHighlight); + } + + &.active { + color: var(--accent); + background: var(--accentedBg); + } + + > .icon { + width: 32px; + margin-right: 2px; + flex-shrink: 0; + text-align: center; + opacity: 0.8; + + &:empty { + display: none; + + & + .text { + padding-left: 4px; + } + } + } + + > .text { + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + padding-right: 12px; + } + + > .right { + margin-left: auto; + opacity: 0.7; + + > .text:not(:empty) { + margin-right: 0.75em; + } + } + } +} +</style> diff --git a/src/client/pages/settings/index.vue b/src/client/pages/settings/index.vue index 3fb5f5f1e6..f3d118e4f0 100644 --- a/src/client/pages/settings/index.vue +++ b/src/client/pages/settings/index.vue @@ -1,50 +1,48 @@ <template> <div class="vvcocwet" :class="{ wide: !narrow }" ref="el"> <div class="nav" v-if="!narrow || page == null"> - <FormBase> - <FormGroup> - <div class="_formItem"> - <div class="_formPanel lwjxoukj"> - <MkAvatar :user="$i" class="avatar"/> - </div> + <FormGroup> + <div class="_debobigegoItem"> + <div class="_debobigegoPanel lwjxoukj"> + <MkAvatar :user="$i" class="avatar"/> </div> - <FormLink :active="page === 'accounts'" replace to="/settings/accounts"><template #icon><i class="fas fa-users"></i></template>{{ $ts.accounts }}</FormLink> - </FormGroup> - <FormInfo v-if="emailNotConfigured" warn>{{ $ts.emailNotConfiguredWarning }} <MkA to="/settings/email" class="_link">{{ $ts.configure }}</MkA></FormInfo> - <FormGroup> - <template #label>{{ $ts.basicSettings }}</template> - <FormLink :active="page === 'profile'" replace to="/settings/profile"><template #icon><i class="fas fa-user"></i></template>{{ $ts.profile }}</FormLink> - <FormLink :active="page === 'privacy'" replace to="/settings/privacy"><template #icon><i class="fas fa-lock-open"></i></template>{{ $ts.privacy }}</FormLink> - <FormLink :active="page === 'reaction'" replace to="/settings/reaction"><template #icon><i class="fas fa-laugh"></i></template>{{ $ts.reaction }}</FormLink> - <FormLink :active="page === 'drive'" replace to="/settings/drive"><template #icon><i class="fas fa-cloud"></i></template>{{ $ts.drive }}</FormLink> - <FormLink :active="page === 'notifications'" replace to="/settings/notifications"><template #icon><i class="fas fa-bell"></i></template>{{ $ts.notifications }}</FormLink> - <FormLink :active="page === 'email'" replace to="/settings/email"><template #icon><i class="fas fa-envelope"></i></template>{{ $ts.email }}</FormLink> - <FormLink :active="page === 'integration'" replace to="/settings/integration"><template #icon><i class="fas fa-share-alt"></i></template>{{ $ts.integration }}</FormLink> - <FormLink :active="page === 'security'" replace to="/settings/security"><template #icon><i class="fas fa-lock"></i></template>{{ $ts.security }}</FormLink> - </FormGroup> - <FormGroup> - <template #label>{{ $ts.clientSettings }}</template> - <FormLink :active="page === 'general'" replace to="/settings/general"><template #icon><i class="fas fa-cogs"></i></template>{{ $ts.general }}</FormLink> - <FormLink :active="page === 'theme'" replace to="/settings/theme"><template #icon><i class="fas fa-palette"></i></template>{{ $ts.theme }}</FormLink> - <FormLink :active="page === 'menu'" replace to="/settings/menu"><template #icon><i class="fas fa-list-ul"></i></template>{{ $ts.menu }}</FormLink> - <FormLink :active="page === 'sounds'" replace to="/settings/sounds"><template #icon><i class="fas fa-music"></i></template>{{ $ts.sounds }}</FormLink> - <FormLink :active="page === 'plugin'" replace to="/settings/plugin"><template #icon><i class="fas fa-plug"></i></template>{{ $ts.plugins }}</FormLink> - </FormGroup> - <FormGroup> - <template #label>{{ $ts.otherSettings }}</template> - <FormLink :active="page === 'import-export'" replace to="/settings/import-export"><template #icon><i class="fas fa-boxes"></i></template>{{ $ts.importAndExport }}</FormLink> - <FormLink :active="page === 'mute-block'" replace to="/settings/mute-block"><template #icon><i class="fas fa-ban"></i></template>{{ $ts.muteAndBlock }}</FormLink> - <FormLink :active="page === 'word-mute'" replace to="/settings/word-mute"><template #icon><i class="fas fa-comment-slash"></i></template>{{ $ts.wordMute }}</FormLink> - <FormLink :active="page === 'api'" replace to="/settings/api"><template #icon><i class="fas fa-key"></i></template>API</FormLink> - <FormLink :active="page === 'other'" replace to="/settings/other"><template #icon><i class="fas fa-ellipsis-h"></i></template>{{ $ts.other }}</FormLink> - </FormGroup> - <FormGroup> - <FormButton @click="clear">{{ $ts.clearCache }}</FormButton> - </FormGroup> - <FormGroup> - <FormButton @click="logout" danger>{{ $ts.logout }}</FormButton> - </FormGroup> - </FormBase> + </div> + <XLink :active="page === 'accounts'" replace to="/settings/accounts"><template #icon><i class="fas fa-users"></i></template>{{ $ts.accounts }}</XLink> + </FormGroup> + <FormInfo v-if="emailNotConfigured" warn>{{ $ts.emailNotConfiguredWarning }} <MkA to="/settings/email" class="_link">{{ $ts.configure }}</MkA></FormInfo> + <FormGroup> + <template #label>{{ $ts.basicSettings }}</template> + <XLink :active="page === 'profile'" replace to="/settings/profile"><template #icon><i class="fas fa-user"></i></template>{{ $ts.profile }}</XLink> + <XLink :active="page === 'privacy'" replace to="/settings/privacy"><template #icon><i class="fas fa-lock-open"></i></template>{{ $ts.privacy }}</XLink> + <XLink :active="page === 'reaction'" replace to="/settings/reaction"><template #icon><i class="fas fa-laugh"></i></template>{{ $ts.reaction }}</XLink> + <XLink :active="page === 'drive'" replace to="/settings/drive"><template #icon><i class="fas fa-cloud"></i></template>{{ $ts.drive }}</XLink> + <XLink :active="page === 'notifications'" replace to="/settings/notifications"><template #icon><i class="fas fa-bell"></i></template>{{ $ts.notifications }}</XLink> + <XLink :active="page === 'email'" replace to="/settings/email"><template #icon><i class="fas fa-envelope"></i></template>{{ $ts.email }}</XLink> + <XLink :active="page === 'integration'" replace to="/settings/integration"><template #icon><i class="fas fa-share-alt"></i></template>{{ $ts.integration }}</XLink> + <XLink :active="page === 'security'" replace to="/settings/security"><template #icon><i class="fas fa-lock"></i></template>{{ $ts.security }}</XLink> + </FormGroup> + <FormGroup> + <template #label>{{ $ts.clientSettings }}</template> + <XLink :active="page === 'general'" replace to="/settings/general"><template #icon><i class="fas fa-cogs"></i></template>{{ $ts.general }}</XLink> + <XLink :active="page === 'theme'" replace to="/settings/theme"><template #icon><i class="fas fa-palette"></i></template>{{ $ts.theme }}</XLink> + <XLink :active="page === 'menu'" replace to="/settings/menu"><template #icon><i class="fas fa-list-ul"></i></template>{{ $ts.menu }}</XLink> + <XLink :active="page === 'sounds'" replace to="/settings/sounds"><template #icon><i class="fas fa-music"></i></template>{{ $ts.sounds }}</XLink> + <XLink :active="page === 'plugin'" replace to="/settings/plugin"><template #icon><i class="fas fa-plug"></i></template>{{ $ts.plugins }}</XLink> + </FormGroup> + <FormGroup> + <template #label>{{ $ts.otherSettings }}</template> + <XLink :active="page === 'import-export'" replace to="/settings/import-export"><template #icon><i class="fas fa-boxes"></i></template>{{ $ts.importAndExport }}</XLink> + <XLink :active="page === 'mute-block'" replace to="/settings/mute-block"><template #icon><i class="fas fa-ban"></i></template>{{ $ts.muteAndBlock }}</XLink> + <XLink :active="page === 'word-mute'" replace to="/settings/word-mute"><template #icon><i class="fas fa-comment-slash"></i></template>{{ $ts.wordMute }}</XLink> + <XLink :active="page === 'api'" replace to="/settings/api"><template #icon><i class="fas fa-key"></i></template>API</XLink> + <XLink :active="page === 'other'" replace to="/settings/other"><template #icon><i class="fas fa-ellipsis-h"></i></template>{{ $ts.other }}</XLink> + </FormGroup> + <FormGroup> + <FormButton @click="clear">{{ $ts.clearCache }}</FormButton> + </FormGroup> + <FormGroup> + <FormButton @click="logout" danger>{{ $ts.logout }}</FormButton> + </FormGroup> </div> <div class="main"> <component :is="component" :key="page" @info="onInfo" v-bind="pageProps"/> @@ -55,11 +53,11 @@ <script lang="ts"> import { computed, defineAsyncComponent, defineComponent, nextTick, onMounted, reactive, ref, watch } from 'vue'; import { i18n } from '@client/i18n'; -import FormLink from '@client/components/form/link.vue'; -import FormGroup from '@client/components/form/group.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormButton from '@client/components/form/button.vue'; -import FormInfo from '@client/components/form/info.vue'; +import XLink from './index.link.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormButton from '@client/components/debobigego/button.vue'; +import FormInfo from '@client/components/debobigego/info.vue'; import { scroll } from '@client/scripts/scroll'; import { signout } from '@client/account'; import { unisonReload } from '@client/scripts/unison-reload'; @@ -70,7 +68,7 @@ import { $i } from '@client/account'; export default defineComponent({ components: { FormBase, - FormLink, + XLink, FormGroup, FormButton, FormInfo, @@ -210,14 +208,13 @@ export default defineComponent({ .vvcocwet { &.wide { display: flex; - max-width: 1100px; + max-width: 1000px; margin: 0 auto; height: 100%; > .nav { width: 32%; box-sizing: border-box; - border-right: solid 0.5px var(--divider); overflow: auto; } diff --git a/src/client/pages/settings/integration.vue b/src/client/pages/settings/integration.vue index f1c0a88afc..7f398dde9d 100644 --- a/src/client/pages/settings/integration.vue +++ b/src/client/pages/settings/integration.vue @@ -1,26 +1,26 @@ <template> <FormBase> - <div class="_formItem" v-if="enableTwitterIntegration"> - <div class="_formLabel"><i class="fab fa-twitter"></i> Twitter</div> - <div class="_formPanel" style="padding: 16px;"> + <div class="_debobigegoItem" v-if="enableTwitterIntegration"> + <div class="_debobigegoLabel"><i class="fab fa-twitter"></i> Twitter</div> + <div class="_debobigegoPanel" style="padding: 16px;"> <p v-if="integrations.twitter">{{ $ts.connectedTo }}: <a :href="`https://twitter.com/${integrations.twitter.screenName}`" rel="nofollow noopener" target="_blank">@{{ integrations.twitter.screenName }}</a></p> <MkButton v-if="integrations.twitter" @click="disconnectTwitter" danger>{{ $ts.disconnectService }}</MkButton> <MkButton v-else @click="connectTwitter" primary>{{ $ts.connectService }}</MkButton> </div> </div> - <div class="_formItem" v-if="enableDiscordIntegration"> - <div class="_formLabel"><i class="fab fa-discord"></i> Discord</div> - <div class="_formPanel" style="padding: 16px;"> + <div class="_debobigegoItem" v-if="enableDiscordIntegration"> + <div class="_debobigegoLabel"><i class="fab fa-discord"></i> Discord</div> + <div class="_debobigegoPanel" style="padding: 16px;"> <p v-if="integrations.discord">{{ $ts.connectedTo }}: <a :href="`https://discord.com/users/${integrations.discord.id}`" rel="nofollow noopener" target="_blank">@{{ integrations.discord.username }}#{{ integrations.discord.discriminator }}</a></p> <MkButton v-if="integrations.discord" @click="disconnectDiscord" danger>{{ $ts.disconnectService }}</MkButton> <MkButton v-else @click="connectDiscord" primary>{{ $ts.connectService }}</MkButton> </div> </div> - <div class="_formItem" v-if="enableGithubIntegration"> - <div class="_formLabel"><i class="fab fa-github"></i> GitHub</div> - <div class="_formPanel" style="padding: 16px;"> + <div class="_debobigegoItem" v-if="enableGithubIntegration"> + <div class="_debobigegoLabel"><i class="fab fa-github"></i> GitHub</div> + <div class="_debobigegoPanel" style="padding: 16px;"> <p v-if="integrations.github">{{ $ts.connectedTo }}: <a :href="`https://github.com/${integrations.github.login}`" rel="nofollow noopener" target="_blank">@{{ integrations.github.login }}</a></p> <MkButton v-if="integrations.github" @click="disconnectGithub" danger>{{ $ts.disconnectService }}</MkButton> <MkButton v-else @click="connectGithub" primary>{{ $ts.connectService }}</MkButton> @@ -32,7 +32,7 @@ <script lang="ts"> import { defineComponent } from 'vue'; import { apiUrl } from '@client/config'; -import FormBase from '@client/components/form/base.vue'; +import FormBase from '@client/components/debobigego/base.vue'; import MkButton from '@client/components/ui/button.vue'; import * as os from '@client/os'; import * as symbols from '@client/symbols'; @@ -49,7 +49,8 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.integration, - icon: 'fas fa-share-alt' + icon: 'fas fa-share-alt', + bg: 'var(--bg)', }, apiUrl, twitterForm: null, diff --git a/src/client/pages/settings/menu.vue b/src/client/pages/settings/menu.vue index 4b315145e1..31472eb0c1 100644 --- a/src/client/pages/settings/menu.vue +++ b/src/client/pages/settings/menu.vue @@ -1,6 +1,6 @@ <template> <FormBase> - <FormTextarea v-model:value="items" tall manual-save> + <FormTextarea v-model="items" tall manual-save> <span>{{ $ts.menu }}</span> <template #desc><button class="_textButton" @click="addItem">{{ $ts.addItem }}</button></template> </FormTextarea> @@ -19,12 +19,10 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import FormSwitch from '@client/components/form/switch.vue'; -import FormTextarea from '@client/components/form/textarea.vue'; -import FormRadios from '@client/components/form/radios.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormGroup from '@client/components/form/group.vue'; -import FormButton from '@client/components/form/button.vue'; +import FormTextarea from '@client/components/debobigego/textarea.vue'; +import FormRadios from '@client/components/debobigego/radios.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormButton from '@client/components/debobigego/button.vue'; import * as os from '@client/os'; import { menuDef } from '@client/menu'; import { defaultStore } from '@client/store'; @@ -45,7 +43,8 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.menu, - icon: 'fas fa-list-ul' + icon: 'fas fa-list-ul', + bg: 'var(--bg)', }, menuDef: menuDef, items: defaultStore.state.menu.join('\n'), diff --git a/src/client/pages/settings/mute-block.vue b/src/client/pages/settings/mute-block.vue index dde0199e18..18b2fc0af4 100644 --- a/src/client/pages/settings/mute-block.vue +++ b/src/client/pages/settings/mute-block.vue @@ -1,6 +1,6 @@ <template> <FormBase> - <MkTab v-model:value="tab" style="margin-bottom: var(--margin);"> + <MkTab v-model="tab" style="margin-bottom: var(--margin);"> <option value="mute">{{ $ts.mutedUsers }}</option> <option value="block">{{ $ts.blockedUsers }}</option> </MkTab> @@ -35,10 +35,10 @@ import { defineComponent } from 'vue'; import MkPagination from '@client/components/ui/pagination.vue'; import MkTab from '@client/components/tab.vue'; -import FormInfo from '@client/components/form/info.vue'; -import FormLink from '@client/components/form/link.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormGroup from '@client/components/form/group.vue'; +import FormInfo from '@client/components/debobigego/info.vue'; +import FormLink from '@client/components/debobigego/link.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; import { userPage } from '@client/filters/user'; import * as os from '@client/os'; import * as symbols from '@client/symbols'; @@ -59,7 +59,8 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.muteAndBlock, - icon: 'fas fa-ban' + icon: 'fas fa-ban', + bg: 'var(--bg)', }, tab: 'mute', mutingPagination: { diff --git a/src/client/pages/settings/notifications.vue b/src/client/pages/settings/notifications.vue index ec95452ba2..1ef350335c 100644 --- a/src/client/pages/settings/notifications.vue +++ b/src/client/pages/settings/notifications.vue @@ -11,10 +11,10 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import FormButton from '@client/components/form/button.vue'; -import FormLink from '@client/components/form/link.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormGroup from '@client/components/form/group.vue'; +import FormButton from '@client/components/debobigego/button.vue'; +import FormLink from '@client/components/debobigego/link.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; import { notificationTypes } from '../../../types'; import * as os from '@client/os'; import * as symbols from '@client/symbols'; @@ -33,7 +33,8 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.notifications, - icon: 'fas fa-bell' + icon: 'fas fa-bell', + bg: 'var(--bg)', }, } }, diff --git a/src/client/pages/settings/other.vue b/src/client/pages/settings/other.vue index 21b5439041..2eb922453f 100644 --- a/src/client/pages/settings/other.vue +++ b/src/client/pages/settings/other.vue @@ -2,18 +2,18 @@ <FormBase> <FormLink to="/settings/update">Misskey Update</FormLink> - <FormSwitch :value="$i.injectFeaturedNote" @update:value="onChangeInjectFeaturedNote"> + <FormSwitch :value="$i.injectFeaturedNote" @update:modelValue="onChangeInjectFeaturedNote"> {{ $ts.showFeaturedNotesInTimeline }} </FormSwitch> - <FormSwitch v-model:value="reportError">{{ $ts.sendErrorReports }}<template #desc>{{ $ts.sendErrorReportsDescription }}</template></FormSwitch> + <FormSwitch v-model="reportError">{{ $ts.sendErrorReports }}<template #desc>{{ $ts.sendErrorReportsDescription }}</template></FormSwitch> <FormLink to="/settings/account-info">{{ $ts.accountInfo }}</FormLink> <FormLink to="/settings/experimental-features">{{ $ts.experimentalFeatures }}</FormLink> <FormGroup> <template #label>{{ $ts.developer }}</template> - <FormSwitch v-model:value="debug" @update:value="changeDebug"> + <FormSwitch v-model="debug" @update:modelValue="changeDebug"> DEBUG MODE </FormSwitch> <template v-if="debug"> @@ -34,10 +34,10 @@ import { defineAsyncComponent, defineComponent } from 'vue'; import FormSwitch from '@client/components/form/switch.vue'; import FormSelect from '@client/components/form/select.vue'; -import FormLink from '@client/components/form/link.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormGroup from '@client/components/form/group.vue'; -import FormButton from '@client/components/form/button.vue'; +import FormLink from '@client/components/debobigego/link.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; +import FormButton from '@client/components/debobigego/button.vue'; import * as os from '@client/os'; import { debug } from '@client/config'; import { defaultStore } from '@client/store'; @@ -60,7 +60,8 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.other, - icon: 'fas fa-ellipsis-h' + icon: 'fas fa-ellipsis-h', + bg: 'var(--bg)', }, debug, } diff --git a/src/client/pages/settings/plugin.install.vue b/src/client/pages/settings/plugin.install.vue index 30cbf58ad7..709ef11abb 100644 --- a/src/client/pages/settings/plugin.install.vue +++ b/src/client/pages/settings/plugin.install.vue @@ -3,7 +3,7 @@ <FormInfo warn>{{ $ts._plugin.installWarn }}</FormInfo> <FormGroup> - <FormTextarea v-model:value="code" tall> + <FormTextarea v-model="code" tall> <span>{{ $ts.code }}</span> </FormTextarea> </FormGroup> @@ -20,11 +20,11 @@ import { v4 as uuid } from 'uuid'; import FormTextarea from '@client/components/form/textarea.vue'; import FormSelect from '@client/components/form/select.vue'; import FormRadios from '@client/components/form/radios.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormGroup from '@client/components/form/group.vue'; -import FormLink from '@client/components/form/link.vue'; -import FormButton from '@client/components/form/button.vue'; -import FormInfo from '@client/components/form/info.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; +import FormLink from '@client/components/debobigego/link.vue'; +import FormButton from '@client/components/debobigego/button.vue'; +import FormInfo from '@client/components/debobigego/info.vue'; import * as os from '@client/os'; import { ColdDeviceStorage } from '@client/store'; import { unisonReload } from '@client/scripts/unison-reload'; @@ -48,7 +48,8 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts._plugin.install, - icon: 'fas fa-download' + icon: 'fas fa-download', + bg: 'var(--bg)', }, code: null, } diff --git a/src/client/pages/settings/plugin.manage.vue b/src/client/pages/settings/plugin.manage.vue index 3df87ca084..f1c27f1e3c 100644 --- a/src/client/pages/settings/plugin.manage.vue +++ b/src/client/pages/settings/plugin.manage.vue @@ -3,9 +3,9 @@ <FormGroup v-for="plugin in plugins" :key="plugin.id"> <template #label><span style="display: flex;"><b>{{ plugin.name }}</b><span style="margin-left: auto;">v{{ plugin.version }}</span></span></template> - <FormSwitch :value="plugin.active" @update:value="changeActive(plugin, $event)">{{ $ts.makeActive }}</FormSwitch> - <div class="_formItem"> - <div class="_formPanel" style="padding: 16px;"> + <FormSwitch :value="plugin.active" @update:modelValue="changeActive(plugin, $event)">{{ $ts.makeActive }}</FormSwitch> + <div class="_debobigegoItem"> + <div class="_debobigegoPanel" style="padding: 16px;"> <div class="_keyValue"> <div>{{ $ts.author }}:</div> <div>{{ plugin.author }}</div> @@ -20,8 +20,8 @@ </div> </div> </div> - <div class="_formItem"> - <div class="_formPanel" style="padding: 16px;"> + <div class="_debobigegoItem"> + <div class="_debobigegoPanel" style="padding: 16px;"> <MkButton @click="config(plugin)" inline v-if="plugin.config"><i class="fas fa-cog"></i> {{ $ts.settings }}</MkButton> <MkButton @click="uninstall(plugin)" inline danger><i class="fas fa-trash-alt"></i> {{ $ts.uninstall }}</MkButton> </div> @@ -33,11 +33,11 @@ <script lang="ts"> import { defineComponent } from 'vue'; import MkButton from '@client/components/ui/button.vue'; -import MkTextarea from '@client/components/ui/textarea.vue'; -import MkSelect from '@client/components/ui/select.vue'; +import MkTextarea from '@client/components/form/textarea.vue'; +import MkSelect from '@client/components/form/select.vue'; import FormSwitch from '@client/components/form/switch.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormGroup from '@client/components/form/group.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; import * as os from '@client/os'; import { ColdDeviceStorage } from '@client/store'; import * as symbols from '@client/symbols'; @@ -58,7 +58,8 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts._plugin.manage, - icon: 'fas fa-plug' + icon: 'fas fa-plug', + bg: 'var(--bg)', }, plugins: ColdDeviceStorage.get('plugins'), } diff --git a/src/client/pages/settings/plugin.vue b/src/client/pages/settings/plugin.vue index 13eaca07fd..23f263bbbd 100644 --- a/src/client/pages/settings/plugin.vue +++ b/src/client/pages/settings/plugin.vue @@ -7,9 +7,9 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import FormBase from '@client/components/form/base.vue'; -import FormGroup from '@client/components/form/group.vue'; -import FormLink from '@client/components/form/link.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; +import FormLink from '@client/components/debobigego/link.vue'; import * as os from '@client/os'; import { ColdDeviceStorage } from '@client/store'; import * as symbols from '@client/symbols'; @@ -26,7 +26,8 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.plugins, - icon: 'fas fa-plug' + icon: 'fas fa-plug', + bg: 'var(--bg)', }, plugins: ColdDeviceStorage.get('plugins').length, } diff --git a/src/client/pages/settings/privacy.vue b/src/client/pages/settings/privacy.vue index 46d8c17ca2..7756158578 100644 --- a/src/client/pages/settings/privacy.vue +++ b/src/client/pages/settings/privacy.vue @@ -1,43 +1,43 @@ <template> <FormBase> <FormGroup> - <FormSwitch v-model:value="isLocked" @update:value="save()">{{ $ts.makeFollowManuallyApprove }}</FormSwitch> - <FormSwitch v-model:value="autoAcceptFollowed" :disabled="!isLocked" @update:value="save()">{{ $ts.autoAcceptFollowed }}</FormSwitch> + <FormSwitch v-model="isLocked" @update:modelValue="save()">{{ $ts.makeFollowManuallyApprove }}</FormSwitch> + <FormSwitch v-model="autoAcceptFollowed" :disabled="!isLocked" @update:modelValue="save()">{{ $ts.autoAcceptFollowed }}</FormSwitch> <template #caption>{{ $ts.lockedAccountInfo }}</template> </FormGroup> - <FormSwitch v-model:value="hideOnlineStatus" @update:value="save()"> + <FormSwitch v-model="hideOnlineStatus" @update:modelValue="save()"> {{ $ts.hideOnlineStatus }} <template #desc>{{ $ts.hideOnlineStatusDescription }}</template> </FormSwitch> - <FormSwitch v-model:value="noCrawle" @update:value="save()"> + <FormSwitch v-model="noCrawle" @update:modelValue="save()"> {{ $ts.noCrawle }} <template #desc>{{ $ts.noCrawleDescription }}</template> </FormSwitch> - <FormSwitch v-model:value="isExplorable" @update:value="save()"> + <FormSwitch v-model="isExplorable" @update:modelValue="save()"> {{ $ts.makeExplorable }} <template #desc>{{ $ts.makeExplorableDescription }}</template> </FormSwitch> - <FormSwitch v-model:value="rememberNoteVisibility" @update:value="save()">{{ $ts.rememberNoteVisibility }}</FormSwitch> + <FormSwitch v-model="rememberNoteVisibility" @update:modelValue="save()">{{ $ts.rememberNoteVisibility }}</FormSwitch> <FormGroup v-if="!rememberNoteVisibility"> <template #label>{{ $ts.defaultNoteVisibility }}</template> - <FormSelect v-model:value="defaultNoteVisibility"> + <FormSelect v-model="defaultNoteVisibility"> <option value="public">{{ $ts._visibility.public }}</option> <option value="home">{{ $ts._visibility.home }}</option> <option value="followers">{{ $ts._visibility.followers }}</option> <option value="specified">{{ $ts._visibility.specified }}</option> </FormSelect> - <FormSwitch v-model:value="defaultNoteLocalOnly">{{ $ts._visibility.localOnly }}</FormSwitch> + <FormSwitch v-model="defaultNoteLocalOnly">{{ $ts._visibility.localOnly }}</FormSwitch> </FormGroup> - <FormSwitch v-model:value="keepCw" @update:value="save()">{{ $ts.keepCw }}</FormSwitch> + <FormSwitch v-model="keepCw" @update:modelValue="save()">{{ $ts.keepCw }}</FormSwitch> </FormBase> </template> <script lang="ts"> import { defineComponent } from 'vue'; -import FormSwitch from '@client/components/form/switch.vue'; -import FormSelect from '@client/components/form/select.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormGroup from '@client/components/form/group.vue'; +import FormSwitch from '@client/components/debobigego/switch.vue'; +import FormSelect from '@client/components/debobigego/select.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; import * as os from '@client/os'; import { defaultStore } from '@client/store'; import * as symbols from '@client/symbols'; @@ -56,7 +56,8 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.privacy, - icon: 'fas fa-lock-open' + icon: 'fas fa-lock-open', + bg: 'var(--bg)', }, isLocked: false, autoAcceptFollowed: false, diff --git a/src/client/pages/settings/profile.vue b/src/client/pages/settings/profile.vue index de7e86bd12..3c93e93480 100644 --- a/src/client/pages/settings/profile.vue +++ b/src/client/pages/settings/profile.vue @@ -1,33 +1,33 @@ <template> <FormBase> <FormGroup> - <div class="_formItem _formPanel llvierxe" :style="{ backgroundImage: $i.bannerUrl ? `url(${ $i.bannerUrl })` : null }"> + <div class="_debobigegoItem _debobigegoPanel llvierxe" :style="{ backgroundImage: $i.bannerUrl ? `url(${ $i.bannerUrl })` : null }"> <MkAvatar class="avatar" :user="$i"/> </div> <FormButton @click="changeAvatar" primary>{{ $ts._profile.changeAvatar }}</FormButton> <FormButton @click="changeBanner" primary>{{ $ts._profile.changeBanner }}</FormButton> </FormGroup> - <FormInput v-model:value="name" :max="30" manual-save> + <FormInput v-model="name" :max="30" manual-save> <span>{{ $ts._profile.name }}</span> </FormInput> - <FormTextarea v-model:value="description" :max="500" tall manual-save> + <FormTextarea v-model="description" :max="500" tall manual-save> <span>{{ $ts._profile.description }}</span> <template #desc>{{ $ts._profile.youCanIncludeHashtags }}</template> </FormTextarea> - <FormInput v-model:value="location" manual-save> + <FormInput v-model="location" manual-save> <span>{{ $ts.location }}</span> <template #prefix><i class="fas fa-map-marker-alt"></i></template> </FormInput> - <FormInput v-model:value="birthday" type="date" manual-save> + <FormInput v-model="birthday" type="date" manual-save> <span>{{ $ts.birthday }}</span> <template #prefix><i class="fas fa-birthday-cake"></i></template> </FormInput> - <FormSelect v-model:value="lang"> + <FormSelect v-model="lang"> <template #label>{{ $ts.language }}</template> <option v-for="x in langs" :value="x[0]" :key="x[0]">{{ x[1] }}</option> </FormSelect> @@ -37,23 +37,23 @@ <template #caption>{{ $ts._profile.metadataDescription }}</template> </FormGroup> - <FormSwitch v-model:value="isCat">{{ $ts.flagAsCat }}<template #desc>{{ $ts.flagAsCatDescription }}</template></FormSwitch> + <FormSwitch v-model="isCat">{{ $ts.flagAsCat }}<template #desc>{{ $ts.flagAsCatDescription }}</template></FormSwitch> - <FormSwitch v-model:value="isBot">{{ $ts.flagAsBot }}<template #desc>{{ $ts.flagAsBotDescription }}</template></FormSwitch> + <FormSwitch v-model="isBot">{{ $ts.flagAsBot }}<template #desc>{{ $ts.flagAsBotDescription }}</template></FormSwitch> - <FormSwitch v-model:value="alwaysMarkNsfw">{{ $ts.alwaysMarkSensitive }}</FormSwitch> + <FormSwitch v-model="alwaysMarkNsfw">{{ $ts.alwaysMarkSensitive }}</FormSwitch> </FormBase> </template> <script lang="ts"> import { defineComponent } from 'vue'; -import FormButton from '@client/components/form/button.vue'; -import FormInput from '@client/components/form/input.vue'; -import FormTextarea from '@client/components/form/textarea.vue'; -import FormSwitch from '@client/components/form/switch.vue'; -import FormSelect from '@client/components/form/select.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormGroup from '@client/components/form/group.vue'; +import FormButton from '@client/components/debobigego/button.vue'; +import FormInput from '@client/components/debobigego/input.vue'; +import FormTextarea from '@client/components/debobigego/textarea.vue'; +import FormSwitch from '@client/components/debobigego/switch.vue'; +import FormSelect from '@client/components/debobigego/select.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; import { host, langs } from '@client/config'; import { selectFile } from '@client/scripts/select-file'; import * as os from '@client/os'; @@ -76,7 +76,8 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.profile, - icon: 'fas fa-user' + icon: 'fas fa-user', + bg: 'var(--bg)' }, host, langs, diff --git a/src/client/pages/settings/reaction.vue b/src/client/pages/settings/reaction.vue index a0024234e4..a5ff46097d 100644 --- a/src/client/pages/settings/reaction.vue +++ b/src/client/pages/settings/reaction.vue @@ -1,8 +1,8 @@ <template> <FormBase> - <div class="_formItem"> - <div class="_formLabel">{{ $ts.reactionSettingDescription }}</div> - <div class="_formPanel"> + <div class="_debobigegoItem"> + <div class="_debobigegoLabel">{{ $ts.reactionSettingDescription }}</div> + <div class="_debobigegoPanel"> <XDraggable class="zoaiodol" v-model="reactions" :item-key="item => item" animation="150" delay="100" delay-on-touch-only="true"> <template #item="{element}"> <button class="_button item" @click="remove(element, $event)"> @@ -14,7 +14,7 @@ </template> </XDraggable> </div> - <div class="_formCaption">{{ $ts.reactionSettingDescription2 }} <button class="_textButton" @click="preview">{{ $ts.preview }}</button></div> + <div class="_debobigegoCaption">{{ $ts.reactionSettingDescription2 }} <button class="_textButton" @click="preview">{{ $ts.preview }}</button></div> </div> <FormRadios v-model="reactionPickerWidth"> @@ -37,10 +37,10 @@ <script lang="ts"> import { defineComponent } from 'vue'; import XDraggable from 'vuedraggable'; -import FormInput from '@client/components/form/input.vue'; -import FormRadios from '@client/components/form/radios.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormButton from '@client/components/form/button.vue'; +import FormInput from '@client/components/debobigego/input.vue'; +import FormRadios from '@client/components/debobigego/radios.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormButton from '@client/components/debobigego/button.vue'; import * as os from '@client/os'; import { defaultStore } from '@client/store'; import * as symbols from '@client/symbols'; @@ -64,7 +64,8 @@ export default defineComponent({ action: { icon: 'fas fa-eye', handler: this.preview - } + }, + bg: 'var(--bg)', }, reactions: JSON.parse(JSON.stringify(this.$store.state.reactions)), } diff --git a/src/client/pages/settings/registry.keys.vue b/src/client/pages/settings/registry.keys.vue index f71589ba4f..d99002e50f 100644 --- a/src/client/pages/settings/registry.keys.vue +++ b/src/client/pages/settings/registry.keys.vue @@ -25,11 +25,11 @@ import { defineAsyncComponent, defineComponent } from 'vue'; import * as JSON5 from 'json5'; import FormSwitch from '@client/components/form/switch.vue'; import FormSelect from '@client/components/form/select.vue'; -import FormLink from '@client/components/form/link.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormGroup from '@client/components/form/group.vue'; -import FormButton from '@client/components/form/button.vue'; -import FormKeyValueView from '@client/components/form/key-value-view.vue'; +import FormLink from '@client/components/debobigego/link.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; +import FormButton from '@client/components/debobigego/button.vue'; +import FormKeyValueView from '@client/components/debobigego/key-value-view.vue'; import * as os from '@client/os'; import * as symbols from '@client/symbols'; @@ -56,7 +56,8 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.registry, - icon: 'fas fa-cogs' + icon: 'fas fa-cogs', + bg: 'var(--bg)', }, keys: null, } diff --git a/src/client/pages/settings/registry.value.vue b/src/client/pages/settings/registry.value.vue index 48245ae99f..06be5737e9 100644 --- a/src/client/pages/settings/registry.value.vue +++ b/src/client/pages/settings/registry.value.vue @@ -19,7 +19,7 @@ </FormGroup> <FormGroup> - <FormTextarea tall v-model:value="valueForEditor" class="_monospace" style="tab-size: 2;"> + <FormTextarea tall v-model="valueForEditor" class="_monospace" style="tab-size: 2;"> <span>{{ $ts.value }} (JSON)</span> </FormTextarea> <FormButton @click="save" primary><i class="fas fa-save"></i> {{ $ts.save }}</FormButton> @@ -38,14 +38,14 @@ <script lang="ts"> import { defineAsyncComponent, defineComponent } from 'vue'; import * as JSON5 from 'json5'; -import FormInfo from '@client/components/form/info.vue'; +import FormInfo from '@client/components/debobigego/info.vue'; import FormSwitch from '@client/components/form/switch.vue'; import FormSelect from '@client/components/form/select.vue'; import FormTextarea from '@client/components/form/textarea.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormGroup from '@client/components/form/group.vue'; -import FormButton from '@client/components/form/button.vue'; -import FormKeyValueView from '@client/components/form/key-value-view.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; +import FormButton from '@client/components/debobigego/button.vue'; +import FormKeyValueView from '@client/components/debobigego/key-value-view.vue'; import * as os from '@client/os'; import * as symbols from '@client/symbols'; @@ -76,7 +76,8 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.registry, - icon: 'fas fa-cogs' + icon: 'fas fa-cogs', + bg: 'var(--bg)', }, value: null, valueForEditor: null, diff --git a/src/client/pages/settings/registry.vue b/src/client/pages/settings/registry.vue index 5ba1bc751b..e4fb230d5c 100644 --- a/src/client/pages/settings/registry.vue +++ b/src/client/pages/settings/registry.vue @@ -13,11 +13,11 @@ import { defineAsyncComponent, defineComponent } from 'vue'; import * as JSON5 from 'json5'; import FormSwitch from '@client/components/form/switch.vue'; import FormSelect from '@client/components/form/select.vue'; -import FormLink from '@client/components/form/link.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormGroup from '@client/components/form/group.vue'; -import FormButton from '@client/components/form/button.vue'; -import FormKeyValueView from '@client/components/form/key-value-view.vue'; +import FormLink from '@client/components/debobigego/link.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; +import FormButton from '@client/components/debobigego/button.vue'; +import FormKeyValueView from '@client/components/debobigego/key-value-view.vue'; import * as os from '@client/os'; import * as symbols from '@client/symbols'; @@ -38,7 +38,8 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.registry, - icon: 'fas fa-cogs' + icon: 'fas fa-cogs', + bg: 'var(--bg)', }, scopes: null, } diff --git a/src/client/pages/settings/security.vue b/src/client/pages/settings/security.vue index b70fa5a9f3..e051685a82 100644 --- a/src/client/pages/settings/security.vue +++ b/src/client/pages/settings/security.vue @@ -6,7 +6,7 @@ <FormPagination :pagination="pagination"> <template #label>{{ $ts.signinHistory }}</template> <template #default="{items}"> - <div class="_formPanel timnmucd" v-for="item in items" :key="item.id"> + <div class="_debobigegoPanel timnmucd" v-for="item in items" :key="item.id"> <header> <i v-if="item.success" class="fas fa-check icon succ"></i> <i v-else class="fas fa-times-circle icon fail"></i> @@ -25,11 +25,11 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import FormBase from '@client/components/form/base.vue'; -import FormLink from '@client/components/form/link.vue'; -import FormGroup from '@client/components/form/group.vue'; -import FormButton from '@client/components/form/button.vue'; -import FormPagination from '@client/components/form/pagination.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormLink from '@client/components/debobigego/link.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; +import FormButton from '@client/components/debobigego/button.vue'; +import FormPagination from '@client/components/debobigego/pagination.vue'; import * as os from '@client/os'; import * as symbols from '@client/symbols'; @@ -48,7 +48,8 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.security, - icon: 'fas fa-lock' + icon: 'fas fa-lock', + bg: 'var(--bg)', }, pagination: { endpoint: 'i/signin-history', diff --git a/src/client/pages/settings/sounds.vue b/src/client/pages/settings/sounds.vue index 1c51685ce8..07310619c8 100644 --- a/src/client/pages/settings/sounds.vue +++ b/src/client/pages/settings/sounds.vue @@ -1,6 +1,6 @@ <template> <FormBase> - <FormRange v-model:value="masterVolume" :min="0" :max="1" :step="0.05"> + <FormRange v-model="masterVolume" :min="0" :max="1" :step="0.05"> <template #label><i class="fas fa-volume-icon"></i> {{ $ts.masterVolume }}</template> </FormRange> @@ -19,11 +19,11 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import FormRange from '@client/components/form/range.vue'; -import FormSelect from '@client/components/form/select.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormButton from '@client/components/form/button.vue'; -import FormGroup from '@client/components/form/group.vue'; +import FormRange from '@client/components/debobigego/range.vue'; +import FormSelect from '@client/components/debobigego/select.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormButton from '@client/components/debobigego/button.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; import * as os from '@client/os'; import { ColdDeviceStorage } from '@client/store'; import { playFile } from '@client/scripts/sound'; @@ -71,7 +71,8 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.sounds, - icon: 'fas fa-music' + icon: 'fas fa-music', + bg: 'var(--bg)', }, sounds: {}, } diff --git a/src/client/pages/settings/theme.install.vue b/src/client/pages/settings/theme.install.vue index d719cc801f..9fbb28929d 100644 --- a/src/client/pages/settings/theme.install.vue +++ b/src/client/pages/settings/theme.install.vue @@ -1,7 +1,7 @@ <template> <FormBase> <FormGroup> - <FormTextarea v-model:value="installThemeCode"> + <FormTextarea v-model="installThemeCode"> <span>{{ $ts._theme.code }}</span> </FormTextarea> <FormButton @click="() => preview(installThemeCode)" :disabled="installThemeCode == null" inline><i class="fas fa-eye"></i> {{ $ts.preview }}</FormButton> @@ -17,10 +17,10 @@ import * as JSON5 from 'json5'; import FormTextarea from '@client/components/form/textarea.vue'; import FormSelect from '@client/components/form/select.vue'; import FormRadios from '@client/components/form/radios.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormGroup from '@client/components/form/group.vue'; -import FormLink from '@client/components/form/link.vue'; -import FormButton from '@client/components/form/button.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; +import FormLink from '@client/components/debobigego/link.vue'; +import FormButton from '@client/components/debobigego/button.vue'; import { applyTheme, validateTheme } from '@client/scripts/theme'; import * as os from '@client/os'; import { ColdDeviceStorage } from '@client/store'; @@ -44,7 +44,8 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts._theme.install, - icon: 'fas fa-download' + icon: 'fas fa-download', + bg: 'var(--bg)', }, installThemeCode: null, } diff --git a/src/client/pages/settings/theme.manage.vue b/src/client/pages/settings/theme.manage.vue index 7cc7a0169a..da21a47a50 100644 --- a/src/client/pages/settings/theme.manage.vue +++ b/src/client/pages/settings/theme.manage.vue @@ -1,6 +1,6 @@ <template> <FormBase> - <FormSelect v-model:value="selectedThemeId"> + <FormSelect v-model="selectedThemeId"> <template #label>{{ $ts.theme }}</template> <optgroup :label="$ts._theme.installedThemes"> <option v-for="x in installedThemes" :value="x.id" :key="x.id">{{ x.name }}</option> @@ -31,10 +31,10 @@ import * as JSON5 from 'json5'; import FormTextarea from '@client/components/form/textarea.vue'; import FormSelect from '@client/components/form/select.vue'; import FormRadios from '@client/components/form/radios.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormGroup from '@client/components/form/group.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; import FormInput from '@client/components/form/input.vue'; -import FormButton from '@client/components/form/button.vue'; +import FormButton from '@client/components/debobigego/button.vue'; import { Theme, builtinThemes } from '@client/scripts/theme'; import copyToClipboard from '@client/scripts/copy-to-clipboard'; import * as os from '@client/os'; @@ -59,7 +59,8 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts._theme.manage, - icon: 'fas fa-folder-open' + icon: 'fas fa-folder-open', + bg: 'var(--bg)', }, installedThemes: getThemes(), builtinThemes, diff --git a/src/client/pages/settings/theme.vue b/src/client/pages/settings/theme.vue index 94eddb1b6f..c6be42251c 100644 --- a/src/client/pages/settings/theme.vue +++ b/src/client/pages/settings/theme.vue @@ -1,7 +1,7 @@ <template> <FormBase> <FormGroup> - <div class="rfqxtzch _formItem _formPanel"> + <div class="rfqxtzch _debobigegoItem _debobigegoPanel"> <div class="darkMode"> <div class="toggleWrapper"> <input type="checkbox" class="dn" id="dn" v-model="darkMode"/> @@ -23,11 +23,11 @@ </div> </div> </div> - <FormSwitch v-model:value="syncDeviceDarkMode">{{ $ts.syncDeviceDarkMode }}</FormSwitch> + <FormSwitch v-model="syncDeviceDarkMode">{{ $ts.syncDeviceDarkMode }}</FormSwitch> </FormGroup> <template v-if="darkMode"> - <FormSelect v-model:value="darkThemeId"> + <FormSelect v-model="darkThemeId"> <template #label>{{ $ts.themeForDarkMode }}</template> <optgroup :label="$ts.darkThemes"> <option v-for="x in darkThemes" :value="x.id" :key="x.id">{{ x.name }}</option> @@ -36,7 +36,7 @@ <option v-for="x in lightThemes" :value="x.id" :key="x.id">{{ x.name }}</option> </optgroup> </FormSelect> - <FormSelect v-model:value="lightThemeId"> + <FormSelect v-model="lightThemeId"> <template #label>{{ $ts.themeForLightMode }}</template> <optgroup :label="$ts.lightThemes"> <option v-for="x in lightThemes" :value="x.id" :key="x.id">{{ x.name }}</option> @@ -47,7 +47,7 @@ </FormSelect> </template> <template v-else> - <FormSelect v-model:value="lightThemeId"> + <FormSelect v-model="lightThemeId"> <template #label>{{ $ts.themeForLightMode }}</template> <optgroup :label="$ts.lightThemes"> <option v-for="x in lightThemes" :value="x.id" :key="x.id">{{ x.name }}</option> @@ -56,7 +56,7 @@ <option v-for="x in darkThemes" :value="x.id" :key="x.id">{{ x.name }}</option> </optgroup> </FormSelect> - <FormSelect v-model:value="darkThemeId"> + <FormSelect v-model="darkThemeId"> <template #label>{{ $ts.themeForDarkMode }}</template> <optgroup :label="$ts.darkThemes"> <option v-for="x in darkThemes" :value="x.id" :key="x.id">{{ x.name }}</option> @@ -86,12 +86,12 @@ <script lang="ts"> import { computed, defineComponent, onActivated, onMounted, ref, watch } from 'vue'; -import FormSwitch from '@client/components/form/switch.vue'; -import FormSelect from '@client/components/form/select.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormGroup from '@client/components/form/group.vue'; -import FormLink from '@client/components/form/link.vue'; -import FormButton from '@client/components/form/button.vue'; +import FormSwitch from '@client/components/debobigego/switch.vue'; +import FormSelect from '@client/components/debobigego/select.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; +import FormLink from '@client/components/debobigego/link.vue'; +import FormButton from '@client/components/debobigego/button.vue'; import { builtinThemes } from '@client/scripts/theme'; import { selectFile } from '@client/scripts/select-file'; import { isDeviceDarkmode } from '@client/scripts/is-device-darkmode'; @@ -116,7 +116,8 @@ export default defineComponent({ setup(props, { emit }) { const INFO = { title: i18n.locale.theme, - icon: 'fas fa-palette' + icon: 'fas fa-palette', + bg: 'var(--bg)', }; const installedThemes = ref(getThemes()); diff --git a/src/client/pages/settings/update.vue b/src/client/pages/settings/update.vue index 8000327d0c..8bc459e936 100644 --- a/src/client/pages/settings/update.vue +++ b/src/client/pages/settings/update.vue @@ -32,12 +32,12 @@ import { defineAsyncComponent, defineComponent } from 'vue'; import FormSwitch from '@client/components/form/switch.vue'; import FormSelect from '@client/components/form/select.vue'; -import FormLink from '@client/components/form/link.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormGroup from '@client/components/form/group.vue'; -import FormButton from '@client/components/form/button.vue'; -import FormKeyValueView from '@client/components/form/key-value-view.vue'; -import FormInfo from '@client/components/form/info.vue'; +import FormLink from '@client/components/debobigego/link.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; +import FormButton from '@client/components/debobigego/button.vue'; +import FormKeyValueView from '@client/components/debobigego/key-value-view.vue'; +import FormInfo from '@client/components/debobigego/info.vue'; import * as os from '@client/os'; import { version, instanceName } from '@client/config'; import * as symbols from '@client/symbols'; @@ -60,7 +60,8 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: 'Misskey Update', - icon: 'fas fa-sync-alt' + icon: 'fas fa-sync-alt', + bg: 'var(--bg)', }, version, instanceName, diff --git a/src/client/pages/settings/word-mute.vue b/src/client/pages/settings/word-mute.vue index fe3fece844..53948b1b1e 100644 --- a/src/client/pages/settings/word-mute.vue +++ b/src/client/pages/settings/word-mute.vue @@ -1,21 +1,21 @@ <template> <div> - <MkTab v-model:value="tab"> + <MkTab v-model="tab"> <option value="soft">{{ $ts._wordMute.soft }}</option> <option value="hard">{{ $ts._wordMute.hard }}</option> </MkTab> <FormBase> - <div class="_formItem"> + <div class="_debobigegoItem"> <div v-show="tab === 'soft'"> <FormInfo>{{ $ts._wordMute.softDescription }}</FormInfo> - <FormTextarea v-model:value="softMutedWords"> + <FormTextarea v-model="softMutedWords"> <span>{{ $ts._wordMute.muteWords }}</span> <template #desc>{{ $ts._wordMute.muteWordsDescription }}<br>{{ $ts._wordMute.muteWordsDescription2 }}</template> </FormTextarea> </div> <div v-show="tab === 'hard'"> <FormInfo>{{ $ts._wordMute.hardDescription }}</FormInfo> - <FormTextarea v-model:value="hardMutedWords"> + <FormTextarea v-model="hardMutedWords"> <span>{{ $ts._wordMute.muteWords }}</span> <template #desc>{{ $ts._wordMute.muteWordsDescription }}<br>{{ $ts._wordMute.muteWordsDescription2 }}</template> </FormTextarea> @@ -33,10 +33,10 @@ <script lang="ts"> import { defineComponent } from 'vue'; import FormTextarea from '@client/components/form/textarea.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormKeyValueView from '@client/components/form/key-value-view.vue'; -import FormButton from '@client/components/form/button.vue'; -import FormInfo from '@client/components/form/info.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormKeyValueView from '@client/components/debobigego/key-value-view.vue'; +import FormButton from '@client/components/debobigego/button.vue'; +import FormInfo from '@client/components/debobigego/info.vue'; import MkTab from '@client/components/tab.vue'; import * as os from '@client/os'; import number from '@client/filters/number'; @@ -58,7 +58,8 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.wordMute, - icon: 'fas fa-comment-slash' + icon: 'fas fa-comment-slash', + bg: 'var(--bg)', }, tab: 'soft', softMutedWords: '', diff --git a/src/client/pages/test.vue b/src/client/pages/test.vue index 131571e9dd..fbab0112ed 100644 --- a/src/client/pages/test.vue +++ b/src/client/pages/test.vue @@ -133,10 +133,10 @@ <script lang="ts"> import { defineComponent, defineAsyncComponent } from 'vue'; import MkButton from '@client/components/ui/button.vue'; -import MkInput from '@client/components/ui/input.vue'; -import MkSwitch from '@client/components/ui/switch.vue'; -import MkTextarea from '@client/components/ui/textarea.vue'; -import MkRadio from '@client/components/ui/radio.vue'; +import MkInput from '@client/components/form/input.vue'; +import MkSwitch from '@client/components/form/switch.vue'; +import MkTextarea from '@client/components/form/textarea.vue'; +import MkRadio from '@client/components/form/radio.vue'; import * as os from '@client/os'; import * as symbols from '@client/symbols'; diff --git a/src/client/pages/theme-editor.vue b/src/client/pages/theme-editor.vue index ce8bae4ff5..3b10396ab8 100644 --- a/src/client/pages/theme-editor.vue +++ b/src/client/pages/theme-editor.vue @@ -1,8 +1,8 @@ <template> <FormBase class="cwepdizn"> - <div class="_formItem colorPicker"> - <div class="_formLabel">{{ $ts.backgroundColor }}</div> - <div class="_formPanel colors"> + <div class="_debobigegoItem colorPicker"> + <div class="_debobigegoLabel">{{ $ts.backgroundColor }}</div> + <div class="_debobigegoPanel colors"> <div class="row"> <button v-for="color in bgColors.filter(x => x.kind === 'light')" :key="color.color" @click="setBgColor(color)" class="color _button" :class="{ active: theme.props.bg === color.color }"> <div class="preview" :style="{ background: color.forPreview }"></div> @@ -15,9 +15,9 @@ </div> </div> </div> - <div class="_formItem colorPicker"> - <div class="_formLabel">{{ $ts.accentColor }}</div> - <div class="_formPanel colors"> + <div class="_debobigegoItem colorPicker"> + <div class="_debobigegoLabel">{{ $ts.accentColor }}</div> + <div class="_debobigegoPanel colors"> <div class="row"> <button v-for="color in accentColors" :key="color" @click="setAccentColor(color)" class="color rounded _button" :class="{ active: theme.props.accent === color }"> <div class="preview" :style="{ background: color }"></div> @@ -25,9 +25,9 @@ </div> </div> </div> - <div class="_formItem colorPicker"> - <div class="_formLabel">{{ $ts.textColor }}</div> - <div class="_formPanel colors"> + <div class="_debobigegoItem colorPicker"> + <div class="_debobigegoLabel">{{ $ts.textColor }}</div> + <div class="_debobigegoPanel colors"> <div class="row"> <button v-for="color in fgColors" :key="color" @click="setFgColor(color)" class="color char _button" :class="{ active: (theme.props.fg === color.forLight) || (theme.props.fg === color.forDark) }"> <div class="preview" :style="{ color: color.forPreview ? color.forPreview : theme.base === 'light' ? '#5f5f5f' : '#dadada' }">A</div> @@ -37,7 +37,7 @@ </div> <FormGroup v-if="codeEnabled"> - <FormTextarea v-model:value="themeCode" tall> + <FormTextarea v-model="themeCode" tall> <span>{{ $ts._theme.code }}</span> </FormTextarea> <FormButton @click="applyThemeCode" primary>{{ $ts.apply }}</FormButton> @@ -45,7 +45,7 @@ <FormButton v-else @click="codeEnabled = true"><i class="fas fa-code"></i> {{ $ts.editCode }}</FormButton> <FormGroup v-if="descriptionEnabled"> - <FormTextarea v-model:value="description"> + <FormTextarea v-model="description"> <span>{{ $ts._theme.description }}</span> </FormTextarea> </FormGroup> @@ -65,10 +65,10 @@ import * as tinycolor from 'tinycolor2'; import { v4 as uuid} from 'uuid'; import * as JSON5 from 'json5'; -import FormBase from '@client/components/form/base.vue'; -import FormButton from '@client/components/form/button.vue'; -import FormTextarea from '@client/components/form/textarea.vue'; -import FormGroup from '@client/components/form/group.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormButton from '@client/components/debobigego/button.vue'; +import FormTextarea from '@client/components/debobigego/textarea.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; import { Theme, applyTheme, validateTheme, darkTheme, lightTheme } from '@client/scripts/theme'; import { host } from '@client/config'; diff --git a/src/client/pages/user-ap-info.vue b/src/client/pages/user-ap-info.vue index c08a352571..cbdff874ed 100644 --- a/src/client/pages/user-ap-info.vue +++ b/src/client/pages/user-ap-info.vue @@ -58,14 +58,14 @@ <script lang="ts"> import { defineAsyncComponent, defineComponent } from 'vue'; -import FormObjectView from '@client/components/form/object-view.vue'; -import FormTextarea from '@client/components/form/textarea.vue'; -import FormLink from '@client/components/form/link.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormGroup from '@client/components/form/group.vue'; -import FormButton from '@client/components/form/button.vue'; -import FormKeyValueView from '@client/components/form/key-value-view.vue'; -import FormSuspense from '@client/components/form/suspense.vue'; +import FormObjectView from '@client/components/debobigego/object-view.vue'; +import FormTextarea from '@client/components/debobigego/textarea.vue'; +import FormLink from '@client/components/debobigego/link.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; +import FormButton from '@client/components/debobigego/button.vue'; +import FormKeyValueView from '@client/components/debobigego/key-value-view.vue'; +import FormSuspense from '@client/components/debobigego/suspense.vue'; import * as os from '@client/os'; import number from '@client/filters/number'; import bytes from '@client/filters/bytes'; diff --git a/src/client/pages/user-info.vue b/src/client/pages/user-info.vue index 503982652b..bf67fc853a 100644 --- a/src/client/pages/user-info.vue +++ b/src/client/pages/user-info.vue @@ -1,7 +1,7 @@ <template> <FormBase> <FormSuspense :p="init"> - <div class="_formItem aeakzknw"> + <div class="_debobigegoItem aeakzknw"> <MkAvatar class="avatar" :user="user" :show-indicator="true"/> </div> @@ -20,9 +20,9 @@ </FormGroup> <FormGroup v-if="iAmModerator"> - <FormSwitch v-if="user.host == null && $i.isAdmin && (moderator || !user.isAdmin)" @update:value="toggleModerator" v-model:value="moderator">{{ $ts.moderator }}</FormSwitch> - <FormSwitch @update:value="toggleSilence" v-model:value="silenced">{{ $ts.silence }}</FormSwitch> - <FormSwitch @update:value="toggleSuspend" v-model:value="suspended">{{ $ts.suspend }}</FormSwitch> + <FormSwitch v-if="user.host == null && $i.isAdmin && (moderator || !user.isAdmin)" @update:modelValue="toggleModerator" v-model="moderator">{{ $ts.moderator }}</FormSwitch> + <FormSwitch @update:modelValue="toggleSilence" v-model="silenced">{{ $ts.silence }}</FormSwitch> + <FormSwitch @update:modelValue="toggleSuspend" v-model="suspended">{{ $ts.suspend }}</FormSwitch> </FormGroup> <FormGroup> @@ -56,15 +56,15 @@ <script lang="ts"> import { computed, defineAsyncComponent, defineComponent } from 'vue'; -import FormObjectView from '@client/components/form/object-view.vue'; -import FormTextarea from '@client/components/form/textarea.vue'; -import FormSwitch from '@client/components/form/switch.vue'; -import FormLink from '@client/components/form/link.vue'; -import FormBase from '@client/components/form/base.vue'; -import FormGroup from '@client/components/form/group.vue'; -import FormButton from '@client/components/form/button.vue'; -import FormKeyValueView from '@client/components/form/key-value-view.vue'; -import FormSuspense from '@client/components/form/suspense.vue'; +import FormObjectView from '@client/components/debobigego/object-view.vue'; +import FormTextarea from '@client/components/debobigego/textarea.vue'; +import FormSwitch from '@client/components/debobigego/switch.vue'; +import FormLink from '@client/components/debobigego/link.vue'; +import FormBase from '@client/components/debobigego/base.vue'; +import FormGroup from '@client/components/debobigego/group.vue'; +import FormButton from '@client/components/debobigego/button.vue'; +import FormKeyValueView from '@client/components/debobigego/key-value-view.vue'; +import FormSuspense from '@client/components/debobigego/suspense.vue'; import * as os from '@client/os'; import number from '@client/filters/number'; import bytes from '@client/filters/bytes'; diff --git a/src/client/pages/user/index.timeline.vue b/src/client/pages/user/index.timeline.vue index 287e6c8b22..8796ded469 100644 --- a/src/client/pages/user/index.timeline.vue +++ b/src/client/pages/user/index.timeline.vue @@ -1,6 +1,6 @@ <template> <div class="yrzkoczt" v-sticky-container> - <MkTab v-model:value="with_" class="_gap tab"> + <MkTab v-model="with_" class="_gap tab"> <option :value="null">{{ $ts.notes }}</option> <option value="replies">{{ $ts.notesAndReplies }}</option> <option value="files">{{ $ts.withFiles }}</option> diff --git a/src/client/pages/welcome.setup.vue b/src/client/pages/welcome.setup.vue index d0091bef67..dfefecc8fa 100644 --- a/src/client/pages/welcome.setup.vue +++ b/src/client/pages/welcome.setup.vue @@ -24,7 +24,7 @@ <script lang="ts"> import { defineComponent } from 'vue'; import MkButton from '@client/components/ui/button.vue'; -import MkInput from '@client/components/ui/input.vue'; +import MkInput from '@client/components/form/input.vue'; import { host } from '@client/config'; import * as os from '@client/os'; import { login } from '@client/account'; diff --git a/src/client/style.scss b/src/client/style.scss index 0318013f60..5b55ab8caf 100644 --- a/src/client/style.scss +++ b/src/client/style.scss @@ -425,12 +425,18 @@ hr { } } -._inputNoTopMargin { - margin-top: 0 !important; +._formBlock { + margin: 20px 0; } -._inputNoBottomMargin { - margin-bottom: 0 !important; +._formRoot { + > ._formBlock:first-child { + margin-top: 0; + } + + > ._formBlock:last-child { + margin-bottom: 0; + } } ._table { diff --git a/src/client/ui/_common_/sidebar.vue b/src/client/ui/_common_/sidebar.vue index 9817a46e30..4bb7bbd985 100644 --- a/src/client/ui/_common_/sidebar.vue +++ b/src/client/ui/_common_/sidebar.vue @@ -395,7 +395,7 @@ export default defineComponent({ left: 0; right: 0; bottom: 0; - border-radius: 8px; + border-radius: 999px; background: var(--accentedBg); } } diff --git a/src/client/ui/chat/note-preview.vue b/src/client/ui/chat/note-preview.vue index 77949e314b..beb38de644 100644 --- a/src/client/ui/chat/note-preview.vue +++ b/src/client/ui/chat/note-preview.vue @@ -6,7 +6,7 @@ <div class="body"> <p v-if="note.cw != null" class="cw"> <span class="text" v-if="note.cw != ''">{{ note.cw }}</span> - <XCwButton v-model:value="showContent" :note="note"/> + <XCwButton v-model="showContent" :note="note"/> </p> <div class="content" v-show="note.cw == null || showContent"> <XSubNote-content class="text" :note="note"/> diff --git a/src/client/ui/chat/note.sub.vue b/src/client/ui/chat/note.sub.vue index bb528dd936..a284ba2bf4 100644 --- a/src/client/ui/chat/note.sub.vue +++ b/src/client/ui/chat/note.sub.vue @@ -7,7 +7,7 @@ <div class="body"> <p v-if="note.cw != null" class="cw"> <Mfm v-if="note.cw != ''" class="text" :text="note.cw" :author="note.user" :i="$i" :custom-emojis="note.emojis" /> - <XCwButton v-model:value="showContent" :note="note"/> + <XCwButton v-model="showContent" :note="note"/> </p> <div class="content" v-show="note.cw == null || showContent"> <XSubNote-content class="text" :note="note"/> diff --git a/src/client/ui/chat/note.vue b/src/client/ui/chat/note.vue index 6d2b9bbf54..26d44d26aa 100644 --- a/src/client/ui/chat/note.vue +++ b/src/client/ui/chat/note.vue @@ -42,7 +42,7 @@ <div class="body"> <p v-if="appearNote.cw != null" class="cw"> <Mfm v-if="appearNote.cw != ''" class="text" :text="appearNote.cw" :author="appearNote.user" :i="$i" :custom-emojis="appearNote.emojis"/> - <XCwButton v-model:value="showContent" :note="appearNote"/> + <XCwButton v-model="showContent" :note="appearNote"/> </p> <div class="content" :class="{ collapsed }" v-show="appearNote.cw == null || showContent"> <div class="text"> diff --git a/yarn.lock b/yarn.lock index 61c052c995..fb6a3bbda0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1244,95 +1244,95 @@ resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44" integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q== -"@vue/compiler-core@3.2.13": - version "3.2.13" - resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.2.13.tgz#901268088b98a53c43be0f02bfa0e3a389ad6bf4" - integrity sha512-H8MUuKVCfAT6C0vth/+1LAriKnM+RTFo/5MoFycwRPwworTvkpWq/EuGoIXdLBblo8Y2/bNsOmIBEEoOtrb/bQ== +"@vue/compiler-core@3.2.19": + version "3.2.19" + resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.2.19.tgz#b537dd377ce51fdb64e9b30ebfbff7cd70a64cb9" + integrity sha512-8dOPX0YOtaXol0Zf2cfLQ4NU/yHYl2H7DCKsLEZ7gdvPK6ZSEwGLJ7IdghhY2YEshEpC5RB9QKdC5I07z8Dtjg== dependencies: "@babel/parser" "^7.15.0" - "@vue/shared" "3.2.13" + "@vue/shared" "3.2.19" estree-walker "^2.0.2" source-map "^0.6.1" -"@vue/compiler-dom@3.2.13": - version "3.2.13" - resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.2.13.tgz#028982494fb9d97807d5275b42355732686f8ed7" - integrity sha512-5+2dYgQyNzM97EEgbdAusUpLjulcKkvLM26jOGpd14+qwEcW/KCnns5DGjlZD/tsdEwToOoTDCm+mjx7cO/G1Q== +"@vue/compiler-dom@3.2.19": + version "3.2.19" + resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.2.19.tgz#0607bc90de6af55fde73b09b3c4d0bf8cb597ed8" + integrity sha512-WzQoE8rfkFjPtIioc7SSgTsnz9g2oG61DU8KHnzPrRS7fW/lji6H2uCYJfp4Z6kZE8GjnHc1Ljwl3/gxDes0cw== dependencies: - "@vue/compiler-core" "3.2.13" - "@vue/shared" "3.2.13" + "@vue/compiler-core" "3.2.19" + "@vue/shared" "3.2.19" -"@vue/compiler-sfc@3.2.13": - version "3.2.13" - resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.2.13.tgz#a38475048aad9c96cf04dfe635129b417e0f9295" - integrity sha512-3j970d969aOILykcTstdihP33xH1Onm0wsvcl+rGv9AGxivB9xicRxBw93HCIA4dAPivr42WjHEoci9q2/85uw== +"@vue/compiler-sfc@3.2.19": + version "3.2.19" + resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.2.19.tgz#d412195a98ebd49b84602f171719294a1d9549be" + integrity sha512-pLlbgkO1UHTO02MSpa/sFOXUwIDxSMiKZ1ozE5n71CY4DM+YmI+G3gT/ZHZ46WBId7f3VTF/D8pGwMygcQbrQA== dependencies: "@babel/parser" "^7.15.0" - "@vue/compiler-core" "3.2.13" - "@vue/compiler-dom" "3.2.13" - "@vue/compiler-ssr" "3.2.13" - "@vue/ref-transform" "3.2.13" - "@vue/shared" "3.2.13" + "@vue/compiler-core" "3.2.19" + "@vue/compiler-dom" "3.2.19" + "@vue/compiler-ssr" "3.2.19" + "@vue/ref-transform" "3.2.19" + "@vue/shared" "3.2.19" estree-walker "^2.0.2" magic-string "^0.25.7" postcss "^8.1.10" source-map "^0.6.1" -"@vue/compiler-ssr@3.2.13": - version "3.2.13" - resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.2.13.tgz#98434672e0b488c2affa4b0570731d6be5cda187" - integrity sha512-ZbO6uDhUWTdKBRguYNEZXj2FU3nh1cudoHBiidbxj9q5J0tVT+j1PSVFAXPq6SquUBdJpa4HvGkQ5kQzv6upXg== +"@vue/compiler-ssr@3.2.19": + version "3.2.19" + resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.2.19.tgz#3e91ecf70f8f961c5f63eacd2139bcdab9a7a07c" + integrity sha512-oLon0Cn3O7WEYzzmzZavGoqXH+199LT+smdjBT3Uf3UX4HwDNuBFCmvL0TsqV9SQnIgKvBRbQ7lhbpnd4lqM3w== dependencies: - "@vue/compiler-dom" "3.2.13" - "@vue/shared" "3.2.13" + "@vue/compiler-dom" "3.2.19" + "@vue/shared" "3.2.19" -"@vue/reactivity@3.2.13": - version "3.2.13" - resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.2.13.tgz#d269b09aaafef06a91bf3eb98defd41a2c3daf54" - integrity sha512-j3ByCiRgrr4uEZpXJM8XowrbYKeNHMHlbmMZE/2QpVzVPIfrQWS2fpLmbchJeMrnwIrzEl+dub3hgwkV4KRn4w== +"@vue/reactivity@3.2.19": + version "3.2.19" + resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.2.19.tgz#fc6e0f0106f295226835cfed5ff5f84d927bea65" + integrity sha512-FtachoYs2SnyrWup5UikP54xDX6ZJ1s5VgHcJp4rkGoutU3Ry61jhs+nCX7J64zjX992Mh9gGUC0LqTs8q9vCA== dependencies: - "@vue/shared" "3.2.13" + "@vue/shared" "3.2.19" -"@vue/ref-transform@3.2.13": - version "3.2.13" - resolved "https://registry.yarnpkg.com/@vue/ref-transform/-/ref-transform-3.2.13.tgz#6adfce50d388cc03683d9d2ba58f3a3bde5166f4" - integrity sha512-q6GXHZFzXjpx1K3UFRF8fa+xSmD9xV/FjhGzTNnfrryBr8tBUNYgP2f0s5K5N+21Ay7+MlQ1XXMUp8McGvsryQ== +"@vue/ref-transform@3.2.19": + version "3.2.19" + resolved "https://registry.yarnpkg.com/@vue/ref-transform/-/ref-transform-3.2.19.tgz#cf0f986486bb26838fbd09749e927bab19745600" + integrity sha512-03wwUnoIAeKti5IGGx6Vk/HEBJ+zUcm5wrUM3+PQsGf7IYnXTbeIfHHpx4HeSeWhnLAjqZjADQwW8uA4rBmVbg== dependencies: "@babel/parser" "^7.15.0" - "@vue/compiler-core" "3.2.13" - "@vue/shared" "3.2.13" + "@vue/compiler-core" "3.2.19" + "@vue/shared" "3.2.19" estree-walker "^2.0.2" magic-string "^0.25.7" -"@vue/runtime-core@3.2.13": - version "3.2.13" - resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.2.13.tgz#8b62f92642e56af71d0d35a9f0065daf6f9eb3fb" - integrity sha512-VQedL9Wa7yWMPVDrIkxzLCm6cWCDBoXcXc+jrsOJkqpWhEeA7+zGOsDsHzhLH8aaJD6vdnUR5Cy0EKvoJDqEWQ== +"@vue/runtime-core@3.2.19": + version "3.2.19" + resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.2.19.tgz#807715b7f4728abb84fa4a8efdbe37d8ddb4c6d3" + integrity sha512-qArZSWKxWsgKfxk9BelZ32nY0MZ31CAW2kUUyVJyxh4cTfHaXGbjiQB5JgsvKc49ROMNffv9t3/qjasQqAH+RQ== dependencies: - "@vue/reactivity" "3.2.13" - "@vue/shared" "3.2.13" + "@vue/reactivity" "3.2.19" + "@vue/shared" "3.2.19" -"@vue/runtime-dom@3.2.13": - version "3.2.13" - resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.2.13.tgz#238f517a75765719f8373409cee853775c636f92" - integrity sha512-DVG+ItkrnCOEa9HSrmGBTLwv/gBVYCO8wkm/yv+d5ChoTnyIILxP0oCiZEPJsgWZfUSRPNi5rXozwo7F99MiwQ== +"@vue/runtime-dom@3.2.19": + version "3.2.19" + resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.2.19.tgz#7e8bf645754703e360fa132e4be9113edf2377bb" + integrity sha512-hIRboxXwafeHhbZEkZYNV0MiJXPNf4fP0X6hM2TJb0vssz8BKhD9cF92BkRgZztTQevecbhk0gu4uAPJ3dxL9A== dependencies: - "@vue/runtime-core" "3.2.13" - "@vue/shared" "3.2.13" + "@vue/runtime-core" "3.2.19" + "@vue/shared" "3.2.19" csstype "^2.6.8" -"@vue/server-renderer@3.2.13": - version "3.2.13" - resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.2.13.tgz#b10a564be67eec6721f90b36c3c817c19e6064b4" - integrity sha512-KI+JFV+vRb95+Jb6IwRRm4Vhvj8wrJTNs+OlATfqwwIRpBGAyxn/4knDJYzlnUf/mrKAkrbw751mHhi+pEwILQ== +"@vue/server-renderer@3.2.19": + version "3.2.19" + resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.2.19.tgz#870bcec9f7cdaee0c2187a169b6e636ab4362fb1" + integrity sha512-A9FNT7fgQJXItwdzWREntAgWKVtKYuXHBKGev/H4+ByTu8vB7gQXGcim01QxaJshdNg4dYuH2tEBZXCNCNx+/w== dependencies: - "@vue/compiler-ssr" "3.2.13" - "@vue/shared" "3.2.13" + "@vue/compiler-ssr" "3.2.19" + "@vue/shared" "3.2.19" -"@vue/shared@3.2.13": - version "3.2.13" - resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.2.13.tgz#c830ef966d7af12598e0ea862a55695ea589cd47" - integrity sha512-F/gs3kHQ8Xeo24F6EImOvBiIoYQsBjF9qoLzvk+LHxYN6ZhIDEL1NWrBFYzdFQ7NphjEYd4EvPZ+Qee+WX8P6w== +"@vue/shared@3.2.19": + version "3.2.19" + resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.2.19.tgz#111ec3da18337d86274446984c49925b1b2b2dd7" + integrity sha512-Knqhx7WieLdVgwCAZgTVrDCXZ50uItuecLh9JdLC8O+a5ayaSyIQYveUK3hCRNC7ws5zalHmZwfdLMGaS8r4Ew== "@webassemblyjs/ast@1.11.0": version "1.11.0" @@ -4091,7 +4091,7 @@ end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1: dependencies: once "^1.4.0" -enhanced-resolve@^5.0.0, enhanced-resolve@^5.8.0: +enhanced-resolve@^5.0.0: version "5.8.0" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.8.0.tgz#d9deae58f9d3773b6a111a5a46831da5be5c9ac0" integrity sha512-Sl3KRpJA8OpprrtaIswVki3cWPiPKxXuFxJXBp+zNb6s6VwNWwFRUdtmzd2ReUut8n+sCPx7QCtQ7w5wfJhSgQ== @@ -4107,6 +4107,14 @@ enhanced-resolve@^5.7.0: graceful-fs "^4.2.4" tapable "^2.2.0" +enhanced-resolve@^5.8.3: + version "5.8.3" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.8.3.tgz#6d552d465cce0423f5b3d718511ea53826a7b2f0" + integrity sha512-EGAbGvH7j7Xt2nc0E7D99La1OiEs8LnyimkRgwExpUMScN6O+3x9tIWs7PLQZVNx4YD+00skHXPXi1yQHpAmZA== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + enquirer@^2.3.5, enquirer@^2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" @@ -4173,10 +4181,10 @@ es-module-lexer@^0.4.0: resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.4.0.tgz#21f4181cc8b7eee06855f1c59e6087c7bc4f77b0" integrity sha512-iuEGihqqhKWFgh72Q/Jtch7V2t/ft8w8IPP2aEN8ArYKO+IWyo6hsi96hCdgyeEDQIV3InhYQ9BlwUFPGXrbEQ== -es-module-lexer@^0.7.1: - version "0.7.1" - resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.7.1.tgz#c2c8e0f46f2df06274cdaf0dd3f3b33e0a0b267d" - integrity sha512-MgtWFl5No+4S3TmhDmCz2ObFGm6lEpTnzbQi+Dd+pw4mlTIZTmM2iAs5gRlmx5zS9luzobCSBSI90JM/1/JgOw== +es-module-lexer@^0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.9.0.tgz#fe4c4621977bc668e285c5f1f70ca3b451095fda" + integrity sha512-qU2eN/XHsrl3E4y7mK1wdWnyy5c8gXtCbfP6Xcsemm7fPUR1PIV1JhZfP7ojcN0Fzp69CfrS3u76h2tusvfKiQ== es-to-primitive@^1.2.1: version "1.2.1" @@ -11521,16 +11529,16 @@ vue-svg-loader@0.17.0-beta.2: semver "^7.3.2" svgo "^1.3.2" -vue@3.2.13: - version "3.2.13" - resolved "https://registry.yarnpkg.com/vue/-/vue-3.2.13.tgz#9d1a94fc62dc29ae21a3dd0d8ee24198e421671e" - integrity sha512-raTGvLXXTdMxrhQKY1r1YFXZMmjbjTe7QHBW9EU4CgCBhq8DbgyLqgILcSUZmeFyazk5WY7a7xu0VYmHElf4lA== +vue@3.2.19: + version "3.2.19" + resolved "https://registry.yarnpkg.com/vue/-/vue-3.2.19.tgz#da2c80a6a0271c7097fee9e31692adfd9d569c8f" + integrity sha512-6KAMdIfAtlK+qohTIUE4urwAv4A3YRuo8uAbByApUmiB0CziGAAPs6qVugN6oHPia8YIafHB/37K0O6KZ7sGmA== dependencies: - "@vue/compiler-dom" "3.2.13" - "@vue/compiler-sfc" "3.2.13" - "@vue/runtime-dom" "3.2.13" - "@vue/server-renderer" "3.2.13" - "@vue/shared" "3.2.13" + "@vue/compiler-dom" "3.2.19" + "@vue/compiler-sfc" "3.2.19" + "@vue/runtime-dom" "3.2.19" + "@vue/server-renderer" "3.2.19" + "@vue/shared" "3.2.19" vuedraggable@4.0.1: version "4.0.1" @@ -11642,10 +11650,10 @@ webpack-sources@^3.2.0: resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.0.tgz#b16973bcf844ebcdb3afde32eda1c04d0b90f89d" integrity sha512-fahN08Et7P9trej8xz/Z7eRu8ltyiygEo/hnRi9KqBUs80KeDcnf96ZJo++ewWd84fEf3xSX9bp4ZS9hbw0OBw== -webpack@5.53.0: - version "5.53.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.53.0.tgz#f463cd9c6fc1356ae4b9b7ac911fd1f5b2df86af" - integrity sha512-RZ1Z3z3ni44snoWjfWeHFyzvd9HMVYDYC5VXmlYUT6NWgEOWdCNpad5Fve2CzzHoRED7WtsKe+FCyP5Vk4pWiQ== +webpack@5.54.0: + version "5.54.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.54.0.tgz#629f0cd14c7a4340af758a3c7cef25c50670ae4d" + integrity sha512-MAVKJMsIUotOQKzFOmN8ZkmMlj7BOyjDU6t1lomW9dWOme5WTStzGa3HMLdV1KYD1AiFETGsznL4LMSvj4tukw== dependencies: "@types/eslint-scope" "^3.7.0" "@types/estree" "^0.0.50" @@ -11656,8 +11664,8 @@ webpack@5.53.0: acorn-import-assertions "^1.7.6" browserslist "^4.14.5" chrome-trace-event "^1.0.2" - enhanced-resolve "^5.8.0" - es-module-lexer "^0.7.1" + enhanced-resolve "^5.8.3" + es-module-lexer "^0.9.0" eslint-scope "5.1.1" events "^3.2.0" glob-to-regexp "^0.4.1" From c5e5a9b8ef8b05f1d043c5f0597ae70bf5e4882a Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Thu, 30 Sep 2021 01:13:54 +0900 Subject: [PATCH 018/107] :art: --- src/client/components/debobigego/switch.vue | 55 ++++++++++----------- src/client/themes/_dark.json5 | 1 + src/client/themes/_light.json5 | 1 + 3 files changed, 29 insertions(+), 28 deletions(-) diff --git a/src/client/components/debobigego/switch.vue b/src/client/components/debobigego/switch.vue index 0ee0881236..abc380374d 100644 --- a/src/client/components/debobigego/switch.vue +++ b/src/client/components/debobigego/switch.vue @@ -13,7 +13,7 @@ @keydown.enter="toggle" > <span class="button" v-tooltip="checked ? $ts.itsOn : $ts.itsOff"> - <span></span> + <span class="handle"></span> </span> <span class="label"> <span><slot></slot></span> @@ -64,23 +64,6 @@ export default defineComponent({ user-select: none; } - &.disabled { - opacity: 0.6; - cursor: not-allowed; - } - - &.checked { - > .button { - background-color: var(--X10); - border-color: var(--X10); - - > * { - background-color: var(--accent); - transform: translateX(14px); - } - } - } - > input { position: absolute; width: 0; @@ -93,25 +76,26 @@ export default defineComponent({ position: relative; display: inline-block; flex-shrink: 0; - margin: 3px 0 0 0; + margin: 0; width: 34px; - height: 14px; - background: var(--X6); + height: 22px; + background: var(--switchBg); outline: none; - border-radius: 14px; + border-radius: 999px; transition: all 0.3s; cursor: pointer; - > * { + > .handle { position: absolute; - top: -3px; - left: 0; + top: 0; + left: 3px; + bottom: 0; + margin: auto 0; border-radius: 100%; transition: background-color 0.3s, transform 0.3s; - width: 20px; - height: 20px; + width: 16px; + height: 16px; background-color: #fff; - box-shadow: 0 2px 1px -1px rgba(#000, 0.2), 0 1px 1px 0 rgba(#000, 0.14), 0 1px 3px 0 rgba(#000, 0.12); } } @@ -127,6 +111,21 @@ export default defineComponent({ transition: inherit; } } + + &.disabled { + opacity: 0.6; + cursor: not-allowed; + } + + &.checked { + > .button { + background-color: var(--accent); + + > .handle { + transform: translateX(12px); + } + } + } } } </style> diff --git a/src/client/themes/_dark.json5 b/src/client/themes/_dark.json5 index e1d5779a80..22f4dc13d4 100644 --- a/src/client/themes/_dark.json5 +++ b/src/client/themes/_dark.json5 @@ -51,6 +51,7 @@ infoFg: '#fff', infoWarnBg: '#42321c', infoWarnFg: '#ffbd3e', + switchBg: 'rgba(255, 255, 255, 0.15)', cwBg: '#687390', cwFg: '#393f4f', cwHoverBg: '#707b97', diff --git a/src/client/themes/_light.json5 b/src/client/themes/_light.json5 index 87895e6406..64b92dba8a 100644 --- a/src/client/themes/_light.json5 +++ b/src/client/themes/_light.json5 @@ -51,6 +51,7 @@ infoFg: '#72818a', infoWarnBg: '#fff0db', infoWarnFg: '#8f6e31', + switchBg: 'rgba(0, 0, 0, 0.15)', cwBg: '#b1b9c1', cwFg: '#fff', cwHoverBg: '#bbc4ce', From 414f1d11582510f77ea3a927053fe68fa160278b Mon Sep 17 00:00:00 2001 From: Johann150 <johann.galle@protonmail.com> Date: Wed, 29 Sep 2021 18:44:22 +0200 Subject: [PATCH 019/107] fix: truncate image descriptions (#7699) * move truncate function to separate file to reuse it * truncate image descriptions * show image description limit in UI * correctly treat null Co-authored-by: nullobsi <me@nullob.si> * make truncate Unicode-aware The strings that truncate returns should now be valid Unicode. PostgreSQL also counts Unicode Code Points instead of bytes so this should be correct. * move truncate to internal, validate in API Truncating could also be done in src/services/drive/add-file.ts or src/services/drive/upload-from-url.ts but those would also affect local images. But local images should result in a hard error if the image comment is too long. * avoid overwriting Co-authored-by: nullobsi <me@nullob.si> --- src/client/components/media-caption.vue | 30 ++++++++++++++++--- src/misc/hard-limits.ts | 6 ++++ src/misc/truncate.ts | 11 +++++++ src/remote/activitypub/models/image.ts | 4 ++- src/remote/activitypub/models/person.ts | 11 +------ .../api/endpoints/drive/files/update.ts | 3 +- .../endpoints/drive/files/upload-from-url.ts | 3 +- 7 files changed, 51 insertions(+), 17 deletions(-) create mode 100644 src/misc/truncate.ts diff --git a/src/client/components/media-caption.vue b/src/client/components/media-caption.vue index 690927d4c5..73eba23025 100644 --- a/src/client/components/media-caption.vue +++ b/src/client/components/media-caption.vue @@ -3,10 +3,13 @@ <div class="container"> <div class="fullwidth top-caption"> <div class="mk-dialog"> - <header v-if="title"><Mfm :text="title"/></header> + <header> + <Mfm v-if="title" class="title" :text="title"/> + <span class="text-count" :class="{ over: remainingLength < 0 }">{{ remainingLength }}</span> + </header> <textarea autofocus v-model="inputValue" :placeholder="input.placeholder" @keydown="onInputKeydown"></textarea> <div class="buttons" v-if="(showOkButton || showCancelButton)"> - <MkButton inline @click="ok" primary>{{ $ts.ok }}</MkButton> + <MkButton inline @click="ok" primary :disabled="remainingLength < 0">{{ $ts.ok }}</MkButton> <MkButton inline @click="cancel" >{{ $ts.cancel }}</MkButton> </div> </div> @@ -26,10 +29,12 @@ <script lang="ts"> import { defineComponent } from 'vue'; +import { length } from 'stringz'; import MkModal from '@client/components/ui/modal.vue'; import MkButton from '@client/components/ui/button.vue'; import bytes from '@client/filters/bytes'; import number from '@client/filters/number'; +import { DB_MAX_IMAGE_COMMENT_LENGTH } from '@/misc/hard-limits'; export default defineComponent({ components: { @@ -79,6 +84,13 @@ export default defineComponent({ document.removeEventListener('keydown', this.onKeydown); }, + computed: { + remainingLength(): number { + if (typeof this.inputValue != "string") return DB_MAX_IMAGE_COMMENT_LENGTH; + return DB_MAX_IMAGE_COMMENT_LENGTH - length(this.inputValue); + } + }, + methods: { bytes, number, @@ -156,8 +168,18 @@ export default defineComponent({ > header { margin: 0 0 8px 0; - font-weight: bold; - font-size: 20px; + position: relative; + + > .title { + font-weight: bold; + font-size: 20px; + } + + > .text-count { + opacity: 0.7; + position: absolute; + right: 0; + } } > .buttons { diff --git a/src/misc/hard-limits.ts b/src/misc/hard-limits.ts index 2a61cb321b..1039f7335a 100644 --- a/src/misc/hard-limits.ts +++ b/src/misc/hard-limits.ts @@ -6,3 +6,9 @@ * Surrogate pairs count as one */ export const DB_MAX_NOTE_TEXT_LENGTH = 8192; + +/** + * Maximum image description length that can be stored in DB. + * Surrogate pairs count as one + */ +export const DB_MAX_IMAGE_COMMENT_LENGTH = 512; diff --git a/src/misc/truncate.ts b/src/misc/truncate.ts new file mode 100644 index 0000000000..cb120331a1 --- /dev/null +++ b/src/misc/truncate.ts @@ -0,0 +1,11 @@ +import { substring } from 'stringz'; + +export function truncate(input: string, size: number): string; +export function truncate(input: string | undefined, size: number): string | undefined; +export function truncate(input: string | undefined, size: number): string | undefined { + if (!input) { + return input; + } else { + return substring(input, 0, size); + } +} diff --git a/src/remote/activitypub/models/image.ts b/src/remote/activitypub/models/image.ts index cd28d59a16..89259d30fb 100644 --- a/src/remote/activitypub/models/image.ts +++ b/src/remote/activitypub/models/image.ts @@ -5,6 +5,8 @@ import { fetchMeta } from '@/misc/fetch-meta'; import { apLogger } from '../logger'; import { DriveFile } from '@/models/entities/drive-file'; import { DriveFiles } from '@/models/index'; +import { truncate } from '@/misc/truncate'; +import { DM_MAX_IMAGE_COMMENT_LENGTH } from '@/misc/hard-limits'; const logger = apLogger; @@ -28,7 +30,7 @@ export async function createImage(actor: IRemoteUser, value: any): Promise<Drive const instance = await fetchMeta(); const cache = instance.cacheRemoteFiles; - let file = await uploadFromUrl(image.url, actor, null, image.url, image.sensitive, false, !cache, image.name); + let file = await uploadFromUrl(image.url, actor, null, image.url, image.sensitive, false, !cache, truncate(image.name, DB_MAX_IMAGE_COMMENT_LENGTH)); if (file.isLink) { // URLが異なっている場合、同じ画像が以前に異なるURLで登録されていたということなので、 diff --git a/src/remote/activitypub/models/person.ts b/src/remote/activitypub/models/person.ts index 4823def7cb..84b2f0c51c 100644 --- a/src/remote/activitypub/models/person.ts +++ b/src/remote/activitypub/models/person.ts @@ -28,22 +28,13 @@ import { getConnection } from 'typeorm'; import { toArray } from '@/prelude/array'; import { fetchInstanceMetadata } from '@/services/fetch-instance-metadata'; import { normalizeForSearch } from '@/misc/normalize-for-search'; +import { truncate } from '@/misc/truncate'; const logger = apLogger; const nameLength = 128; const summaryLength = 2048; -function truncate(input: string, size: number): string; -function truncate(input: string | undefined, size: number): string | undefined; -function truncate(input: string | undefined, size: number): string | undefined { - if (!input || input.length <= size) { - return input; - } else { - return input.substring(0, size); - } -} - /** * Validate and convert to actor object * @param x Fetched object diff --git a/src/server/api/endpoints/drive/files/update.ts b/src/server/api/endpoints/drive/files/update.ts index 1ef445625c..f277a9c3dc 100644 --- a/src/server/api/endpoints/drive/files/update.ts +++ b/src/server/api/endpoints/drive/files/update.ts @@ -4,6 +4,7 @@ import { publishDriveStream } from '@/services/stream'; import define from '../../../define'; import { ApiError } from '../../../error'; import { DriveFiles, DriveFolders } from '@/models/index'; +import { DB_MAX_IMAGE_COMMENT_LENGTH } from '@/misc/hard-limits'; export const meta = { tags: ['drive'], @@ -33,7 +34,7 @@ export const meta = { }, comment: { - validator: $.optional.nullable.str, + validator: $.optional.nullable.str.max(DB_MAX_IMAGE_COMMENT_LENGTH), default: undefined as any, } }, diff --git a/src/server/api/endpoints/drive/files/upload-from-url.ts b/src/server/api/endpoints/drive/files/upload-from-url.ts index f37f316efb..9f10a42d24 100644 --- a/src/server/api/endpoints/drive/files/upload-from-url.ts +++ b/src/server/api/endpoints/drive/files/upload-from-url.ts @@ -5,6 +5,7 @@ import uploadFromUrl from '@/services/drive/upload-from-url'; import define from '../../../define'; import { DriveFiles } from '@/models/index'; import { publishMainStream } from '@/services/stream'; +import { DB_MAX_IMAGE_COMMENT_LENGTH } from '@/misc/hard-limits'; export const meta = { tags: ['drive'], @@ -35,7 +36,7 @@ export const meta = { }, comment: { - validator: $.optional.nullable.str, + validator: $.optional.nullable.str.max(DB_MAX_IMAGE_COMMENT_LENGTH), default: null, }, From 18fa317ee70b5d570637407a3f8a2888ab27cb6f Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Thu, 30 Sep 2021 03:07:47 +0900 Subject: [PATCH 020/107] fix bug --- src/client/directives/tooltip.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/client/directives/tooltip.ts b/src/client/directives/tooltip.ts index ee690558af..32d137b2e2 100644 --- a/src/client/directives/tooltip.ts +++ b/src/client/directives/tooltip.ts @@ -36,7 +36,7 @@ export default { }); } - const show = e => { + self.show = () => { if (!document.body.contains(el)) return; if (self._close) return; if (self.text == null) return; @@ -60,7 +60,7 @@ export default { el.addEventListener(start, () => { clearTimeout(self.showTimer); clearTimeout(self.hideTimer); - self.showTimer = setTimeout(show, delay); + self.showTimer = setTimeout(self.show, delay); }, { passive: true }); el.addEventListener(end, () => { @@ -75,6 +75,11 @@ export default { }); }, + updated(el, binding) { + const self = el._tooltipDirective_; + self.text = binding.value as string; + }, + unmounted(el, binding, vn) { const self = el._tooltipDirective_; clearInterval(self.checkTimer); From 834fb3bebd2f69c28cb67918f11006fca1fdd7db Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Thu, 30 Sep 2021 22:45:20 +0900 Subject: [PATCH 021/107] =?UTF-8?q?fix(client):=20=E3=83=A6=E3=83=BC?= =?UTF-8?q?=E3=82=B6=E3=83=BC=E3=83=9A=E3=83=BC=E3=82=B8=E3=81=AE=E3=82=BF?= =?UTF-8?q?=E3=83=96=E3=81=8C=E6=A9=9F=E8=83=BD=E3=81=97=E3=81=A6=E3=81=84?= =?UTF-8?q?=E3=81=AA=E3=81=84=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix #7853 --- CHANGELOG.md | 1 + src/client/pages/user/index.vue | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9bbc09860e..db271cdd41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ - admin inbox queue does not show individual jobs - クライアント: ヘッダーのタブが折り返される問題を修正 - クライアント: ヘッダーにタブが表示されている状態でタイトルをクリックしたときにタブ選択が表示されるのを修正 +- クライアント: ユーザーページのタブが機能していない問題を修正 ## 12.91.0 (2021/09/22) diff --git a/src/client/pages/user/index.vue b/src/client/pages/user/index.vue index 86dc7361b5..458ce4b618 100644 --- a/src/client/pages/user/index.vue +++ b/src/client/pages/user/index.vue @@ -255,21 +255,22 @@ export default defineComponent({ active: this.page === 'index', title: this.$ts.overview, icon: 'fas fa-home', + onClick: () => { this.$router.push('/@' + getAcct(this.user)); }, }, { active: this.page === 'clips', title: this.$ts.clips, icon: 'fas fa-paperclip', - onClick: () => { this.page = 'clips'; }, + onClick: () => { this.$router.push('/@' + getAcct(this.user) + '/clips'); }, }, { active: this.page === 'pages', title: this.$ts.pages, icon: 'fas fa-file-alt', - onClick: () => { this.page = 'pages'; }, + onClick: () => { this.$router.push('/@' + getAcct(this.user) + '/pages'); }, }, { active: this.page === 'gallery', title: this.$ts.gallery, icon: 'fas fa-icons', - onClick: () => { this.page = 'gallery'; }, + onClick: () => { this.$router.push('/@' + getAcct(this.user) + '/gallery'); }, }] } : null), user: null, From 7974dbf4778c022747a34bff9189d03393bf0c4e Mon Sep 17 00:00:00 2001 From: Johann150 <johann.galle@protonmail.com> Date: Thu, 30 Sep 2021 15:47:07 +0200 Subject: [PATCH 022/107] insert space for unknown MFM functions (#7851) --- src/client/components/mfm.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/components/mfm.ts b/src/client/components/mfm.ts index a228ca4b8d..2bdd7d46ee 100644 --- a/src/client/components/mfm.ts +++ b/src/client/components/mfm.ts @@ -185,7 +185,7 @@ export default defineComponent({ } } if (style == null) { - return h('span', {}, ['[', token.props.name, ...genEl(token.children), ']']); + return h('span', {}, ['[', token.props.name, ' ', ...genEl(token.children), ']']); } else { return h('span', { style: 'display: inline-block;' + style, From 68aa1312f5f784a9f8bbd3b88ede8538814884a7 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Thu, 30 Sep 2021 23:09:12 +0900 Subject: [PATCH 023/107] =?UTF-8?q?dev:=20develop=E3=83=96=E3=83=A9?= =?UTF-8?q?=E3=83=B3=E3=83=81=E3=82=92Docker=20Hub=E3=81=ABpush=E3=81=99?= =?UTF-8?q?=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolve #7845 --- .github/workflows/docker-develop.yml | 33 ++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 .github/workflows/docker-develop.yml diff --git a/.github/workflows/docker-develop.yml b/.github/workflows/docker-develop.yml new file mode 100644 index 0000000000..f03ad23ad1 --- /dev/null +++ b/.github/workflows/docker-develop.yml @@ -0,0 +1,33 @@ +name: Publish Docker image (develop) + +on: + push: + branches: + - develop + workflow_dispatch: + +jobs: + push_to_registry: + name: Push Docker image to Docker Hub + runs-on: ubuntu-latest + + steps: + - name: Check out the repo + uses: actions/checkout@v2 + - name: Docker meta + id: meta + uses: docker/metadata-action@v3 + with: + images: misskey/misskey + - name: Log in to Docker Hub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + - name: Build and Push to Docker Hub + uses: docker/build-push-action@v2 + with: + context: . + push: true + tags: develop + labels: develop From bd9df789d18fe51e4224a93cc2ed814729a97b92 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 1 Oct 2021 00:31:43 +0900 Subject: [PATCH 024/107] =?UTF-8?q?refactor:=20prelude/url=E3=81=A7queryst?= =?UTF-8?q?ring=E3=82=92=E4=BD=BF=E7=94=A8=E3=81=97=E3=81=AA=E3=81=84?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolve #7854 --- src/prelude/url.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/prelude/url.ts b/src/prelude/url.ts index a3613fc9b9..ce75fe89d1 100644 --- a/src/prelude/url.ts +++ b/src/prelude/url.ts @@ -1,9 +1,11 @@ -import { stringify } from 'querystring'; - export function query(obj: {}): string { - return stringify(Object.entries(obj) + const params = Object.entries(obj) .filter(([, v]) => Array.isArray(v) ? v.length : v !== undefined) - .reduce((a, [k, v]) => (a[k] = v, a), {} as Record<string, any>)); + .reduce((a, [k, v]) => (a[k] = v, a), {} as Record<string, any>); + + return Object.entries(params) + .map((e) => `${e[0]}=${e[1]}`) + .join('&'); } export function appendQuery(url: string, query: string): string { From a73a787753a24259ec0846ea5e9c92d072e1fe89 Mon Sep 17 00:00:00 2001 From: MeiMei <30769358+mei23@users.noreply.github.com> Date: Fri, 1 Oct 2021 02:25:57 +0900 Subject: [PATCH 025/107] Fix prelude/url (#7855) --- src/prelude/url.ts | 2 +- test/prelude/url.ts | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 test/prelude/url.ts diff --git a/src/prelude/url.ts b/src/prelude/url.ts index ce75fe89d1..c7f2b7c1e7 100644 --- a/src/prelude/url.ts +++ b/src/prelude/url.ts @@ -4,7 +4,7 @@ export function query(obj: {}): string { .reduce((a, [k, v]) => (a[k] = v, a), {} as Record<string, any>); return Object.entries(params) - .map((e) => `${e[0]}=${e[1]}`) + .map((e) => `${e[0]}=${encodeURIComponent(e[1])}`) .join('&'); } diff --git a/test/prelude/url.ts b/test/prelude/url.ts new file mode 100644 index 0000000000..1f814968a6 --- /dev/null +++ b/test/prelude/url.ts @@ -0,0 +1,13 @@ +import * as assert from 'assert'; +import { query } from '../../src/prelude/url'; + +describe('url', () => { + it('query', () => { + const s = query({ + foo: 'ふぅ', + bar: 'b a r', + baz: undefined + }); + assert.deepStrictEqual(s, 'foo=%E3%81%B5%E3%81%85&bar=b%20a%20r'); + }); +}); From 027380c013e92c78d895f7f7a1056a11044b7986 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 1 Oct 2021 19:32:16 +0900 Subject: [PATCH 026/107] fix bug --- src/client/components/captcha.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/client/components/captcha.vue b/src/client/components/captcha.vue index 5da8ede3b9..baa922506e 100644 --- a/src/client/components/captcha.vue +++ b/src/client/components/captcha.vue @@ -39,7 +39,7 @@ export default defineComponent({ type: String, required: true, }, - value: { + modelValue: { type: String, }, }, @@ -116,7 +116,7 @@ export default defineComponent({ } }, callback(response?: string) { - this.$emit('update:value', typeof response == 'string' ? response : null); + this.$emit('update:modelValue', typeof response == 'string' ? response : null); }, }, }); From f9185f201aebf4e5cf870162ec82a93688268b7b Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 1 Oct 2021 19:34:24 +0900 Subject: [PATCH 027/107] fix bug --- src/client/components/tab.vue | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/client/components/tab.vue b/src/client/components/tab.vue index 7705fc3d6d..58538c2ca1 100644 --- a/src/client/components/tab.vue +++ b/src/client/components/tab.vue @@ -13,11 +13,11 @@ export default defineComponent({ return withDirectives(h('div', { class: 'pxhvhrfw', }, options.map(option => withDirectives(h('button', { - class: ['_button', { active: this.modelValue === option.props.modelValue }], + class: ['_button', { active: this.modelValue === option.props.value }], key: option.key, - disabled: this.modelValue === option.props.modelValue, + disabled: this.modelValue === option.props.value, onClick: () => { - this.$emit('update:modelValue', option.props.modelValue); + this.$emit('update:modelValue', option.props.value); } }, option.children), [ [resolveDirective('click-anime')] From 8d3fe0c5c24622008f9fa1f465d2b8a44947c2e4 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 2 Oct 2021 00:51:37 +0900 Subject: [PATCH 028/107] :art: --- src/client/components/ui/info.vue | 9 --- src/client/pages/page-editor/page-editor.vue | 1 + src/client/pages/settings/index.vue | 59 +++++++++----------- 3 files changed, 28 insertions(+), 41 deletions(-) diff --git a/src/client/components/ui/info.vue b/src/client/components/ui/info.vue index 513682ef55..4dc6b58373 100644 --- a/src/client/components/ui/info.vue +++ b/src/client/components/ui/info.vue @@ -27,7 +27,6 @@ export default defineComponent({ <style lang="scss" scoped> .fpezltsf { - margin: 16px 0; padding: 16px; font-size: 90%; background: var(--infoBg); @@ -39,14 +38,6 @@ export default defineComponent({ color: var(--infoWarnFg); } - &:first-child { - margin-top: 0; - } - - &:last-child { - margin-bottom: 0; - } - > i { margin-right: 4px; } diff --git a/src/client/pages/page-editor/page-editor.vue b/src/client/pages/page-editor/page-editor.vue index 2d617bee75..74cfd29dae 100644 --- a/src/client/pages/page-editor/page-editor.vue +++ b/src/client/pages/page-editor/page-editor.vue @@ -142,6 +142,7 @@ export default defineComponent({ return { title: title, icon: 'fas fa-pencil-alt', + bg: 'var(--bg)', }; }), author: this.$i, diff --git a/src/client/pages/settings/index.vue b/src/client/pages/settings/index.vue index f3d118e4f0..25c83a823f 100644 --- a/src/client/pages/settings/index.vue +++ b/src/client/pages/settings/index.vue @@ -1,17 +1,13 @@ <template> <div class="vvcocwet" :class="{ wide: !narrow }" ref="el"> <div class="nav" v-if="!narrow || page == null"> - <FormGroup> - <div class="_debobigegoItem"> - <div class="_debobigegoPanel lwjxoukj"> - <MkAvatar :user="$i" class="avatar"/> - </div> - </div> + <div class="group"> + <MkAvatar :user="$i" class="avatar"/> <XLink :active="page === 'accounts'" replace to="/settings/accounts"><template #icon><i class="fas fa-users"></i></template>{{ $ts.accounts }}</XLink> - </FormGroup> - <FormInfo v-if="emailNotConfigured" warn>{{ $ts.emailNotConfiguredWarning }} <MkA to="/settings/email" class="_link">{{ $ts.configure }}</MkA></FormInfo> - <FormGroup> - <template #label>{{ $ts.basicSettings }}</template> + </div> + <MkInfo v-if="emailNotConfigured || true" warn>{{ $ts.emailNotConfiguredWarning }} <MkA to="/settings/email" class="_link">{{ $ts.configure }}</MkA></MkInfo> + <div class="group"> + <div class="label">{{ $ts.basicSettings }}</div> <XLink :active="page === 'profile'" replace to="/settings/profile"><template #icon><i class="fas fa-user"></i></template>{{ $ts.profile }}</XLink> <XLink :active="page === 'privacy'" replace to="/settings/privacy"><template #icon><i class="fas fa-lock-open"></i></template>{{ $ts.privacy }}</XLink> <XLink :active="page === 'reaction'" replace to="/settings/reaction"><template #icon><i class="fas fa-laugh"></i></template>{{ $ts.reaction }}</XLink> @@ -20,23 +16,23 @@ <XLink :active="page === 'email'" replace to="/settings/email"><template #icon><i class="fas fa-envelope"></i></template>{{ $ts.email }}</XLink> <XLink :active="page === 'integration'" replace to="/settings/integration"><template #icon><i class="fas fa-share-alt"></i></template>{{ $ts.integration }}</XLink> <XLink :active="page === 'security'" replace to="/settings/security"><template #icon><i class="fas fa-lock"></i></template>{{ $ts.security }}</XLink> - </FormGroup> - <FormGroup> - <template #label>{{ $ts.clientSettings }}</template> + </div> + <div class="group"> + <div class="label">{{ $ts.clientSettings }}</div> <XLink :active="page === 'general'" replace to="/settings/general"><template #icon><i class="fas fa-cogs"></i></template>{{ $ts.general }}</XLink> <XLink :active="page === 'theme'" replace to="/settings/theme"><template #icon><i class="fas fa-palette"></i></template>{{ $ts.theme }}</XLink> <XLink :active="page === 'menu'" replace to="/settings/menu"><template #icon><i class="fas fa-list-ul"></i></template>{{ $ts.menu }}</XLink> <XLink :active="page === 'sounds'" replace to="/settings/sounds"><template #icon><i class="fas fa-music"></i></template>{{ $ts.sounds }}</XLink> <XLink :active="page === 'plugin'" replace to="/settings/plugin"><template #icon><i class="fas fa-plug"></i></template>{{ $ts.plugins }}</XLink> - </FormGroup> - <FormGroup> - <template #label>{{ $ts.otherSettings }}</template> + </div> + <div class="group"> + <div class="label">{{ $ts.otherSettings }}</div> <XLink :active="page === 'import-export'" replace to="/settings/import-export"><template #icon><i class="fas fa-boxes"></i></template>{{ $ts.importAndExport }}</XLink> <XLink :active="page === 'mute-block'" replace to="/settings/mute-block"><template #icon><i class="fas fa-ban"></i></template>{{ $ts.muteAndBlock }}</XLink> <XLink :active="page === 'word-mute'" replace to="/settings/word-mute"><template #icon><i class="fas fa-comment-slash"></i></template>{{ $ts.wordMute }}</XLink> <XLink :active="page === 'api'" replace to="/settings/api"><template #icon><i class="fas fa-key"></i></template>API</XLink> <XLink :active="page === 'other'" replace to="/settings/other"><template #icon><i class="fas fa-ellipsis-h"></i></template>{{ $ts.other }}</XLink> - </FormGroup> + </div> <FormGroup> <FormButton @click="clear">{{ $ts.clearCache }}</FormButton> </FormGroup> @@ -55,9 +51,8 @@ import { computed, defineAsyncComponent, defineComponent, nextTick, onMounted, r import { i18n } from '@client/i18n'; import XLink from './index.link.vue'; import FormGroup from '@client/components/debobigego/group.vue'; -import FormBase from '@client/components/debobigego/base.vue'; import FormButton from '@client/components/debobigego/button.vue'; -import FormInfo from '@client/components/debobigego/info.vue'; +import MkInfo from '@client/components/ui/info.vue'; import { scroll } from '@client/scripts/scroll'; import { signout } from '@client/account'; import { unisonReload } from '@client/scripts/unison-reload'; @@ -67,11 +62,10 @@ import { $i } from '@client/account'; export default defineComponent({ components: { - FormBase, XLink, FormGroup, FormButton, - FormInfo, + MkInfo, }, props: { @@ -206,6 +200,18 @@ export default defineComponent({ <style lang="scss" scoped> .vvcocwet { + > .nav { + > .group { + padding: 16px; + + > .label { + font-size: 0.9em; + opacity: 0.7; + margin: 0 0 8px 8px; + } + } + } + &.wide { display: flex; max-width: 1000px; @@ -226,15 +232,4 @@ export default defineComponent({ } } } - -.lwjxoukj { - padding: 16px; - - > .avatar { - display: block; - margin: auto; - width: 42px; - height: 42px; - } -} </style> From 9109ae02a7f34b8e4f59bb0f2ba3e332549ac222 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 2 Oct 2021 01:08:04 +0900 Subject: [PATCH 029/107] chore: fix bug --- src/client/scripts/physics.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/scripts/physics.ts b/src/client/scripts/physics.ts index 8e971d5844..445b6296eb 100644 --- a/src/client/scripts/physics.ts +++ b/src/client/scripts/physics.ts @@ -79,7 +79,7 @@ export function physics(container: HTMLElement) { objEl.offsetWidth, objEl.offsetHeight, { - chamfer: { radius: parseInt(style.borderRadius, 10) }, + chamfer: { radius: parseInt(style.borderRadius || '0', 10) }, restitution: 0.5 } ); From 19d531922df3d1dda6d3e887a72e6d39798408a7 Mon Sep 17 00:00:00 2001 From: tamaina <tamaina@hotmail.co.jp> Date: Sat, 2 Oct 2021 16:36:25 +0900 Subject: [PATCH 030/107] =?UTF-8?q?enhance:=20=E3=83=9A=E3=83=BC=E3=82=B8?= =?UTF-8?q?=E3=83=AD=E3=83=BC=E3=83=89=E3=82=A8=E3=83=A9=E3=83=BC=E3=83=9A?= =?UTF-8?q?=E3=83=BC=E3=82=B8=E3=81=AB=E3=83=AA=E3=83=AD=E3=83=BC=E3=83=89?= =?UTF-8?q?=E3=83=9C=E3=82=BF=E3=83=B3=E3=82=92=E8=BF=BD=E5=8A=A0=20(#7835?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * wip * modify page load error page * add changelog * サーバーが死んでるエラーを追加 * add MkLoading --- CHANGELOG.md | 1 + locales/ja-JP.yml | 2 ++ src/client/pages/_error_.vue | 38 +++++++++++++++++++++++++++++++++--- 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index db271cdd41..8b6c357c4f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## 12.x.x (unreleased) ### Improvements +- ページロードエラーページにリロードボタンを追加 ### Bugfixes diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index f5f859b9ff..06b10ba278 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -81,6 +81,8 @@ somethingHappened: "問題が発生しました" retry: "再試行" pageLoadError: "ページの読み込みに失敗しました。" pageLoadErrorDescription: "これは通常、ネットワークまたはブラウザキャッシュが原因です。キャッシュをクリアするか、しばらく待ってから再度試してください。" +serverIsDead: "サーバーの応答がありません。しばらく待ってから再度試してください。" +youShouldUpgradeClient: "このページを表示するためには、リロードして新しいバージョンのクライアントをご利用ください。" enterListName: "リスト名を入力" privacy: "プライバシー" makeFollowManuallyApprove: "フォローを承認制にする" diff --git a/src/client/pages/_error_.vue b/src/client/pages/_error_.vue index 1d67d9b14d..d1cefad8dd 100644 --- a/src/client/pages/_error_.vue +++ b/src/client/pages/_error_.vue @@ -1,9 +1,16 @@ <template> +<MkLoading v-if="!loaded" /> <transition :name="$store.state.animation ? 'zoom' : ''" appear> - <div class="mjndxjch"> + <div class="mjndxjch" v-show="loaded"> <img src="https://xn--931a.moe/assets/error.jpg" class="_ghost"/> <p><b><i class="fas fa-exclamation-triangle"></i> {{ $ts.pageLoadError }}</b></p> - <p>{{ $ts.pageLoadErrorDescription }}</p> + <p v-if="version === meta.version">{{ $ts.pageLoadErrorDescription }}</p> + <p v-else-if="serverIsDead">{{ $ts.serverIsDead }}</p> + <template v-else> + <p>{{ $ts.newVersionOfClientAvailable }}</p> + <p>{{ $ts.youShouldUpgradeClient }}</p> + <MkButton @click="reload" class="button primary">{{ $ts.reload }}</MkButton> + </template> <p><MkA to="/docs/general/troubleshooting" class="_link">{{ $ts.troubleshooting }}</MkA></p> <p v-if="error" class="error">ERROR: {{ error }}</p> </div> @@ -14,6 +21,9 @@ import { defineComponent } from 'vue'; import MkButton from '@client/components/ui/button.vue'; import * as symbols from '@client/symbols'; +import { version } from '@client/config'; +import * as os from '@client/os'; +import { unisonReload } from '@client/scripts/unison-reload'; export default defineComponent({ components: { @@ -30,8 +40,30 @@ export default defineComponent({ title: this.$ts.error, icon: 'fas fa-exclamation-triangle' }, + loaded: false, + serverIsDead: false, + meta: {} as any, + version, }; }, + created() { + os.api('meta', { + detail: false + }).then(meta => { + this.loaded = true; + this.serverIsDead = false; + this.meta = meta; + localStorage.setItem('v', meta.version); + }, () => { + this.loaded = true; + this.serverIsDead = true; + }); + }, + methods: { + reload() { + unisonReload(); + }, + }, }); </script> @@ -45,7 +77,7 @@ export default defineComponent({ } > .button { - margin: 0 auto; + margin: 8px auto; } > img { From 3e81ebf8e98bb9f7a7662509be4fe0bff9a01d27 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 2 Oct 2021 23:11:21 +0900 Subject: [PATCH 031/107] :art: --- src/client/pages/federation.vue | 3 +- src/client/pages/instance/abuses.vue | 3 +- src/client/pages/instance/ads.vue | 3 +- src/client/pages/instance/announcements.vue | 3 +- src/client/pages/instance/emojis.vue | 1 + src/client/pages/instance/files.vue | 40 +++++--- src/client/pages/instance/index.link.vue | 97 +++++++++++++++++++ src/client/pages/instance/index.vue | 102 +++++++++++--------- src/client/pages/instance/overview.vue | 3 +- src/client/pages/instance/users.vue | 28 ++++-- src/client/pages/settings/index.vue | 19 +++- 11 files changed, 227 insertions(+), 75 deletions(-) create mode 100644 src/client/pages/instance/index.link.vue diff --git a/src/client/pages/federation.vue b/src/client/pages/federation.vue index aac94e00af..eae6a05367 100644 --- a/src/client/pages/federation.vue +++ b/src/client/pages/federation.vue @@ -116,7 +116,8 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.federation, - icon: 'fas fa-globe' + icon: 'fas fa-globe', + bg: 'var(--bg)', }, host: '', state: 'federating', diff --git a/src/client/pages/instance/abuses.vue b/src/client/pages/instance/abuses.vue index a66847938d..29da8cc2c5 100644 --- a/src/client/pages/instance/abuses.vue +++ b/src/client/pages/instance/abuses.vue @@ -86,7 +86,8 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.abuseReports, - icon: 'fas fa-exclamation-circle' + icon: 'fas fa-exclamation-circle', + bg: 'var(--bg)', }, searchUsername: '', searchHost: '', diff --git a/src/client/pages/instance/ads.vue b/src/client/pages/instance/ads.vue index 8742d0bda1..c54c1c0280 100644 --- a/src/client/pages/instance/ads.vue +++ b/src/client/pages/instance/ads.vue @@ -64,7 +64,8 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.ads, - icon: 'fas fa-audio-description' + icon: 'fas fa-audio-description', + bg: 'var(--bg)', }, ads: [], } diff --git a/src/client/pages/instance/announcements.vue b/src/client/pages/instance/announcements.vue index 35d676cf28..e4f7334c05 100644 --- a/src/client/pages/instance/announcements.vue +++ b/src/client/pages/instance/announcements.vue @@ -43,7 +43,8 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.announcements, - icon: 'fas fa-broadcast-tower' + icon: 'fas fa-broadcast-tower', + bg: 'var(--bg)', }, announcements: [], } diff --git a/src/client/pages/instance/emojis.vue b/src/client/pages/instance/emojis.vue index 6118d869e9..219955dc45 100644 --- a/src/client/pages/instance/emojis.vue +++ b/src/client/pages/instance/emojis.vue @@ -78,6 +78,7 @@ export default defineComponent({ [symbols.PAGE_INFO]: { title: this.$ts.customEmojis, icon: 'fas fa-laugh', + bg: 'var(--bg)', action: { icon: 'fas fa-plus', handler: this.add diff --git a/src/client/pages/instance/files.vue b/src/client/pages/instance/files.vue index df2431ad02..55189cfd84 100644 --- a/src/client/pages/instance/files.vue +++ b/src/client/pages/instance/files.vue @@ -1,20 +1,14 @@ <template> <div class="xrmjdkdw"> - <div class="_section"> - <div class="_content"> - <MkButton primary @click="clear()"><i class="fas fa-trash-alt"></i> {{ $ts.clearCachedFiles }}</MkButton> - </div> - </div> - - <div class="_section lookup"> - <div class="_title"><i class="fas fa-search"></i> {{ $ts.lookup }}</div> - <div class="_content"> - <MkInput class="target" v-model="q" type="text" @enter="find()"> + <MkContainer :foldable="true" class="lookup"> + <template #header><i class="fas fa-search"></i> {{ $ts.lookup }}</template> + <div class="xrmjdkdw-lookup"> + <MkInput class="item" v-model="q" type="text" @enter="find()"> <template #label>{{ $ts.fileIdOrUrl }}</template> </MkInput> <MkButton @click="find()" primary><i class="fas fa-search"></i> {{ $ts.lookup }}</MkButton> </div> - </div> + </MkContainer> <div class="_section"> <div class="_content"> @@ -31,7 +25,7 @@ </div> <div class="inputs" style="display: flex; padding-top: 1.2em;"> <MkInput v-model="type" :debounce="true" type="search" style="margin: 0; flex: 1;"> - <template #label>{{ $ts.type }}</template> + <template #label>MIME type</template> </MkInput> </div> <MkPagination :pagination="pagination" #default="{items}" class="urempief" ref="files"> @@ -66,6 +60,7 @@ import MkButton from '@client/components/ui/button.vue'; import MkInput from '@client/components/form/input.vue'; import MkSelect from '@client/components/form/select.vue'; import MkPagination from '@client/components/ui/pagination.vue'; +import MkContainer from '@client/components/ui/container.vue'; import MkDriveFileThumbnail from '@client/components/drive-file-thumbnail.vue'; import bytes from '@client/filters/bytes'; import * as os from '@client/os'; @@ -77,6 +72,7 @@ export default defineComponent({ MkInput, MkSelect, MkPagination, + MkContainer, MkDriveFileThumbnail, }, @@ -86,7 +82,13 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.files, - icon: 'fas fa-cloud' + icon: 'fas fa-cloud', + bg: 'var(--bg)', + actions: [{ + text: this.$ts.clearCachedFiles, + icon: 'fas fa-trash-alt', + handler: this.clear + }] }, q: null, origin: 'local', @@ -161,6 +163,10 @@ export default defineComponent({ .xrmjdkdw { margin: var(--margin); + > .lookup { + margin-bottom: 16px; + } + .urempief { margin-top: var(--margin); @@ -192,4 +198,12 @@ export default defineComponent({ } } } + +.xrmjdkdw-lookup { + padding: 16px; + + > .item { + margin-bottom: 16px; + } +} </style> diff --git a/src/client/pages/instance/index.link.vue b/src/client/pages/instance/index.link.vue new file mode 100644 index 0000000000..e1f4773800 --- /dev/null +++ b/src/client/pages/instance/index.link.vue @@ -0,0 +1,97 @@ +<template> +<div class="qmfkfnzh"> + <a class="main _button" :href="to" target="_blank" v-if="external"> + <span class="icon"><slot name="icon"></slot></span> + <span class="text"><slot></slot></span> + </a> + <MkA class="main _button" :class="{ active }" :to="to" :behavior="behavior" v-else> + <span class="icon"><slot name="icon"></slot></span> + <span class="text"><slot></slot></span> + </MkA> +</div> +</template> + +<script lang="ts"> +import { defineComponent } from 'vue'; + +export default defineComponent({ + props: { + to: { + type: String, + required: true + }, + active: { + type: Boolean, + required: false + }, + external: { + type: Boolean, + required: false + }, + behavior: { + type: String, + required: false, + }, + }, + data() { + return { + }; + } +}); +</script> + +<style lang="scss" scoped> +.qmfkfnzh { + > .main { + display: flex; + align-items: center; + width: 100%; + box-sizing: border-box; + padding: 10px 16px 10px 14px; + border-radius: 999px; + font-size: 0.9em; + + &:hover { + text-decoration: none; + background: var(--panelHighlight); + } + + &.active { + color: var(--accent); + background: var(--accentedBg); + } + + > .icon { + width: 32px; + margin-right: 2px; + flex-shrink: 0; + text-align: center; + opacity: 0.8; + + &:empty { + display: none; + + & + .text { + padding-left: 4px; + } + } + } + + > .text { + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + padding-right: 12px; + } + + > .right { + margin-left: auto; + opacity: 0.7; + + > .text:not(:empty) { + margin-right: 0.75em; + } + } + } +} +</style> diff --git a/src/client/pages/instance/index.vue b/src/client/pages/instance/index.vue index 657a654e51..780dd099b6 100644 --- a/src/client/pages/instance/index.vue +++ b/src/client/pages/instance/index.vue @@ -1,51 +1,49 @@ <template> <div class="hiyeyicy" :class="{ wide: !narrow }" ref="el"> <div class="nav" v-if="!narrow || page == null"> - <FormBase> - <FormGroup> - <div class="_debobigegoItem"> - <div class="_debobigegoPanel lxpfedzu"> - <img :src="$instance.iconUrl || '/favicon.ico'" alt="" class="icon"/> - </div> + <div class="group"> + <div class="_debobigegoItem"> + <div class="_debobigegoPanel lxpfedzu"> + <img :src="$instance.iconUrl || '/favicon.ico'" alt="" class="icon"/> </div> - <FormLink :active="page === 'overview'" replace to="/instance/overview"><template #icon><i class="fas fa-tachometer-alt"></i></template>{{ $ts.overview }}</FormLink> - </FormGroup> - <FormGroup> - <template #label>{{ $ts.quickAction }}</template> - <FormButton @click="lookup"><i class="fas fa-search"></i> {{ $ts.lookup }}</FormButton> - <FormButton v-if="$instance.disableRegistration" @click="invite"><i class="fas fa-user"></i> {{ $ts.invite }}</FormButton> - </FormGroup> - <FormGroup> - <template #label>{{ $ts.administration }}</template> - <FormLink :active="page === 'users'" replace to="/instance/users"><template #icon><i class="fas fa-users"></i></template>{{ $ts.users }}</FormLink> - <FormLink :active="page === 'emojis'" replace to="/instance/emojis"><template #icon><i class="fas fa-laugh"></i></template>{{ $ts.customEmojis }}</FormLink> - <FormLink :active="page === 'federation'" replace to="/instance/federation"><template #icon><i class="fas fa-globe"></i></template>{{ $ts.federation }}</FormLink> - <FormLink :active="page === 'queue'" replace to="/instance/queue"><template #icon><i class="fas fa-clipboard-list"></i></template>{{ $ts.jobQueue }}</FormLink> - <FormLink :active="page === 'files'" replace to="/instance/files"><template #icon><i class="fas fa-cloud"></i></template>{{ $ts.files }}</FormLink> - <FormLink :active="page === 'announcements'" replace to="/instance/announcements"><template #icon><i class="fas fa-broadcast-tower"></i></template>{{ $ts.announcements }}</FormLink> - <FormLink :active="page === 'ads'" replace to="/instance/ads"><template #icon><i class="fas fa-audio-description"></i></template>{{ $ts.ads }}</FormLink> - <FormLink :active="page === 'abuses'" replace to="/instance/abuses"><template #icon><i class="fas fa-exclamation-circle"></i></template>{{ $ts.abuseReports }}</FormLink> - </FormGroup> - <FormGroup> - <template #label>{{ $ts.settings }}</template> - <FormLink :active="page === 'settings'" replace to="/instance/settings"><template #icon><i class="fas fa-cog"></i></template>{{ $ts.general }}</FormLink> - <FormLink :active="page === 'files-settings'" replace to="/instance/files-settings"><template #icon><i class="fas fa-cloud"></i></template>{{ $ts.files }}</FormLink> - <FormLink :active="page === 'email-settings'" replace to="/instance/email-settings"><template #icon><i class="fas fa-envelope"></i></template>{{ $ts.emailServer }}</FormLink> - <FormLink :active="page === 'object-storage'" replace to="/instance/object-storage"><template #icon><i class="fas fa-cloud"></i></template>{{ $ts.objectStorage }}</FormLink> - <FormLink :active="page === 'security'" replace to="/instance/security"><template #icon><i class="fas fa-lock"></i></template>{{ $ts.security }}</FormLink> - <FormLink :active="page === 'service-worker'" replace to="/instance/service-worker"><template #icon><i class="fas fa-bolt"></i></template>ServiceWorker</FormLink> - <FormLink :active="page === 'relays'" replace to="/instance/relays"><template #icon><i class="fas fa-globe"></i></template>{{ $ts.relays }}</FormLink> - <FormLink :active="page === 'integrations'" replace to="/instance/integrations"><template #icon><i class="fas fa-share-alt"></i></template>{{ $ts.integration }}</FormLink> - <FormLink :active="page === 'instance-block'" replace to="/instance/instance-block"><template #icon><i class="fas fa-ban"></i></template>{{ $ts.instanceBlocking }}</FormLink> - <FormLink :active="page === 'proxy-account'" replace to="/instance/proxy-account"><template #icon><i class="fas fa-ghost"></i></template>{{ $ts.proxyAccount }}</FormLink> - <FormLink :active="page === 'other-settings'" replace to="/instance/other-settings"><template #icon><i class="fas fa-cogs"></i></template>{{ $ts.other }}</FormLink> - </FormGroup> - <FormGroup> - <template #label>{{ $ts.info }}</template> - <FormLink :active="page === 'database'" replace to="/instance/database"><template #icon><i class="fas fa-database"></i></template>{{ $ts.database }}</FormLink> - <FormLink :active="page === 'logs'" replace to="/instance/logs"><template #icon><i class="fas fa-stream"></i></template>{{ $ts.logs }}</FormLink> - </FormGroup> - </FormBase> + </div> + <XLink :active="page === 'overview'" replace to="/instance/overview"><template #icon><i class="fas fa-tachometer-alt"></i></template>{{ $ts.overview }}</XLink> + </div> + <div class="group"> + <div class="label">{{ $ts.quickAction }}</div> + <FormButton @click="lookup"><i class="fas fa-search"></i> {{ $ts.lookup }}</FormButton> + <FormButton v-if="$instance.disableRegistration" @click="invite"><i class="fas fa-user"></i> {{ $ts.invite }}</FormButton> + </div> + <div class="group"> + <div class="label">{{ $ts.administration }}</div> + <XLink :active="page === 'users'" replace to="/instance/users"><template #icon><i class="fas fa-users"></i></template>{{ $ts.users }}</XLink> + <XLink :active="page === 'emojis'" replace to="/instance/emojis"><template #icon><i class="fas fa-laugh"></i></template>{{ $ts.customEmojis }}</XLink> + <XLink :active="page === 'federation'" replace to="/instance/federation"><template #icon><i class="fas fa-globe"></i></template>{{ $ts.federation }}</XLink> + <XLink :active="page === 'queue'" replace to="/instance/queue"><template #icon><i class="fas fa-clipboard-list"></i></template>{{ $ts.jobQueue }}</XLink> + <XLink :active="page === 'files'" replace to="/instance/files"><template #icon><i class="fas fa-cloud"></i></template>{{ $ts.files }}</XLink> + <XLink :active="page === 'announcements'" replace to="/instance/announcements"><template #icon><i class="fas fa-broadcast-tower"></i></template>{{ $ts.announcements }}</XLink> + <XLink :active="page === 'ads'" replace to="/instance/ads"><template #icon><i class="fas fa-audio-description"></i></template>{{ $ts.ads }}</XLink> + <XLink :active="page === 'abuses'" replace to="/instance/abuses"><template #icon><i class="fas fa-exclamation-circle"></i></template>{{ $ts.abuseReports }}</XLink> + </div> + <div class="group"> + <div class="label">{{ $ts.settings }}</div> + <XLink :active="page === 'settings'" replace to="/instance/settings"><template #icon><i class="fas fa-cog"></i></template>{{ $ts.general }}</XLink> + <XLink :active="page === 'files-settings'" replace to="/instance/files-settings"><template #icon><i class="fas fa-cloud"></i></template>{{ $ts.files }}</XLink> + <XLink :active="page === 'email-settings'" replace to="/instance/email-settings"><template #icon><i class="fas fa-envelope"></i></template>{{ $ts.emailServer }}</XLink> + <XLink :active="page === 'object-storage'" replace to="/instance/object-storage"><template #icon><i class="fas fa-cloud"></i></template>{{ $ts.objectStorage }}</XLink> + <XLink :active="page === 'security'" replace to="/instance/security"><template #icon><i class="fas fa-lock"></i></template>{{ $ts.security }}</XLink> + <XLink :active="page === 'service-worker'" replace to="/instance/service-worker"><template #icon><i class="fas fa-bolt"></i></template>ServiceWorker</XLink> + <XLink :active="page === 'relays'" replace to="/instance/relays"><template #icon><i class="fas fa-globe"></i></template>{{ $ts.relays }}</XLink> + <XLink :active="page === 'integrations'" replace to="/instance/integrations"><template #icon><i class="fas fa-share-alt"></i></template>{{ $ts.integration }}</XLink> + <XLink :active="page === 'instance-block'" replace to="/instance/instance-block"><template #icon><i class="fas fa-ban"></i></template>{{ $ts.instanceBlocking }}</XLink> + <XLink :active="page === 'proxy-account'" replace to="/instance/proxy-account"><template #icon><i class="fas fa-ghost"></i></template>{{ $ts.proxyAccount }}</XLink> + <XLink :active="page === 'other-settings'" replace to="/instance/other-settings"><template #icon><i class="fas fa-cogs"></i></template>{{ $ts.other }}</XLink> + </div> + <div class="group"> + <div class="label">{{ $ts.info }}</div> + <XLink :active="page === 'database'" replace to="/instance/database"><template #icon><i class="fas fa-database"></i></template>{{ $ts.database }}</XLink> + <XLink :active="page === 'logs'" replace to="/instance/logs"><template #icon><i class="fas fa-stream"></i></template>{{ $ts.logs }}</XLink> + </div> </div> <div class="main"> <component :is="component" :key="page" @info="onInfo" v-bind="pageProps"/> @@ -56,7 +54,7 @@ <script lang="ts"> import { computed, defineAsyncComponent, defineComponent, nextTick, onMounted, reactive, ref, watch } from 'vue'; import { i18n } from '@client/i18n'; -import FormLink from '@client/components/debobigego/link.vue'; +import XLink from './index.link.vue'; import FormGroup from '@client/components/debobigego/group.vue'; import FormBase from '@client/components/debobigego/base.vue'; import FormButton from '@client/components/debobigego/button.vue'; @@ -68,7 +66,7 @@ import { lookupUser } from '@client/scripts/lookup-user'; export default defineComponent({ components: { FormBase, - FormLink, + XLink, FormGroup, FormButton, }, @@ -214,15 +212,25 @@ export default defineComponent({ .hiyeyicy { &.wide { display: flex; - max-width: 1100px; margin: 0 auto; height: 100%; > .nav { width: 32%; + max-width: 320px; box-sizing: border-box; border-right: solid 0.5px var(--divider); overflow: auto; + + > .group { + padding: 16px; + + > .label { + font-size: 0.9em; + opacity: 0.7; + margin: 0 0 8px 12px; + } + } } > .main { diff --git a/src/client/pages/instance/overview.vue b/src/client/pages/instance/overview.vue index 28dcfc03bf..61fbc03c64 100644 --- a/src/client/pages/instance/overview.vue +++ b/src/client/pages/instance/overview.vue @@ -86,7 +86,8 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.overview, - icon: 'fas fa-tachometer-alt' + icon: 'fas fa-tachometer-alt', + bg: 'var(--bg)', }, page: 'index', version, diff --git a/src/client/pages/instance/users.vue b/src/client/pages/instance/users.vue index 7671387f01..b72d3f7d3c 100644 --- a/src/client/pages/instance/users.vue +++ b/src/client/pages/instance/users.vue @@ -6,15 +6,15 @@ </div> <div class="users"> - <div class="inputs" style="display: flex;"> - <MkSelect v-model="sort" style="margin: 0; flex: 1;"> + <div class="inputs"> + <MkSelect v-model="sort" style="flex: 1;"> <template #label>{{ $ts.sort }}</template> <option value="-createdAt">{{ $ts.registeredDate }} ({{ $ts.ascendingOrder }})</option> <option value="+createdAt">{{ $ts.registeredDate }} ({{ $ts.descendingOrder }})</option> <option value="-updatedAt">{{ $ts.lastUsed }} ({{ $ts.ascendingOrder }})</option> <option value="+updatedAt">{{ $ts.lastUsed }} ({{ $ts.descendingOrder }})</option> </MkSelect> - <MkSelect v-model="state" style="margin: 0; flex: 1;"> + <MkSelect v-model="state" style="flex: 1;"> <template #label>{{ $ts.state }}</template> <option value="all">{{ $ts.all }}</option> <option value="available">{{ $ts.normal }}</option> @@ -23,18 +23,18 @@ <option value="silenced">{{ $ts.silence }}</option> <option value="suspended">{{ $ts.suspend }}</option> </MkSelect> - <MkSelect v-model="origin" style="margin: 0; flex: 1;"> + <MkSelect v-model="origin" style="flex: 1;"> <template #label>{{ $ts.instance }}</template> <option value="combined">{{ $ts.all }}</option> <option value="local">{{ $ts.local }}</option> <option value="remote">{{ $ts.remote }}</option> </MkSelect> </div> - <div class="inputs" style="display: flex; padding-top: 1.2em;"> - <MkInput v-model="searchUsername" style="margin: 0; flex: 1;" type="text" spellcheck="false" @update:modelValue="$refs.users.reload()"> + <div class="inputs"> + <MkInput v-model="searchUsername" style="flex: 1;" type="text" spellcheck="false" @update:modelValue="$refs.users.reload()"> <template #label>{{ $ts.username }}</template> </MkInput> - <MkInput v-model="searchHost" style="margin: 0; flex: 1;" type="text" spellcheck="false" @update:modelValue="$refs.users.reload()" :disabled="pagination.params().origin === 'local'"> + <MkInput v-model="searchHost" style="flex: 1;" type="text" spellcheck="false" @update:modelValue="$refs.users.reload()" :disabled="pagination.params().origin === 'local'"> <template #label>{{ $ts.host }}</template> </MkInput> </div> @@ -90,6 +90,7 @@ export default defineComponent({ [symbols.PAGE_INFO]: { title: this.$ts.users, icon: 'fas fa-users', + bg: 'var(--bg)', action: { icon: 'fas fa-search', handler: this.searchUser @@ -178,6 +179,19 @@ export default defineComponent({ > .users { margin: var(--margin); + + > .inputs { + display: flex; + margin-bottom: 16px; + + > * { + margin-right: 16px; + + &:last-child { + margin-right: 0; + } + } + } > .users { margin-top: var(--margin); diff --git a/src/client/pages/settings/index.vue b/src/client/pages/settings/index.vue index 25c83a823f..399f4049b6 100644 --- a/src/client/pages/settings/index.vue +++ b/src/client/pages/settings/index.vue @@ -1,11 +1,11 @@ <template> <div class="vvcocwet" :class="{ wide: !narrow }" ref="el"> <div class="nav" v-if="!narrow || page == null"> - <div class="group"> + <div class="group accounts"> <MkAvatar :user="$i" class="avatar"/> <XLink :active="page === 'accounts'" replace to="/settings/accounts"><template #icon><i class="fas fa-users"></i></template>{{ $ts.accounts }}</XLink> </div> - <MkInfo v-if="emailNotConfigured || true" warn>{{ $ts.emailNotConfiguredWarning }} <MkA to="/settings/email" class="_link">{{ $ts.configure }}</MkA></MkInfo> + <MkInfo v-if="emailNotConfigured || true" warn class="info">{{ $ts.emailNotConfiguredWarning }} <MkA to="/settings/email" class="_link">{{ $ts.configure }}</MkA></MkInfo> <div class="group"> <div class="label">{{ $ts.basicSettings }}</div> <XLink :active="page === 'profile'" replace to="/settings/profile"><template #icon><i class="fas fa-user"></i></template>{{ $ts.profile }}</XLink> @@ -207,7 +207,20 @@ export default defineComponent({ > .label { font-size: 0.9em; opacity: 0.7; - margin: 0 0 8px 8px; + margin: 0 0 8px 12px; + } + } + + > .info { + margin: 0 16px; + } + + > .accounts { + > .avatar { + display: block; + width: 50px; + height: 50px; + margin: 0 auto 8px auto; } } } From dcd216daff36a62d283963e10f8ec337a7e4c111 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 3 Oct 2021 00:33:29 +0900 Subject: [PATCH 032/107] :art: --- src/client/pages/instance/index.vue | 6 +-- src/client/pages/page-editor/page-editor.vue | 56 +++++++++++++------- 2 files changed, 39 insertions(+), 23 deletions(-) diff --git a/src/client/pages/instance/index.vue b/src/client/pages/instance/index.vue index 780dd099b6..867031eaf2 100644 --- a/src/client/pages/instance/index.vue +++ b/src/client/pages/instance/index.vue @@ -2,10 +2,8 @@ <div class="hiyeyicy" :class="{ wide: !narrow }" ref="el"> <div class="nav" v-if="!narrow || page == null"> <div class="group"> - <div class="_debobigegoItem"> - <div class="_debobigegoPanel lxpfedzu"> - <img :src="$instance.iconUrl || '/favicon.ico'" alt="" class="icon"/> - </div> + <div class="lxpfedzu"> + <img :src="$instance.iconUrl || '/favicon.ico'" alt="" class="icon"/> </div> <XLink :active="page === 'overview'" replace to="/instance/overview"><template #icon><i class="fas fa-tachometer-alt"></i></template>{{ $ts.overview }}</XLink> </div> diff --git a/src/client/pages/page-editor/page-editor.vue b/src/client/pages/page-editor/page-editor.vue index 74cfd29dae..0daefc6518 100644 --- a/src/client/pages/page-editor/page-editor.vue +++ b/src/client/pages/page-editor/page-editor.vue @@ -8,31 +8,30 @@ <MkButton inline @click="del" class="delete" v-if="pageId && !readonly"><i class="fas fa-trash-alt"></i> {{ $ts.delete }}</MkButton> </div> - <MkContainer :foldable="true" :expanded="true" class="_gap"> - <template #header><i class="fas fa-cog"></i> {{ $ts._pages.pageSetting }}</template> - <div style="padding: 16px;"> - <MkInput v-model="title"> + <div v-if="tab === 'settings'"> + <div style="padding: 16px;" class="_formRoot"> + <MkInput v-model="title" class="_formBlock"> <template #label>{{ $ts._pages.title }}</template> </MkInput> - <MkInput v-model="summary"> + <MkInput v-model="summary" class="_formBlock"> <template #label>{{ $ts._pages.summary }}</template> </MkInput> - <MkInput v-model="name"> + <MkInput v-model="name" class="_formBlock"> <template #prefix>{{ url }}/@{{ author.username }}/pages/</template> <template #label>{{ $ts._pages.url }}</template> </MkInput> - <MkSwitch v-model="alignCenter">{{ $ts._pages.alignCenter }}</MkSwitch> + <MkSwitch v-model="alignCenter" class="_formBlock">{{ $ts._pages.alignCenter }}</MkSwitch> - <MkSelect v-model="font"> + <MkSelect v-model="font" class="_formBlock"> <template #label>{{ $ts._pages.font }}</template> <option value="serif">{{ $ts._pages.fontSerif }}</option> <option value="sans-serif">{{ $ts._pages.fontSansSerif }}</option> </MkSelect> - <MkSwitch v-model="hideTitleWhenPinned">{{ $ts._pages.hideTitleWhenPinned }}</MkSwitch> + <MkSwitch v-model="hideTitleWhenPinned" class="_formBlock">{{ $ts._pages.hideTitleWhenPinned }}</MkSwitch> <div class="eyeCatch"> <MkButton v-if="eyeCatchingImageId == null && !readonly" @click="setEyeCatchingImage"><i class="fas fa-plus"></i> {{ $ts._pages.eyeCatchingImageSet }}</MkButton> @@ -42,19 +41,17 @@ </div> </div> </div> - </MkContainer> + </div> - <MkContainer :foldable="true" :expanded="true" class="_gap"> - <template #header><i class="fas fa-sticky-note"></i> {{ $ts._pages.contents }}</template> + <div v-else-if="tab === 'contents'"> <div style="padding: 16px;"> <XBlocks class="content" v-model="content" :hpml="hpml"/> <MkButton @click="add()" v-if="!readonly"><i class="fas fa-plus"></i></MkButton> </div> - </MkContainer> + </div> - <MkContainer :foldable="true" class="_gap"> - <template #header><i class="fas fa-magic"></i> {{ $ts._pages.variables }}</template> + <div v-else-if="tab === 'variables'"> <div class="qmuvgica"> <XDraggable tag="div" class="variables" v-show="variables.length > 0" v-model="variables" item-key="name" handle=".drag-handle" :group="{ name: 'variables' }" animation="150" swap-threshold="0.5"> <template #item="{element}"> @@ -72,14 +69,13 @@ <MkButton @click="addVariable()" class="add" v-if="!readonly"><i class="fas fa-plus"></i></MkButton> </div> - </MkContainer> + </div> - <MkContainer :foldable="true" :expanded="true" class="_gap"> - <template #header><i class="fas fa-code"></i> {{ $ts.script }}</template> + <div v-else-if="tab === 'script'"> <div> <MkTextarea class="_code" v-model="script"/> </div> - </MkContainer> + </div> </div> </template> @@ -143,8 +139,30 @@ export default defineComponent({ title: title, icon: 'fas fa-pencil-alt', bg: 'var(--bg)', + tabs: [{ + active: this.tab === 'settings', + title: this.$ts._pages.pageSetting, + icon: 'fas fa-cog', + onClick: () => { this.tab = 'settings'; }, + }, { + active: this.tab === 'contents', + title: this.$ts._pages.contents, + icon: 'fas fa-sticky-note', + onClick: () => { this.tab = 'contents'; }, + }, { + active: this.tab === 'variables', + title: this.$ts._pages.variables, + icon: 'fas fa-magic', + onClick: () => { this.tab = 'variables'; }, + }, { + active: this.tab === 'script', + title: this.$ts.script, + icon: 'fas fa-code', + onClick: () => { this.tab = 'script'; }, + }] }; }), + tab: 'settings', author: this.$i, readonly: false, page: null, From 9cab659392a6ff9bacdc95742d4ff423c843b73f Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 3 Oct 2021 02:46:58 +0900 Subject: [PATCH 033/107] :art: --- .../components/channel-follow-button.vue | 2 +- src/client/components/emoji-picker-window.vue | 2 +- src/client/components/emoji-picker.vue | 2 +- src/client/components/follow-button.vue | 2 +- src/client/components/media-caption.vue | 2 +- src/client/components/note.vue | 2 +- src/client/components/post-form.vue | 2 +- src/client/components/ui/button.vue | 17 ++++++++++---- src/client/components/ui/menu.vue | 2 +- src/client/pages/messaging/index.vue | 3 ++- src/client/pages/reversi/game.setting.vue | 2 +- src/client/pages/settings/index.link.vue | 22 +++++++++++++------ src/client/pages/settings/index.vue | 16 +++++--------- src/client/style.scss | 4 ++-- src/client/ui/chat/note.vue | 2 +- src/client/ui/chat/post-form.vue | 2 +- src/client/widgets/aiscript.vue | 2 +- src/client/widgets/memo.vue | 2 +- 18 files changed, 50 insertions(+), 38 deletions(-) diff --git a/src/client/components/channel-follow-button.vue b/src/client/components/channel-follow-button.vue index 6f9405b97f..bd8627f6e8 100644 --- a/src/client/components/channel-follow-button.vue +++ b/src/client/components/channel-follow-button.vue @@ -91,7 +91,7 @@ export default defineComponent({ width: 31px; } - &:focus { + &:focus-visible { &:after { content: ""; pointer-events: none; diff --git a/src/client/components/emoji-picker-window.vue b/src/client/components/emoji-picker-window.vue index 53b6ae6b32..b7b884565b 100644 --- a/src/client/components/emoji-picker-window.vue +++ b/src/client/components/emoji-picker-window.vue @@ -153,7 +153,7 @@ export default defineComponent({ height: var(--eachSize); border-radius: 4px; - &:focus { + &:focus-visible { outline: solid 2px var(--focus); z-index: 1; } diff --git a/src/client/components/emoji-picker.vue b/src/client/components/emoji-picker.vue index d8703202c7..85a12a08e6 100644 --- a/src/client/components/emoji-picker.vue +++ b/src/client/components/emoji-picker.vue @@ -465,7 +465,7 @@ export default defineComponent({ height: var(--eachSize); border-radius: 4px; - &:focus { + &:focus-visible { outline: solid 2px var(--focus); z-index: 1; } diff --git a/src/client/components/follow-button.vue b/src/client/components/follow-button.vue index 5685b86a51..5eba9b1f6b 100644 --- a/src/client/components/follow-button.vue +++ b/src/client/components/follow-button.vue @@ -161,7 +161,7 @@ export default defineComponent({ width: 31px; } - &:focus { + &:focus-visible { &:after { content: ""; pointer-events: none; diff --git a/src/client/components/media-caption.vue b/src/client/components/media-caption.vue index 73eba23025..b35b101d06 100644 --- a/src/client/components/media-caption.vue +++ b/src/client/components/media-caption.vue @@ -206,7 +206,7 @@ export default defineComponent({ min-width: 100%; min-height: 90px; - &:focus { + &:focus-visible { outline: none; } diff --git a/src/client/components/note.vue b/src/client/components/note.vue index 9fa986836d..3b07884cee 100644 --- a/src/client/components/note.vue +++ b/src/client/components/note.vue @@ -888,7 +888,7 @@ export default defineComponent({ //content-visibility: auto; //contain-intrinsic-size: 0 128px; - &:focus { + &:focus-visible { outline: none; &:after { diff --git a/src/client/components/post-form.vue b/src/client/components/post-form.vue index 657053cc93..4ec09e76db 100644 --- a/src/client/components/post-form.vue +++ b/src/client/components/post-form.vue @@ -819,7 +819,7 @@ export default defineComponent({ color: var(--fg); font-family: inherit; - &:focus { + &:focus-visible { outline: none; } diff --git a/src/client/components/ui/button.vue b/src/client/components/ui/button.vue index 6e3cd485c8..53b973fe55 100644 --- a/src/client/components/ui/button.vue +++ b/src/client/components/ui/button.vue @@ -1,7 +1,7 @@ <template> <component class="bghgjjyj _button" :is="link ? 'MkA' : 'button'" - :class="{ inline, primary, danger, full }" + :class="{ inline, primary, danger, rounded, full }" :type="type" @click="$emit('click', $event)" @mousedown="onMousedown" @@ -27,6 +27,11 @@ export default defineComponent({ required: false, default: false }, + rounded: { + type: Boolean, + required: false, + default: false + }, inline: { type: Boolean, required: false, @@ -124,8 +129,8 @@ export default defineComponent({ box-shadow: none; text-decoration: none; background: var(--buttonBg); - border-radius: 999px; - overflow: hidden; + border-radius: 4px; + overflow: clip; box-sizing: border-box; transition: background 0.1s ease; @@ -141,6 +146,10 @@ export default defineComponent({ width: 100%; } + &.rounded { + border-radius: 999px; + } + &.primary { font-weight: bold; color: var(--fgOnAccent) !important; @@ -176,7 +185,7 @@ export default defineComponent({ opacity: 0.7; } - &:focus { + &:focus-visible { outline: solid 2px var(--focus); outline-offset: 2px; } diff --git a/src/client/components/ui/menu.vue b/src/client/components/ui/menu.vue index 26b4b04b11..d1cf4f2db8 100644 --- a/src/client/components/ui/menu.vue +++ b/src/client/components/ui/menu.vue @@ -206,7 +206,7 @@ export default defineComponent({ background: var(--accentDarken); } - &:not(:active):focus { + &:not(:active):focus-visible { box-shadow: 0 0 0 2px var(--focus) inset; } diff --git a/src/client/pages/messaging/index.vue b/src/client/pages/messaging/index.vue index 1e0d4dc64c..84783360bb 100644 --- a/src/client/pages/messaging/index.vue +++ b/src/client/pages/messaging/index.vue @@ -53,7 +53,8 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.messaging, - icon: 'fas fa-comments' + icon: 'fas fa-comments', + bg: 'var(--bg)', }, fetching: true, moreFetching: false, diff --git a/src/client/pages/reversi/game.setting.vue b/src/client/pages/reversi/game.setting.vue index 52757b0032..eb6f24e4ab 100644 --- a/src/client/pages/reversi/game.setting.vue +++ b/src/client/pages/reversi/game.setting.vue @@ -303,7 +303,7 @@ export default defineComponent({ -moz-appearance: none; appearance: none; - &:focus, + &:focus-visible, &:active { border-color: var(--accent); } diff --git a/src/client/pages/settings/index.link.vue b/src/client/pages/settings/index.link.vue index 37d06bc22e..895efffc9c 100644 --- a/src/client/pages/settings/index.link.vue +++ b/src/client/pages/settings/index.link.vue @@ -1,13 +1,17 @@ <template> <div class="qmfkfnzj"> - <a class="main _button" :href="to" target="_blank" v-if="external"> + <a v-if="external" class="main _button" :href="to" target="_blank"> <span class="icon"><slot name="icon"></slot></span> <span class="text"><slot></slot></span> </a> - <MkA class="main _button" :class="{ active }" :to="to" :behavior="behavior" v-else> + <MkA v-else-if="to" class="main _button" :class="{ active }" :to="to" :behavior="behavior"> <span class="icon"><slot name="icon"></slot></span> <span class="text"><slot></slot></span> </MkA> + <button v-else class="main _button button" :class="{ danger }"> + <span class="icon"><slot name="icon"></slot></span> + <span class="text"><slot></slot></span> + </button> </div> </template> @@ -18,12 +22,16 @@ export default defineComponent({ props: { to: { type: String, - required: true + required: false }, active: { type: Boolean, required: false }, + danger: { + type: Boolean, + required: false + }, external: { type: Boolean, required: false @@ -33,10 +41,6 @@ export default defineComponent({ required: false, }, }, - data() { - return { - }; - } }); </script> @@ -61,6 +65,10 @@ export default defineComponent({ background: var(--accentedBg); } + &.danger { + color: var(--error); + } + > .icon { width: 32px; margin-right: 2px; diff --git a/src/client/pages/settings/index.vue b/src/client/pages/settings/index.vue index 399f4049b6..5748d4bf5c 100644 --- a/src/client/pages/settings/index.vue +++ b/src/client/pages/settings/index.vue @@ -33,12 +33,10 @@ <XLink :active="page === 'api'" replace to="/settings/api"><template #icon><i class="fas fa-key"></i></template>API</XLink> <XLink :active="page === 'other'" replace to="/settings/other"><template #icon><i class="fas fa-ellipsis-h"></i></template>{{ $ts.other }}</XLink> </div> - <FormGroup> - <FormButton @click="clear">{{ $ts.clearCache }}</FormButton> - </FormGroup> - <FormGroup> - <FormButton @click="logout" danger>{{ $ts.logout }}</FormButton> - </FormGroup> + <div class="group"> + <XLink @click="clear"><template #icon><i class="fas fa-trash"></i></template>{{ $ts.clearCache }}</XLink> + <XLink @click="logout" danger><template #icon><i class="fas fa-sign-in-alt fa-flip-horizontal"></i></template>{{ $ts.logout }}</XLink> + </div> </div> <div class="main"> <component :is="component" :key="page" @info="onInfo" v-bind="pageProps"/> @@ -50,8 +48,6 @@ import { computed, defineAsyncComponent, defineComponent, nextTick, onMounted, reactive, ref, watch } from 'vue'; import { i18n } from '@client/i18n'; import XLink from './index.link.vue'; -import FormGroup from '@client/components/debobigego/group.vue'; -import FormButton from '@client/components/debobigego/button.vue'; import MkInfo from '@client/components/ui/info.vue'; import { scroll } from '@client/scripts/scroll'; import { signout } from '@client/account'; @@ -63,8 +59,6 @@ import { $i } from '@client/account'; export default defineComponent({ components: { XLink, - FormGroup, - FormButton, MkInfo, }, @@ -220,7 +214,7 @@ export default defineComponent({ display: block; width: 50px; height: 50px; - margin: 0 auto 8px auto; + margin: 8px auto 16px auto; } } } diff --git a/src/client/style.scss b/src/client/style.scss index 5b55ab8caf..8e1d74bc76 100644 --- a/src/client/style.scss +++ b/src/client/style.scss @@ -178,7 +178,7 @@ hr { pointer-events: none; } - &:focus { + &:focus-visible { outline: none; } @@ -509,7 +509,7 @@ hr { padding: 5px; } -.prism-editor__textarea:focus { +.prism-editor__textarea:focus-visible { outline: none; } diff --git a/src/client/ui/chat/note.vue b/src/client/ui/chat/note.vue index 26d44d26aa..c376887b84 100644 --- a/src/client/ui/chat/note.vue +++ b/src/client/ui/chat/note.vue @@ -872,7 +872,7 @@ export default defineComponent({ //content-visibility: auto; //contain-intrinsic-size: 0 128px; - &:focus { + &:focus-visible { outline: none; } diff --git a/src/client/ui/chat/post-form.vue b/src/client/ui/chat/post-form.vue index 0f9a206fab..0cacaf77e7 100644 --- a/src/client/ui/chat/post-form.vue +++ b/src/client/ui/chat/post-form.vue @@ -681,7 +681,7 @@ export default defineComponent({ color: var(--fg); font-family: inherit; - &:focus { + &:focus-visible { outline: none; } diff --git a/src/client/widgets/aiscript.vue b/src/client/widgets/aiscript.vue index 2ea6d09ff5..aaf0a0372e 100644 --- a/src/client/widgets/aiscript.vue +++ b/src/client/widgets/aiscript.vue @@ -125,7 +125,7 @@ export default defineComponent({ box-sizing: border-box; font: inherit; - &:focus { + &:focus-visible { outline: none; } } diff --git a/src/client/widgets/memo.vue b/src/client/widgets/memo.vue index 13ab628f24..3f11e6409e 100644 --- a/src/client/widgets/memo.vue +++ b/src/client/widgets/memo.vue @@ -81,7 +81,7 @@ export default defineComponent({ font: inherit; font-size: 0.9em; - &:focus { + &:focus-visible { outline: none; } } From ef67f3eee6e639f859ddfcf9c0290c187c80948d Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 3 Oct 2021 12:25:59 +0900 Subject: [PATCH 034/107] fix bug --- src/client/pages/settings/index.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/pages/settings/index.vue b/src/client/pages/settings/index.vue index 5748d4bf5c..cb86fdca71 100644 --- a/src/client/pages/settings/index.vue +++ b/src/client/pages/settings/index.vue @@ -5,7 +5,7 @@ <MkAvatar :user="$i" class="avatar"/> <XLink :active="page === 'accounts'" replace to="/settings/accounts"><template #icon><i class="fas fa-users"></i></template>{{ $ts.accounts }}</XLink> </div> - <MkInfo v-if="emailNotConfigured || true" warn class="info">{{ $ts.emailNotConfiguredWarning }} <MkA to="/settings/email" class="_link">{{ $ts.configure }}</MkA></MkInfo> + <MkInfo v-if="emailNotConfigured" warn class="info">{{ $ts.emailNotConfiguredWarning }} <MkA to="/settings/email" class="_link">{{ $ts.configure }}</MkA></MkInfo> <div class="group"> <div class="label">{{ $ts.basicSettings }}</div> <XLink :active="page === 'profile'" replace to="/settings/profile"><template #icon><i class="fas fa-user"></i></template>{{ $ts.profile }}</XLink> From f0b2eaf70deb2c237aaf48d241e28270def513ab Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 3 Oct 2021 12:27:29 +0900 Subject: [PATCH 035/107] fix bug --- src/remote/activitypub/models/image.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/remote/activitypub/models/image.ts b/src/remote/activitypub/models/image.ts index 89259d30fb..d0a96e4313 100644 --- a/src/remote/activitypub/models/image.ts +++ b/src/remote/activitypub/models/image.ts @@ -6,7 +6,7 @@ import { apLogger } from '../logger'; import { DriveFile } from '@/models/entities/drive-file'; import { DriveFiles } from '@/models/index'; import { truncate } from '@/misc/truncate'; -import { DM_MAX_IMAGE_COMMENT_LENGTH } from '@/misc/hard-limits'; +import { DB_MAX_IMAGE_COMMENT_LENGTH } from '@/misc/hard-limits'; const logger = apLogger; From 9a3349569460c672c5622c66317d8fe1612aa426 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 3 Oct 2021 15:39:39 +0900 Subject: [PATCH 036/107] :art: --- src/client/components/debobigego/switch.vue | 1 + src/client/components/ui/menu.vue | 63 +++++++++++---------- src/client/components/ui/popup-menu.vue | 4 +- src/client/components/ui/popup.vue | 5 +- 4 files changed, 37 insertions(+), 36 deletions(-) diff --git a/src/client/components/debobigego/switch.vue b/src/client/components/debobigego/switch.vue index abc380374d..9a69e18302 100644 --- a/src/client/components/debobigego/switch.vue +++ b/src/client/components/debobigego/switch.vue @@ -96,6 +96,7 @@ export default defineComponent({ width: 16px; height: 16px; background-color: #fff; + pointer-events: none; } } diff --git a/src/client/components/ui/menu.vue b/src/client/components/ui/menu.vue index d1cf4f2db8..a55dccdd71 100644 --- a/src/client/components/ui/menu.vue +++ b/src/client/components/ui/menu.vue @@ -1,5 +1,5 @@ <template> -<div class="rrevdjwt" :class="{ left: align === 'left', pointer: point === 'top' }" +<div class="rrevdjwt" :class="{ center: align === 'center' }" ref="items" @contextmenu.self="e => e.preventDefault()" v-hotkey="keymap" @@ -59,10 +59,6 @@ export default defineComponent({ type: String, requried: false }, - point: { - type: String, - requried: false - }, }, emits: ['close'], data() { @@ -145,26 +141,11 @@ export default defineComponent({ <style lang="scss" scoped> .rrevdjwt { padding: 8px 0; + min-width: 200px; - &.pointer { - &:before { - --size: 8px; - content: ''; - display: block; - position: absolute; - top: calc(0px - (var(--size) * 2)); - left: 0; - right: 0; - width: 0; - margin: auto; - border: solid var(--size) transparent; - border-bottom-color: var(--popup); - } - } - - &.left { + &.center { > .item { - text-align: left; + text-align: center; } } @@ -177,33 +158,55 @@ export default defineComponent({ white-space: nowrap; font-size: 0.9em; line-height: 20px; - text-align: center; + text-align: left; overflow: hidden; text-overflow: ellipsis; + &:before { + content: ""; + display: block; + position: absolute; + top: 0; + left: 0; + right: 0; + margin: auto; + width: calc(100% - 16px); + height: 100%; + border-radius: 6px; + } + &.danger { color: #ff2a2a; &:hover { color: #fff; - background: #ff4242; + + &:before { + background: #ff4242; + } } &:active { color: #fff; - background: #d42e2e; + + &:before { + background: #d42e2e; + } } } &:hover { - color: var(--fgOnAccent); - background: var(--accent); + color: var(--accent); text-decoration: none; + + &:before { + background: var(--accentedBg); + } } &:active { - color: var(--fgOnAccent); - background: var(--accentDarken); + //color: var(--fgOnAccent); + //background: var(--accentDarken); } &:not(:active):focus-visible { diff --git a/src/client/components/ui/popup-menu.vue b/src/client/components/ui/popup-menu.vue index 3590426172..23f7c89f3b 100644 --- a/src/client/components/ui/popup-menu.vue +++ b/src/client/components/ui/popup-menu.vue @@ -1,6 +1,6 @@ <template> -<MkPopup ref="popup" :src="src" @closed="$emit('closed')" #default="{point}"> - <MkMenu :items="items" :align="align" :point="point" @close="$refs.popup.close()" class="_popup _shadow"/> +<MkPopup ref="popup" :src="src" @closed="$emit('closed')"> + <MkMenu :items="items" :align="align" @close="$refs.popup.close()" class="_popup _shadow"/> </MkPopup> </template> diff --git a/src/client/components/ui/popup.vue b/src/client/components/ui/popup.vue index 8497eedecb..0fb1780cc5 100644 --- a/src/client/components/ui/popup.vue +++ b/src/client/components/ui/popup.vue @@ -1,7 +1,7 @@ <template> <transition :name="$store.state.animation ? 'popup-menu' : ''" appear @after-leave="onClosed" @enter="$emit('opening')" @after-enter="childRendered"> <div v-show="manualShowing != null ? manualShowing : showing" class="ccczpooj" :class="{ front, fixed, top: position === 'top' }" ref="content" :style="{ pointerEvents: (manualShowing != null ? manualShowing : showing) ? 'auto' : 'none', '--transformOrigin': transformOrigin }"> - <slot :point="point"></slot> + <slot></slot> </div> </transition> </template> @@ -52,7 +52,6 @@ export default defineComponent({ fixed: false, transformOrigin: 'center', contentClicking: false, - point: null, }; }, @@ -136,10 +135,8 @@ export default defineComponent({ } if (top > rect.top + (this.fixed ? 0 : window.pageYOffset)) { - this.point = 'top'; this.transformOrigin = 'center top'; } else { - this.point = null; this.transformOrigin = 'center'; } From 66369b4b1d6d24f639057bef68a0ada4d2e70ce0 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 3 Oct 2021 23:51:54 +0900 Subject: [PATCH 037/107] :art: --- src/client/pages/announcements.vue | 3 +- src/client/pages/settings/import-export.vue | 55 +++++++++++---------- webpack.config.ts | 27 +++++++--- 3 files changed, 51 insertions(+), 34 deletions(-) diff --git a/src/client/pages/announcements.vue b/src/client/pages/announcements.vue index a7ccb03588..327fa9b1f6 100644 --- a/src/client/pages/announcements.vue +++ b/src/client/pages/announcements.vue @@ -32,7 +32,8 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.announcements, - icon: 'fas fa-broadcast-tower' + icon: 'fas fa-broadcast-tower', + bg: 'var(--bg)', }, pagination: { endpoint: 'announcements', diff --git a/src/client/pages/settings/import-export.vue b/src/client/pages/settings/import-export.vue index 5f1ed43340..64def6fdf0 100644 --- a/src/client/pages/settings/import-export.vue +++ b/src/client/pages/settings/import-export.vue @@ -1,45 +1,42 @@ <template> -<FormBase> - <FormGroup> +<div> + <FormSection> <template #label>{{ $ts._exportOrImport.allNotes }}</template> - <FormButton @click="doExport('notes')"><i class="fas fa-download"></i> {{ $ts.export }}</FormButton> - </FormGroup> - <FormGroup> + <MkButton :class="$style.button" inline @click="doExport('notes')"><i class="fas fa-download"></i> {{ $ts.export }}</MkButton> + </FormSection> + <FormSection> <template #label>{{ $ts._exportOrImport.followingList }}</template> - <FormButton @click="doExport('following')"><i class="fas fa-download"></i> {{ $ts.export }}</FormButton> - <FormButton @click="doImport('following', $event)"><i class="fas fa-upload"></i> {{ $ts.import }}</FormButton> - </FormGroup> - <FormGroup> + <MkButton :class="$style.button" inline @click="doExport('following')"><i class="fas fa-download"></i> {{ $ts.export }}</MkButton> + <MkButton :class="$style.button" inline @click="doImport('following', $event)"><i class="fas fa-upload"></i> {{ $ts.import }}</MkButton> + </FormSection> + <FormSection> <template #label>{{ $ts._exportOrImport.userLists }}</template> - <FormButton @click="doExport('user-lists')"><i class="fas fa-download"></i> {{ $ts.export }}</FormButton> - <FormButton @click="doImport('user-lists', $event)"><i class="fas fa-upload"></i> {{ $ts.import }}</FormButton> - </FormGroup> - <FormGroup> + <MkButton :class="$style.button" inline @click="doExport('user-lists')"><i class="fas fa-download"></i> {{ $ts.export }}</MkButton> + <MkButton :class="$style.button" inline @click="doImport('user-lists', $event)"><i class="fas fa-upload"></i> {{ $ts.import }}</MkButton> + </FormSection> + <FormSection> <template #label>{{ $ts._exportOrImport.muteList }}</template> - <FormButton @click="doExport('mute')"><i class="fas fa-download"></i> {{ $ts.export }}</FormButton> - </FormGroup> - <FormGroup> + <MkButton :class="$style.button" inline @click="doExport('mute')"><i class="fas fa-download"></i> {{ $ts.export }}</MkButton> + </FormSection> + <FormSection> <template #label>{{ $ts._exportOrImport.blockingList }}</template> - <FormButton @click="doExport('blocking')"><i class="fas fa-download"></i> {{ $ts.export }}</FormButton> - </FormGroup> -</FormBase> + <MkButton :class="$style.button" inline @click="doExport('blocking')"><i class="fas fa-download"></i> {{ $ts.export }}</MkButton> + </FormSection> +</div> </template> <script lang="ts"> import { defineComponent } from 'vue'; -import FormSelect from '@client/components/form/select.vue'; -import FormButton from '@client/components/debobigego/button.vue'; -import FormBase from '@client/components/debobigego/base.vue'; -import FormGroup from '@client/components/debobigego/group.vue'; +import MkButton from '@client/components/ui/button.vue'; +import FormSection from '@client/components/form/section.vue'; import * as os from '@client/os'; import { selectFile } from '@client/scripts/select-file'; import * as symbols from '@client/symbols'; export default defineComponent({ components: { - FormBase, - FormGroup, - FormButton, + FormSection, + MkButton, }, emits: ['info'], @@ -103,3 +100,9 @@ export default defineComponent({ } }); </script> + +<style module> +.button { + margin-right: 16px; +} +</style> diff --git a/webpack.config.ts b/webpack.config.ts index e9f3aa6e47..74426df2e6 100644 --- a/webpack.config.ts +++ b/webpack.config.ts @@ -95,14 +95,27 @@ module.exports = { }] }, { test: /\.css$/, - use: [{ - loader: 'vue-style-loader' + oneOf: [{ + resourceQuery: /module/, + use: [{ + loader: 'vue-style-loader' + }, { + loader: 'css-loader', + options: { + modules: true, + esModule: false, // TODO: trueにすると壊れる。Vue3移行の折にはtrueにできるかもしれない + } + }, postcss] }, { - loader: 'css-loader', - options: { - esModule: false, // TODO: trueにすると壊れる。Vue3移行の折にはtrueにできるかもしれない - } - }, postcss] + use: [{ + loader: 'vue-style-loader' + }, { + loader: 'css-loader', + options: { + esModule: false, // TODO: trueにすると壊れる。Vue3移行の折にはtrueにできるかもしれない + } + }, postcss] + }] }, { test: /\.svg$/, use: [ From 8d05ef3058682661084ad120e6402de802fbe77b Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Tue, 5 Oct 2021 20:28:07 +0900 Subject: [PATCH 038/107] fix(api): fix file type regex --- src/server/api/endpoints/admin/drive/files.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/api/endpoints/admin/drive/files.ts b/src/server/api/endpoints/admin/drive/files.ts index c0788c8e02..fe1c799805 100644 --- a/src/server/api/endpoints/admin/drive/files.ts +++ b/src/server/api/endpoints/admin/drive/files.ts @@ -25,7 +25,7 @@ export const meta = { }, type: { - validator: $.optional.nullable.str.match(/^[a-zA-Z\/\-*]+$/) + validator: $.optional.nullable.str.match(/^[a-zA-Z0-9\/\-*]+$/) }, origin: { From dd17065129c66f5093add1192fcdf853123f7583 Mon Sep 17 00:00:00 2001 From: tamaina <tamaina@hotmail.co.jp> Date: Thu, 7 Oct 2021 14:26:16 +0900 Subject: [PATCH 039/107] Fix idb --- src/client/scripts/idb-proxy.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/client/scripts/idb-proxy.ts b/src/client/scripts/idb-proxy.ts index 21c4dcff65..5f76ae30bb 100644 --- a/src/client/scripts/idb-proxy.ts +++ b/src/client/scripts/idb-proxy.ts @@ -4,7 +4,6 @@ import { get as iget, set as iset, del as idel, - createStore, } from 'idb-keyval'; const fallbackName = (key: string) => `idbfallback::${key}`; @@ -13,9 +12,9 @@ let idbAvailable = typeof window !== 'undefined' ? !!window.indexedDB : true; if (idbAvailable) { try { - await createStore('keyval-store', 'keyval'); + await iset('idb-test', 'test'); } catch (e) { - console.error('idb open error', e); + console.error('idb error', e); idbAvailable = false; } } From 67875e2afa65fb0be477099def0e3d9cdb08ea22 Mon Sep 17 00:00:00 2001 From: tamaina <tamaina@hotmail.co.jp> Date: Thu, 7 Oct 2021 14:28:22 +0900 Subject: [PATCH 040/107] Revert "Fix idb" This reverts commit dd17065129c66f5093add1192fcdf853123f7583. --- src/client/scripts/idb-proxy.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/client/scripts/idb-proxy.ts b/src/client/scripts/idb-proxy.ts index 5f76ae30bb..21c4dcff65 100644 --- a/src/client/scripts/idb-proxy.ts +++ b/src/client/scripts/idb-proxy.ts @@ -4,6 +4,7 @@ import { get as iget, set as iset, del as idel, + createStore, } from 'idb-keyval'; const fallbackName = (key: string) => `idbfallback::${key}`; @@ -12,9 +13,9 @@ let idbAvailable = typeof window !== 'undefined' ? !!window.indexedDB : true; if (idbAvailable) { try { - await iset('idb-test', 'test'); + await createStore('keyval-store', 'keyval'); } catch (e) { - console.error('idb error', e); + console.error('idb open error', e); idbAvailable = false; } } From e568c3888f5de08b7986c53d7fb7df2d9dbc57b7 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 8 Oct 2021 13:34:57 +0900 Subject: [PATCH 041/107] update dependencies --- package.json | 50 +++---- yarn.lock | 403 +++++++++++++++++++++++++++------------------------ 2 files changed, 241 insertions(+), 212 deletions(-) diff --git a/package.json b/package.json index 9f37bea172..5f8900d7af 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "@sinonjs/fake-timers": "7.1.2", "@syuilo/aiscript": "0.11.1", "@types/bcryptjs": "2.4.2", - "@types/bull": "3.15.4", + "@types/bull": "3.15.5", "@types/cbor": "6.0.0", "@types/dateformat": "3.0.1", "@types/escape-regexp": "0.0.0", @@ -63,17 +63,17 @@ "@types/koa-bodyparser": "4.3.3", "@types/koa-cors": "0.0.2", "@types/koa-favicon": "2.0.21", - "@types/koa-logger": "3.1.1", + "@types/koa-logger": "3.1.2", "@types/koa-mount": "4.0.1", "@types/koa-send": "4.1.3", "@types/koa-views": "7.0.0", "@types/koa__cors": "3.0.3", "@types/koa__multer": "2.0.3", "@types/koa__router": "8.0.8", - "@types/markdown-it": "12.2.1", + "@types/markdown-it": "12.2.3", "@types/matter-js": "0.17.5", "@types/mocha": "8.2.3", - "@types/node": "16.9.6", + "@types/node": "16.10.3", "@types/node-fetch": "2.5.12", "@types/nodemailer": "6.4.4", "@types/nprogress": "0.2.0", @@ -102,28 +102,28 @@ "@types/webpack": "5.28.0", "@types/webpack-stream": "3.2.12", "@types/websocket": "1.0.4", - "@types/ws": "7.4.7", - "@typescript-eslint/parser": "4.31.2", + "@types/ws": "8.2.0", + "@typescript-eslint/parser": "4.33.0", "@vue/compiler-sfc": "3.2.19", "abort-controller": "3.0.0", "apexcharts": "3.28.3", "autobind-decorator": "2.4.0", "autosize": "4.0.4", "autwh": "0.1.0", - "aws-sdk": "2.992.0", + "aws-sdk": "2.1003.0", "bcryptjs": "2.4.3", "blurhash": "1.1.4", "broadcast-channel": "4.2.0", "bull": "3.29.2", - "cacheable-lookup": "6.0.1", + "cacheable-lookup": "6.0.2", "cafy": "15.2.1", - "cbor": "8.0.0", + "cbor": "8.0.2", "chalk": "4.1.2", "chart.js": "2.9.4", "cli-highlight": "2.1.11", - "commander": "8.1.0", + "commander": "8.2.0", "compare-versions": "3.6.0", - "concurrently": "6.2.1", + "concurrently": "6.3.0", "content-disposition": "0.5.3", "crc-32": "1.2.0", "css-loader": "6.3.0", @@ -131,18 +131,18 @@ "dateformat": "4.5.1", "escape-regexp": "0.0.1", "eslint": "7.32.0", - "eslint-plugin-vue": "7.18.0", + "eslint-plugin-vue": "7.19.1", "eventemitter3": "4.0.7", "feed": "4.2.2", "file-type": "16.5.3", "fluent-ffmpeg": "2.1.2", - "glob": "7.1.7", + "glob": "7.2.0", "got": "11.8.2", "gulp": "4.0.2", "gulp-cssnano": "2.1.3", "gulp-rename": "2.0.0", "gulp-replace": "1.1.3", - "gulp-terser": "2.0.1", + "gulp-terser": "2.1.0", "gulp-tslint": "8.1.4", "hpagent": "0.1.2", "http-signature": "1.3.5", @@ -157,7 +157,7 @@ "jsonld": "5.2.0", "jsrsasign": "8.0.20", "katex": "0.13.18", - "koa": "2.13.1", + "koa": "2.13.3", "koa-bodyparser": "4.3.0", "koa-favicon": "2.1.0", "koa-json-body": "5.3.0", @@ -170,19 +170,19 @@ "markdown-it": "12.2.0", "markdown-it-anchor": "7.1.0", "matter-js": "0.17.1", - "mfm-js": "0.19.0", + "mfm-js": "0.20.0", "misskey-js": "0.0.6", "mocha": "8.4.0", "ms": "2.1.3", "multer": "1.4.3", "nested-property": "4.0.0", "node-fetch": "2.6.1", - "nodemailer": "6.6.3", + "nodemailer": "6.6.5", "os-utils": "0.0.14", "parse5": "6.0.1", "pg": "8.7.1", "portscanner": "2.2.0", - "postcss": "8.3.7", + "postcss": "8.3.9", "postcss-loader": "6.1.1", "prismjs": "1.25.0", "private-ip": "2.2.1", @@ -213,7 +213,7 @@ "style-loader": "3.3.0", "summaly": "2.4.1", "syslog-pro": "1.0.0", - "systeminformation": "5.9.3", + "systeminformation": "5.9.4", "syuilo-password-strength": "0.0.1", "textarea-caret": "3.1.0", "three": "0.117.1", @@ -222,12 +222,12 @@ "tmp": "0.2.1", "ts-loader": "9.2.6", "ts-node": "10.2.1", - "tsc-alias": "1.3.9", + "tsc-alias": "1.3.10", "tsconfig-paths": "3.11.0", "tslint": "6.1.3", "tslint-sonarts": "1.9.0", "twemoji-parser": "13.1.0", - "typeorm": "0.2.37", + "typeorm": "0.2.38", "typescript": "4.4.3", "ulid": "2.3.0", "uuid": "8.3.2", @@ -241,17 +241,17 @@ "vue-svg-loader": "0.17.0-beta.2", "vuedraggable": "4.0.1", "web-push": "3.4.5", - "webpack": "5.54.0", - "webpack-cli": "4.8.0", + "webpack": "5.58.0", + "webpack-cli": "4.9.0", "websocket": "1.0.34", - "ws": "8.2.2", + "ws": "8.2.3", "xev": "2.0.1" }, "devDependencies": { "@redocly/openapi-core": "1.0.0-beta.54", "@types/fluent-ffmpeg": "2.1.17", "cross-env": "7.0.3", - "cypress": "8.4.1", + "cypress": "8.5.0", "start-server-and-test": "1.14.0" } } diff --git a/yarn.lock b/yarn.lock index fb6a3bbda0..71a2002c1b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -447,10 +447,10 @@ "@types/connect" "*" "@types/node" "*" -"@types/bull@3.15.4": - version "3.15.4" - resolved "https://registry.yarnpkg.com/@types/bull/-/bull-3.15.4.tgz#71390e83b6aa41a9106535240ad226c5239615f1" - integrity sha512-vsw4kqsI/SLocfcgTUY2b8POTBUw9AwmTaehvSVnEAbcgJHsPheFMP8WKDFDk2I+aNTLRQX5iP21dPwz3CGLVA== +"@types/bull@3.15.5": + version "3.15.5" + resolved "https://registry.yarnpkg.com/@types/bull/-/bull-3.15.5.tgz#a4459c127c5b10fb847531579a2cd5db35751366" + integrity sha512-XgJQWJ03jyKMfdoL8IAIoHIo7JkkL74kcxuujTONkSJswm0giIJ9kuVgDNHS0OvD+OiPNcFmbBl0H3scj2+A8A== dependencies: "@types/ioredis" "*" @@ -734,10 +734,10 @@ dependencies: "@types/koa" "*" -"@types/koa-logger@3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@types/koa-logger/-/koa-logger-3.1.1.tgz#bf759ba2f25b7562ffa30b09616bb1c1e5070fea" - integrity sha512-wp2HaskkPugfwgXgNnc+idnReuJZSTTYQbkcxXjsMhp1kTc342PxDzTL9FXDgBfEvgt9NX1CCGjkwPKX2dlEKQ== +"@types/koa-logger@3.1.2": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@types/koa-logger/-/koa-logger-3.1.2.tgz#91e890f405ddb0626bc385767e4cc0cd7226d1a8" + integrity sha512-sioTA1xlKYiIgryANWPRHBkG3XGbWftw9slWADUPC+qvPIY/yRLSrhvX7zkJwMrntub5dPO0GuAoyGGf0yitfQ== dependencies: "@types/koa" "*" @@ -816,14 +816,13 @@ resolved "https://registry.yarnpkg.com/@types/linkify-it/-/linkify-it-2.1.0.tgz#ea3dd64c4805597311790b61e872cbd1ed2cd806" integrity sha512-Q7DYAOi9O/+cLLhdaSvKdaumWyHbm7HAk/bFwwyTuU0arR5yyCeW5GOoqt4tJTpDRxhpx9Q8kQL6vMpuw9hDSw== -"@types/markdown-it@12.2.1": - version "12.2.1" - resolved "https://registry.yarnpkg.com/@types/markdown-it/-/markdown-it-12.2.1.tgz#ca36e1edce6f15a770f3e99e68622d1d2e2f0c65" - integrity sha512-iij+ilRX/vxtUPCREjn74xzHo/RorHJDwOsJ6X+TgKw7zSvazhVXnDfwlTnyLOMdiVUjtRYU4CrcUZ7Aci4PmQ== +"@types/markdown-it@12.2.3": + version "12.2.3" + resolved "https://registry.yarnpkg.com/@types/markdown-it/-/markdown-it-12.2.3.tgz#0d6f6e5e413f8daaa26522904597be3d6cd93b51" + integrity sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ== dependencies: "@types/linkify-it" "*" "@types/mdurl" "*" - highlight.js "^10.7.2" "@types/matter-js@0.17.5": version "0.17.5" @@ -863,10 +862,10 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-16.6.2.tgz#331b7b9f8621c638284787c5559423822fdffc50" integrity sha512-LSw8TZt12ZudbpHc6EkIyDM3nHVWKYrAvGy6EAJfNfjusbwnThqjqxUKKRwuV3iWYeW/LYMzNgaq3MaLffQ2xA== -"@types/node@16.9.6": - version "16.9.6" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.9.6.tgz#040a64d7faf9e5d9e940357125f0963012e66f04" - integrity sha512-YHUZhBOMTM3mjFkXVcK+WwAcYmyhe1wL4lfqNtzI0b3qAy7yuSetnM7QJazgE5PFmgVTNGiLOgRFfJMqW7XpSQ== +"@types/node@16.10.3": + version "16.10.3" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.10.3.tgz#7a8f2838603ea314d1d22bb3171d899e15c57bd5" + integrity sha512-ho3Ruq+fFnBrZhUYI46n/bV2GjwzSkwuT4dTf0GkuNFmnb8nq4ny2z9JEVemFi6bdEJanHLlYfy9c6FN9B9McQ== "@types/node@^14.11.8", "@types/node@^14.14.31", "@types/node@^14.14.41": version "14.17.9" @@ -880,11 +879,6 @@ dependencies: "@types/node" "*" -"@types/normalize-package-data@^2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" - integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA== - "@types/nprogress@0.2.0": version "0.2.0" resolved "https://registry.yarnpkg.com/@types/nprogress/-/nprogress-0.2.0.tgz#86c593682d4199212a0509cc3c4d562bbbd6e45f" @@ -1176,10 +1170,10 @@ dependencies: "@types/node" "*" -"@types/ws@7.4.7": - version "7.4.7" - resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.4.7.tgz#f7c390a36f7a0679aa69de2d501319f4f8d9b702" - integrity sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww== +"@types/ws@8.2.0": + version "8.2.0" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.2.0.tgz#75faefbe2328f3b833cb8dc640658328990d04f3" + integrity sha512-cyeefcUCgJlEk+hk2h3N+MqKKsPViQgF5boi9TTHSK+PoR9KWBb/C5ccPcDyAqgsbAYHTwulch725DV84+pSpg== dependencies: "@types/node" "*" @@ -1195,48 +1189,48 @@ resolved "https://registry.yarnpkg.com/@types/zen-observable/-/zen-observable-0.8.2.tgz#808c9fa7e4517274ed555fa158f2de4b4f468e71" integrity sha512-HrCIVMLjE1MOozVoD86622S7aunluLb2PJdPfb3nYiEtohm8mIB/vyv0Fd37AdeMFrTUQXEunw78YloMA3Qilg== -"@typescript-eslint/parser@4.31.2": - version "4.31.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.31.2.tgz#54aa75986e3302d91eff2bbbaa6ecfa8084e9c34" - integrity sha512-EcdO0E7M/sv23S/rLvenHkb58l3XhuSZzKf6DBvLgHqOYdL6YFMYVtreGFWirxaU2mS1GYDby3Lyxco7X5+Vjw== +"@typescript-eslint/parser@4.33.0": + version "4.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.33.0.tgz#dfe797570d9694e560528d18eecad86c8c744899" + integrity sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA== dependencies: - "@typescript-eslint/scope-manager" "4.31.2" - "@typescript-eslint/types" "4.31.2" - "@typescript-eslint/typescript-estree" "4.31.2" + "@typescript-eslint/scope-manager" "4.33.0" + "@typescript-eslint/types" "4.33.0" + "@typescript-eslint/typescript-estree" "4.33.0" debug "^4.3.1" -"@typescript-eslint/scope-manager@4.31.2": - version "4.31.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.31.2.tgz#1d528cb3ed3bcd88019c20a57c18b897b073923a" - integrity sha512-2JGwudpFoR/3Czq6mPpE8zBPYdHWFGL6lUNIGolbKQeSNv4EAiHaR5GVDQaLA0FwgcdcMtRk+SBJbFGL7+La5w== +"@typescript-eslint/scope-manager@4.33.0": + version "4.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.33.0.tgz#d38e49280d983e8772e29121cf8c6e9221f280a3" + integrity sha512-5IfJHpgTsTZuONKbODctL4kKuQje/bzBRkwHE8UOZ4f89Zeddg+EGZs8PD8NcN4LdM3ygHWYB3ukPAYjvl/qbQ== dependencies: - "@typescript-eslint/types" "4.31.2" - "@typescript-eslint/visitor-keys" "4.31.2" + "@typescript-eslint/types" "4.33.0" + "@typescript-eslint/visitor-keys" "4.33.0" -"@typescript-eslint/types@4.31.2": - version "4.31.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.31.2.tgz#2aea7177d6d744521a168ed4668eddbd912dfadf" - integrity sha512-kWiTTBCTKEdBGrZKwFvOlGNcAsKGJSBc8xLvSjSppFO88AqGxGNYtF36EuEYG6XZ9vT0xX8RNiHbQUKglbSi1w== +"@typescript-eslint/types@4.33.0": + version "4.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.33.0.tgz#a1e59036a3b53ae8430ceebf2a919dc7f9af6d72" + integrity sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ== -"@typescript-eslint/typescript-estree@4.31.2": - version "4.31.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.31.2.tgz#abfd50594d8056b37e7428df3b2d185ef2d0060c" - integrity sha512-ieBq8U9at6PvaC7/Z6oe8D3czeW5d//Fo1xkF/s9394VR0bg/UaMYPdARiWyKX+lLEjY3w/FNZJxitMsiWv+wA== +"@typescript-eslint/typescript-estree@4.33.0": + version "4.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz#0dfb51c2908f68c5c08d82aefeaf166a17c24609" + integrity sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA== dependencies: - "@typescript-eslint/types" "4.31.2" - "@typescript-eslint/visitor-keys" "4.31.2" + "@typescript-eslint/types" "4.33.0" + "@typescript-eslint/visitor-keys" "4.33.0" debug "^4.3.1" globby "^11.0.3" is-glob "^4.0.1" semver "^7.3.5" tsutils "^3.21.0" -"@typescript-eslint/visitor-keys@4.31.2": - version "4.31.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.31.2.tgz#7d5b4a4705db7fe59ecffb273c1d082760f635cc" - integrity sha512-PrBId7EQq2Nibns7dd/ch6S6/M4/iwLM9McbgeEbCXfxdwRUNxJ4UNreJ6Gh3fI2GNKNrWnQxKL7oCPmngKBug== +"@typescript-eslint/visitor-keys@4.33.0": + version "4.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz#2a22f77a41604289b7a186586e9ec48ca92ef1dd" + integrity sha512-uqi/2aSz9g2ftcHWf8uLPJA70rUv6yuMW5Bohw+bwcuzaxQIHaKFZCKGoGXIrc9vkTJ3+0txM73K0Hq3d5wgIg== dependencies: - "@typescript-eslint/types" "4.31.2" + "@typescript-eslint/types" "4.33.0" eslint-visitor-keys "^2.0.0" "@ungap/promise-all-settled@1.1.2": @@ -1576,22 +1570,22 @@ "@webassemblyjs/ast" "1.11.1" "@xtuc/long" "4.2.2" -"@webpack-cli/configtest@^1.0.4": - version "1.0.4" - resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-1.0.4.tgz#f03ce6311c0883a83d04569e2c03c6238316d2aa" - integrity sha512-cs3XLy+UcxiP6bj0A6u7MLLuwdXJ1c3Dtc0RkKg+wiI1g/Ti1om8+/2hc2A2B60NbBNAbMgyBMHvyymWm/j4wQ== +"@webpack-cli/configtest@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-1.1.0.tgz#8342bef0badfb7dfd3b576f2574ab80c725be043" + integrity sha512-ttOkEkoalEHa7RaFYpM0ErK1xc4twg3Am9hfHhL7MVqlHebnkYd2wuI/ZqTDj0cVzZho6PdinY0phFZV3O0Mzg== -"@webpack-cli/info@^1.3.0": - version "1.3.0" - resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-1.3.0.tgz#9d78a31101a960997a4acd41ffd9b9300627fe2b" - integrity sha512-ASiVB3t9LOKHs5DyVUcxpraBXDOKubYu/ihHhU+t1UPpxsivg6Od2E2qU4gJCekfEddzRBzHhzA/Acyw/mlK/w== +"@webpack-cli/info@^1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-1.4.0.tgz#b9179c3227ab09cbbb149aa733475fcf99430223" + integrity sha512-F6b+Man0rwE4n0409FyAJHStYA5OIZERxmnUfLVwv0mc0V1wLad3V7jqRlMkgKBeAq07jUvglacNaa6g9lOpuw== dependencies: envinfo "^7.7.3" -"@webpack-cli/serve@^1.5.2": - version "1.5.2" - resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.5.2.tgz#ea584b637ff63c5a477f6f21604b5a205b72c9ec" - integrity sha512-vgJ5OLWadI8aKjDlOH3rb+dYyPd2GTZuQC/Tihjct6F9GpXGZINo3Y/IVuZVTM1eDQB+/AOsjPUWH/WySDaXvw== +"@webpack-cli/serve@^1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.6.0.tgz#2c275aa05c895eccebbfc34cfb223c6e8bd591a2" + integrity sha512-ZkVeqEmRpBV2GHvjjUZqEai2PpUbuq8Bqd//vEYsp63J8WyexI8ppCqVS3Zs0QADf6aWuPdU+0XsPI647PVlQA== "@xtuc/ieee754@^1.2.0": version "1.2.0" @@ -2094,10 +2088,10 @@ autwh@0.1.0: dependencies: oauth "0.9.15" -aws-sdk@2.992.0: - version "2.992.0" - resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.992.0.tgz#3d8f663727cf3ea62a794a7499ae001fe6256abb" - integrity sha512-FP/AOu1nxfaPJ6to05eHriBUzvPiNapEwy96sm5GNOL8/T38k9//H6UhxLJ/46CzxFMH/Mo/WFp0qwpS39ev5A== +aws-sdk@2.1003.0: + version "2.1003.0" + resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1003.0.tgz#f4ca218f466c524a90370b5604a3ad4cda4c0e08" + integrity sha512-UEZveI1m7+/YsomU2tVxLMmlo5g3sr3ue+QMJ2UwbrvHZ+O9hr9vVia1lD+L8fYTQenOff95NFc02h3pDE3iDA== dependencies: buffer "4.9.2" events "1.1.1" @@ -2503,10 +2497,10 @@ cache-content-type@^1.0.0: mime-types "^2.1.18" ylru "^1.2.0" -cacheable-lookup@6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-6.0.1.tgz#f32ab50c3212302d9f49aa094c8a7593c162af7c" - integrity sha512-vaccXt7hUfa5UzrtbdzHTWnL6V6ir39QtLuvGZys32j4HboAeiWVhrcdAm8ecTz1rLubxPhec2n22BBb5/dgVA== +cacheable-lookup@6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-6.0.2.tgz#8df03d6239c91bb9f6394700d7ba4a100abbad67" + integrity sha512-9RJkUl1k/A1dFhaRfrEUdISvvou0WKx8LboMO0j1BpsqgAuolwZgwaEtn0dmFMk5HQxpFtHF1bHCnIQMywUpvw== cacheable-lookup@^5.0.3: version "5.0.3" @@ -2619,12 +2613,12 @@ cbor@*: "@cto.af/textdecoder" "^0.0.0" nofilter "^2.0.3" -cbor@8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/cbor/-/cbor-8.0.0.tgz#51657d26a99a6a1866f8c3258e948576eb17d709" - integrity sha512-nMmaLWbj7+bC6MsApKRIig8h+yjgNLhPLXaCelq5+C7mpWsHgIcseZSdvgexSY5uE1Q3m2uPvIDZwSdxdo7qig== +cbor@8.0.2: + version "8.0.2" + resolved "https://registry.yarnpkg.com/cbor/-/cbor-8.0.2.tgz#d0f5088423437efcc160e9304bd0576f45d06abb" + integrity sha512-H5WTjQYgyHQI0VrCmbyQBOPy1353MjmUi/r3DbPib4U13vuyqm7es9Mfpe8G58bN/mCdRlJWkiCrPl1uM1wAlg== dependencies: - nofilter "^3.0.2" + nofilter "^3.0.3" chalk@4.0.0: version "4.0.0" @@ -3068,6 +3062,11 @@ colorette@^1.2.0, colorette@^1.2.1, colorette@^1.2.2: resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94" integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w== +colorette@^2.0.14: + version "2.0.15" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.15.tgz#8e634aa0429b110d24be82eac4d42f5ea65ab2d5" + integrity sha512-lIFQhufWaVvwi4wOlX9Gx5b0Nmw3XAZ8HzHNH9dfxhe+JaKNTmX6QLk4o7UHyI+tUY8ClvyfaHUm5bf61O3psA== + colormin@^1.0.5: version "1.1.2" resolved "https://registry.yarnpkg.com/colormin/-/colormin-1.1.2.tgz#ea2f7420a72b96881a38aae59ec124a6f7298133" @@ -3094,10 +3093,10 @@ combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: dependencies: delayed-stream "~1.0.0" -commander@8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-8.1.0.tgz#db36e3e66edf24ff591d639862c6ab2c52664362" - integrity sha512-mf45ldcuHSYShkplHHGKWb4TrmwQadxOn7v4WuhDJy0ZVoY5JFajaRDKD0PNe5qXzBX0rhovjTnP6Kz9LETcuA== +commander@8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-8.2.0.tgz#37fe2bde301d87d47a53adeff8b5915db1381ca8" + integrity sha512-LLKxDvHeL91/8MIyTAD5BFMNtoIwztGPMiM/7Bl8rIPmHCZXRxmSWr91h57dpOpnQ6jIUqEWdXE/uBYMfiVZDA== commander@^2.12.1, commander@^2.19.0, commander@^2.20.0: version "2.20.3" @@ -3165,15 +3164,14 @@ concat-stream@^1.5.2, concat-stream@^1.6.0: readable-stream "^2.2.2" typedarray "^0.0.6" -concurrently@6.2.1: - version "6.2.1" - resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-6.2.1.tgz#d880fc1d77559084732fa514092a3d5109a0d5bf" - integrity sha512-emgwhH+ezkuYKSHZQ+AkgEpoUZZlbpPVYCVv7YZx0r+T7fny1H03r2nYRebpi2DudHR4n1Rgbo2YTxKOxVJ4+g== +concurrently@6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-6.3.0.tgz#63128cb4a6ed54d3c0ed8528728590a5fe54582a" + integrity sha512-k4k1jQGHHKsfbqzkUszVf29qECBrkvBKkcPJEUDTyVR7tZd1G/JOfnst4g1sYbFvJ4UjHZisj1aWQR8yLKpGPw== dependencies: chalk "^4.1.0" date-fns "^2.16.1" lodash "^4.17.21" - read-pkg "^5.2.0" rxjs "^6.6.3" spawn-command "^0.0.2-1" supports-color "^8.1.0" @@ -3558,10 +3556,10 @@ csstype@^2.6.8: resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.13.tgz#a6893015b90e84dd6e85d0e3b442a1e84f2dbe0f" integrity sha512-ul26pfSQTZW8dcOnD2iiJssfXw0gdNVX9IJDH/X3K5DGPfj+fUYe3kB+swUY6BF3oZDxaID3AJt+9/ojSAE05A== -cypress@8.4.1: - version "8.4.1" - resolved "https://registry.yarnpkg.com/cypress/-/cypress-8.4.1.tgz#8b5898bf49359cadc28f02ba05d51f63b8e3a717" - integrity sha512-itJXq0Vx3sXCUrDyBi2IUrkxVu/gTTp1VhjB5tzGgkeCR8Ae+/T8WV63rsZ7fS8Tpq7LPPXiyoM/sEdOX7cR6A== +cypress@8.5.0: + version "8.5.0" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-8.5.0.tgz#5712ca170913f8344bf167301205c4217c1eb9bd" + integrity sha512-MMkXIS+Ro2KETn4gAlG3tIc/7FiljuuCZP0zpd9QsRG6MZSyZW/l1J3D4iQM6WHsVxuX4rFChn5jPFlC2tNSvQ== dependencies: "@cypress/request" "^2.88.6" "@cypress/xvfb" "^1.2.4" @@ -3597,6 +3595,7 @@ cypress@8.4.1: minimist "^1.2.5" ospath "^1.2.2" pretty-bytes "^5.6.0" + proxy-from-env "1.0.0" ramda "~0.27.1" request-progress "^3.0.0" supports-color "^8.1.1" @@ -4268,10 +4267,10 @@ escodegen@^2.0.0: optionalDependencies: source-map "~0.6.1" -eslint-plugin-vue@7.18.0: - version "7.18.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-7.18.0.tgz#02a452142330c7f27c242db21a1b9e25238540f6" - integrity sha512-ceDXlXYMMPMSXw7tdKUR42w9jlzthJGJ3Kvm3YrZ0zuQfvAySNxe8sm6VHuksBW0+060GzYXhHJG6IHVOfF83Q== +eslint-plugin-vue@7.19.1: + version "7.19.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-7.19.1.tgz#435fb2ce712842a9530b28eacb883680e8eaa4f3" + integrity sha512-e2pD7nW2sTY04ThH+66BgToNwC4n6dqfNhKE+ypdJFtZgn3Zn+nP8ZEIFPG0PGqCKQ3qxy8dJk1bzUsuQd3ANA== dependencies: eslint-utils "^2.1.0" natural-compare "^1.4.0" @@ -5101,10 +5100,10 @@ glob@7.1.6, glob@^7.1.1, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: once "^1.3.0" path-is-absolute "^1.0.0" -glob@7.1.7: - version "7.1.7" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" - integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== +glob@7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" + integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" @@ -5261,13 +5260,13 @@ gulp-replace@1.1.3: replacestream "^4.0.3" yargs-parser ">=5.0.0-security.0" -gulp-terser@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/gulp-terser/-/gulp-terser-2.0.1.tgz#5f8f4fc54588b79519243809cc8eef4936286d0d" - integrity sha512-XCrnCXP8ovNpgLK9McJIXlgm0j3W2TsiWu7K9y3m+Sn5XZgUzi6U8MPHtS3NdLMic9poCj695N0ARJ2B6atypw== +gulp-terser@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/gulp-terser/-/gulp-terser-2.1.0.tgz#149b693a1adbde922807b60b844bb7351dafbde1" + integrity sha512-lQ3+JUdHDVISAlUIUSZ/G9Dz/rBQHxOiYDQ70IVWFQeh4b33TC1MCIU+K18w07PS3rq/CVc34aQO4SUbdaNMPQ== dependencies: plugin-error "^1.0.1" - terser "5.4.0" + terser "^5.9.0" through2 "^4.0.2" vinyl-sourcemaps-apply "^0.2.1" @@ -5408,11 +5407,6 @@ highlight.js@^10.7.1: resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.2.tgz#89319b861edc66c48854ed1e6da21ea89f847360" integrity sha512-oFLl873u4usRM9K63j4ME9u3etNF0PLiJhSQ8rdfuL51Wn3zkD6drf9ZW0dOzjnZI22YYG24z30JcmfCZjMgYg== -highlight.js@^10.7.2: - version "10.7.3" - resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531" - integrity sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A== - homedir-polyfill@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz#743298cef4e5af3e194161fbadcc2151d3a058e8" @@ -6535,6 +6529,14 @@ koa-convert@^1.2.0: co "^4.6.0" koa-compose "^3.0.0" +koa-convert@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/koa-convert/-/koa-convert-2.0.0.tgz#86a0c44d81d40551bae22fee6709904573eea4f5" + integrity sha512-asOvN6bFlSnxewce2e/DK3p4tltyfC4VM7ZwuTuepI7dEQVcvpyFuBcEARu1+Hxg8DIwytce2n7jrZtRlPrARA== + dependencies: + co "^4.6.0" + koa-compose "^4.1.0" + koa-favicon@2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/koa-favicon/-/koa-favicon-2.1.0.tgz#c430cc594614fb494adcb5ee1196a2f7f53ea442" @@ -6627,6 +6629,35 @@ koa@2.13.1: type-is "^1.6.16" vary "^1.1.2" +koa@2.13.3: + version "2.13.3" + resolved "https://registry.yarnpkg.com/koa/-/koa-2.13.3.tgz#a62641ba753ec54bee2c6da1a4f294c5fac35407" + integrity sha512-XhXIoR+ylAwqG3HhXwnMPQAM/4xfywz52OvxZNmxmTWGGHsvmBv4NSIhURha6yMuvEex1WdtplUTHnxnKpQiGw== + dependencies: + accepts "^1.3.5" + cache-content-type "^1.0.0" + content-disposition "~0.5.2" + content-type "^1.0.4" + cookies "~0.8.0" + debug "^4.3.2" + delegates "^1.0.0" + depd "^2.0.0" + destroy "^1.0.4" + encodeurl "^1.0.2" + escape-html "^1.0.3" + fresh "~0.5.2" + http-assert "^1.3.0" + http-errors "^1.6.3" + is-generator-function "^1.0.7" + koa-compose "^4.1.0" + koa-convert "^2.0.0" + on-finished "^2.3.0" + only "~0.0.2" + parseurl "^1.3.2" + statuses "^1.5.0" + type-is "^1.6.16" + vary "^1.1.2" + ky-universal@^0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/ky-universal/-/ky-universal-0.8.2.tgz#edc398d54cf495d7d6830aa1ab69559a3cc7f824" @@ -7116,10 +7147,10 @@ methods@^1.1.2: resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= -mfm-js@0.19.0: - version "0.19.0" - resolved "https://registry.yarnpkg.com/mfm-js/-/mfm-js-0.19.0.tgz#77419fb54cb1bcfa49d931467903d6dc6c1815e1" - integrity sha512-06uVz9v+ltO9Obt1vWpwfJZEyZloihYuoxud+Rt6R9PW4aUk2/zua8qUeMBDNQkKZ/+IRtRSmmgn2A2GiDSMEg== +mfm-js@0.20.0: + version "0.20.0" + resolved "https://registry.yarnpkg.com/mfm-js/-/mfm-js-0.20.0.tgz#3afdcd7959461fd825aa8af9b9e8a57cdbddc290" + integrity sha512-1+3tV3nWUKQNh/ztX3wXu5iLBtdsg6q3wUhl+XyOhc2H3sQdG+sih/w2c0nR9TIawjN+Z1/pvgGzxMJHfmKQmA== dependencies: twemoji-parser "13.1.x" @@ -7402,11 +7433,6 @@ nano-time@1.0.0: dependencies: big-integer "^1.6.16" -nanocolors@^0.1.5: - version "0.1.6" - resolved "https://registry.yarnpkg.com/nanocolors/-/nanocolors-0.1.6.tgz#bc2350d3edfdbfadd7ac018c855ae7c13905a6ad" - integrity sha512-2pvTw6vYRaBLGir2xR7MxaJtyWkrn+C53EpW8yPotG+pdAwBvt0Xwk4VJ6VHLY0aLthVZPvDfm9TdZvrvAm5UQ== - nanoid@3.1.20, nanoid@^3.1.20: version "3.1.20" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.20.tgz#badc263c6b1dcf14b71efaa85f6ab4c1d6cfc788" @@ -7417,10 +7443,10 @@ nanoid@^3.1.23: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.23.tgz#f744086ce7c2bc47ee0a8472574d5c78e4183a81" integrity sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw== -nanoid@^3.1.25: - version "3.1.25" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.25.tgz#09ca32747c0e543f0e1814b7d3793477f9c8e152" - integrity sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q== +nanoid@^3.1.28: + version "3.1.29" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.29.tgz#214fb2d7a33e1a5bef4757b779dfaeb6a4e5aeb4" + integrity sha512-dW2pUSGZ8ZnCFIlBIA31SV8huOGCHb6OwzVCc7A69rb/a+SgPBwfmLvK5TKQ3INPbRkcI8a/Owo0XbiTNH19wg== nanomatch@^1.2.9: version "1.2.13" @@ -7539,10 +7565,10 @@ node-releases@^1.1.70, node-releases@^1.1.71: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.71.tgz#cb1334b179896b1c89ecfdd4b725fb7bbdfc7dbb" integrity sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg== -nodemailer@6.6.3: - version "6.6.3" - resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.6.3.tgz#31fb53dd4d8ae16fc088a65cb9ffa8d928a69b48" - integrity sha512-faZFufgTMrphYoDjvyVpbpJcYzwyFnbAMmQtj1lVBYAUSm3SOy2fIdd9+Mr4UxPosBa0JRw9bJoIwQn+nswiew== +nodemailer@6.6.5: + version "6.6.5" + resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.6.5.tgz#f9f6953cee5cfe82cbea152eeddacf7a0442049a" + integrity sha512-C/v856DBijUzHcHIgGpQoTrfsH3suKIRAGliIzCstatM2cAa+MYX3LuyCrABiO/cdJTxgBBHXxV1ztiqUwst5A== nofilter@^2.0.3: version "2.0.3" @@ -7551,7 +7577,7 @@ nofilter@^2.0.3: dependencies: "@cto.af/textdecoder" "^0.0.0" -nofilter@^3.0.2: +nofilter@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/nofilter/-/nofilter-3.0.3.tgz#3ff3b142efdccb403434ccae4a0c2c835cb9b522" integrity sha512-TN/MCrQmXQk5DyUJ8TGUq1Il8rv4fTsjddLmMopV006QP8DMkglmGgYfQKD5620vXLRXfr8iGI6ZZ4/ZWld2cQ== @@ -7571,7 +7597,7 @@ nopt@^5.0.0: dependencies: abbrev "1" -normalize-package-data@^2.3.2, normalize-package-data@^2.5.0: +normalize-package-data@^2.3.2: version "2.5.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== @@ -8196,6 +8222,11 @@ pgpass@1.x: dependencies: split "^1.0.0" +picocolors@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-0.2.1.tgz#570670f793646851d1ba135996962abad587859f" + integrity sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA== + picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.0.7, picomatch@^2.2.1: version "2.2.2" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" @@ -8755,13 +8786,13 @@ postcss-zindex@^2.0.1: postcss "^5.0.4" uniqs "^2.0.0" -postcss@8.3.7: - version "8.3.7" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.3.7.tgz#ec88563588c8da8e58e7226f7633b51ae221eeda" - integrity sha512-9SaY7nnyQ63/WittqZYAvkkYPyKxchMKH71UDzeTmWuLSvxTRpeEeABZAzlCi55cuGcoFyoV/amX2BdsafQidQ== +postcss@8.3.9: + version "8.3.9" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.3.9.tgz#98754caa06c4ee9eb59cc48bd073bb6bd3437c31" + integrity sha512-f/ZFyAKh9Dnqytx5X62jgjhhzttjZS7hMsohcI7HEI5tjELX/HxCy3EFhsRxyzGvrzFF+82XPvCS8T9TFleVJw== dependencies: - nanocolors "^0.1.5" - nanoid "^3.1.25" + nanoid "^3.1.28" + picocolors "^0.2.1" source-map-js "^0.6.2" postcss@^5.0.10, postcss@^5.0.11, postcss@^5.0.12, postcss@^5.0.13, postcss@^5.0.14, postcss@^5.0.16, postcss@^5.0.2, postcss@^5.0.4, postcss@^5.0.5, postcss@^5.0.8, postcss@^5.2.16: @@ -8951,6 +8982,11 @@ proto-list@~1.2.1: resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk= +proxy-from-env@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.0.0.tgz#33c50398f70ea7eb96d21f7b817630a55791c7ee" + integrity sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4= + ps-tree@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/ps-tree/-/ps-tree-1.2.0.tgz#5e7425b89508736cdd4f2224d028f7bb3f722ebd" @@ -9248,16 +9284,6 @@ read-pkg@^1.0.0: normalize-package-data "^2.3.2" path-type "^1.0.0" -read-pkg@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" - integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== - dependencies: - "@types/normalize-package-data" "^2.4.0" - normalize-package-data "^2.5.0" - parse-json "^5.0.0" - type-fest "^0.6.0" - readable-stream@1.1.x: version "1.1.14" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" @@ -10046,6 +10072,14 @@ source-map-support@~0.5.19: buffer-from "^1.0.0" source-map "^0.6.0" +source-map-support@~0.5.20: + version "0.5.20" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.20.tgz#12166089f8f5e5e8c56926b377633392dd2cb6c9" + integrity sha512-n1lZZ8Ve4ksRqizaBQgxXDgKwttHDhyfQjA6YZZn8+AroHbsIz+JjwxQDxbp+7y5OYCI8t1Yk7etjD9CRd2hIw== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + source-map-url@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" @@ -10586,10 +10620,10 @@ syslog-pro@1.0.0: dependencies: moment "^2.22.2" -systeminformation@5.9.3: - version "5.9.3" - resolved "https://registry.yarnpkg.com/systeminformation/-/systeminformation-5.9.3.tgz#d247c3fc27dc51787af28f0b4a0d82f9bce7f88b" - integrity sha512-FmifqCPDU5uJZeORt1jCiATBTHwpX7luDzeFo8lojYbEiJk6oR3mtAZBOayCo3iEmgSILzmbcO855OXPHCeU+g== +systeminformation@5.9.4: + version "5.9.4" + resolved "https://registry.yarnpkg.com/systeminformation/-/systeminformation-5.9.4.tgz#1f0e29e0aa376dec8f69cc517eeefc5cdcda411a" + integrity sha512-FOsiTn0CyJZoj9kIhla11ndsMzbbwwuriul81wpqIBt9IpbxHZ6P/oZCphIFgJrwqjTnme0Qp1HDzIkUD9Xr/g== syuilo-password-strength@0.0.1: version "0.0.1" @@ -10703,15 +10737,6 @@ terser-webpack-plugin@^5.1.3: source-map "^0.6.1" terser "^5.7.0" -terser@5.4.0: - version "5.4.0" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.4.0.tgz#9815c0839072d5c894e22c6fc508fbe9f5e7d7e8" - integrity sha512-3dZunFLbCJis9TAF2VnX+VrQLctRUmt1p3W2kCsJuZE4ZgWqh//+1MZ62EanewrqKoUf4zIaDGZAvml4UDc0OQ== - dependencies: - commander "^2.20.0" - source-map "~0.7.2" - source-map-support "~0.5.19" - terser@^5.5.1: version "5.5.1" resolved "https://registry.yarnpkg.com/terser/-/terser-5.5.1.tgz#540caa25139d6f496fdea056e414284886fb2289" @@ -10730,6 +10755,15 @@ terser@^5.7.0: source-map "~0.7.2" source-map-support "~0.5.19" +terser@^5.9.0: + version "5.9.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.9.0.tgz#47d6e629a522963240f2b55fcaa3c99083d2c351" + integrity sha512-h5hxa23sCdpzcye/7b8YqbE5OwKca/ni0RQz1uRX3tGh8haaGHqcuSqbGRybuAKNdntZ0mDgFNXPJ48xQ2RXKQ== + dependencies: + commander "^2.20.0" + source-map "~0.7.2" + source-map-support "~0.5.20" + text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" @@ -10956,10 +10990,10 @@ ts-node@10.2.1: make-error "^1.1.1" yn "3.1.1" -tsc-alias@1.3.9: - version "1.3.9" - resolved "https://registry.yarnpkg.com/tsc-alias/-/tsc-alias-1.3.9.tgz#0c9d1dd571c0a97af8159d20e7cd4ce6aaab1799" - integrity sha512-PXNsdsuygWpvQrt41D7CBndJyZ+8Juf2BDvQ1OJNqq0QsMR4i+A4rfniY/NVwb70gW4nMDJBvNxxvzLJjakXtQ== +tsc-alias@1.3.10: + version "1.3.10" + resolved "https://registry.yarnpkg.com/tsc-alias/-/tsc-alias-1.3.10.tgz#6ccf81c644092387ab9be3a3a75549a95eeffd80" + integrity sha512-7SF56qiV7Oh/bON+XjF/uAzEFqbmwCuEIHQyoTyVJAK80WnxaIyhO9TBwD/x8InIMU8lnvExQBOrgKkRPsHH+w== dependencies: "@jfonx/console-utils" "^1.0.3" "@jfonx/file-utils" "^3.0.1" @@ -11095,11 +11129,6 @@ type-fest@^0.21.3: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== -type-fest@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" - integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== - type-is@^1.6.14, type-is@^1.6.16, type-is@^1.6.4: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" @@ -11135,10 +11164,10 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typeorm@0.2.37: - version "0.2.37" - resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.2.37.tgz#1a5e59216077640694d27c04c99ed3f968d15dc8" - integrity sha512-7rkW0yCgFC24I5T0f3S/twmLSuccPh1SQmxET/oDWn2sSDVzbyWdnItSdKy27CdJGTlKHYtUVeOcMYw5LRsXVw== +typeorm@0.2.38: + version "0.2.38" + resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.2.38.tgz#2af08079919f6ab04cd17017f9faa2c8d5cd566f" + integrity sha512-M6Y3KQcAREQcphOVJciywf4mv6+A0I/SeR+lWNjKsjnQ+a3XcMwGYMGL0Jonsx3H0Cqlf/3yYqVki1jIXSK/xg== dependencies: "@sqltools/formatter" "^1.2.2" app-root-path "^3.0.0" @@ -11610,16 +11639,16 @@ webidl-conversions@^6.1.0: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514" integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== -webpack-cli@4.8.0: - version "4.8.0" - resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.8.0.tgz#5fc3c8b9401d3c8a43e2afceacfa8261962338d1" - integrity sha512-+iBSWsX16uVna5aAYN6/wjhJy1q/GKk4KjKvfg90/6hykCTSgozbfz5iRgDTSJt/LgSbYxdBX3KBHeobIs+ZEw== +webpack-cli@4.9.0: + version "4.9.0" + resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.9.0.tgz#dc43e6e0f80dd52e89cbf73d5294bcd7ad6eb343" + integrity sha512-n/jZZBMzVEl4PYIBs+auy2WI0WTQ74EnJDiyD98O2JZY6IVIHJNitkYp/uTXOviIOMfgzrNvC9foKv/8o8KSZw== dependencies: "@discoveryjs/json-ext" "^0.5.0" - "@webpack-cli/configtest" "^1.0.4" - "@webpack-cli/info" "^1.3.0" - "@webpack-cli/serve" "^1.5.2" - colorette "^1.2.1" + "@webpack-cli/configtest" "^1.1.0" + "@webpack-cli/info" "^1.4.0" + "@webpack-cli/serve" "^1.6.0" + colorette "^2.0.14" commander "^7.0.0" execa "^5.0.0" fastest-levenshtein "^1.0.12" @@ -11650,10 +11679,10 @@ webpack-sources@^3.2.0: resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.0.tgz#b16973bcf844ebcdb3afde32eda1c04d0b90f89d" integrity sha512-fahN08Et7P9trej8xz/Z7eRu8ltyiygEo/hnRi9KqBUs80KeDcnf96ZJo++ewWd84fEf3xSX9bp4ZS9hbw0OBw== -webpack@5.54.0: - version "5.54.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.54.0.tgz#629f0cd14c7a4340af758a3c7cef25c50670ae4d" - integrity sha512-MAVKJMsIUotOQKzFOmN8ZkmMlj7BOyjDU6t1lomW9dWOme5WTStzGa3HMLdV1KYD1AiFETGsznL4LMSvj4tukw== +webpack@5.58.0: + version "5.58.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.58.0.tgz#9ec621cf8534f23c25e779e7c35dfde1211d5ccb" + integrity sha512-xc2k5MLbR1iah24Z5xUm1nBh1PZXEdUnrX6YkTSOScq/VWbl5JCLREXJzGYqEAUbIO8tZI+Dzv82lGtnuUnVCQ== dependencies: "@types/eslint-scope" "^3.7.0" "@types/estree" "^0.0.50" @@ -11852,10 +11881,10 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= -ws@8.2.2: - version "8.2.2" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.2.2.tgz#ca684330c6dd6076a737250ed81ac1606cb0a63e" - integrity sha512-Q6B6H2oc8QY3llc3cB8kVmQ6pnJWVQbP7Q5algTcIxx7YEpc0oU4NBVHlztA7Ekzfhw2r0rPducMUiCGWKQRzw== +ws@8.2.3: + version "8.2.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.2.3.tgz#63a56456db1b04367d0b721a0b80cae6d8becbba" + integrity sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA== ws@^7.4.6: version "7.5.3" From b875cc994968bb334dfb9d83707e56ab3971a0d1 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 8 Oct 2021 13:37:02 +0900 Subject: [PATCH 042/107] =?UTF-8?q?feat:=20=E3=82=A2=E3=82=AB=E3=82=A6?= =?UTF-8?q?=E3=83=B3=E3=83=88=E4=BD=9C=E6=88=90=E3=81=AB=E3=83=A1=E3=83=BC?= =?UTF-8?q?=E3=83=AB=E3=82=A2=E3=83=89=E3=83=AC=E3=82=B9=E5=BF=85=E9=A0=88?= =?UTF-8?q?=E3=81=AB=E3=81=99=E3=82=8B=E3=82=AA=E3=83=97=E3=82=B7=E3=83=A7?= =?UTF-8?q?=E3=83=B3=20(#7856)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: アカウント作成にメールアドレス必須にするオプション * ui * fix bug * fix bug * fix bug * :art: --- CHANGELOG.md | 1 + locales/ja-JP.yml | 6 ++ ...1633068642000-email-required-for-signup.ts | 14 ++++ migration/1633071909016-user-pending.ts | 16 +++++ src/client/components/signup-dialog.vue | 6 +- src/client/components/signup.vue | 71 ++++++++++++++----- src/client/pages/instance/security.vue | 5 ++ src/client/pages/signup-complete.vue | 50 +++++++++++++ src/client/router.ts | 1 + src/db/postgre.ts | 2 + src/models/entities/meta.ts | 5 ++ src/models/entities/user-pending.ts | 32 +++++++++ src/models/index.ts | 2 + src/server/api/common/signup.ts | 26 ++++--- .../api/endpoints/admin/accounts/create.ts | 5 +- src/server/api/endpoints/admin/update-meta.ts | 8 +++ .../api/endpoints/email-address/available.ts | 37 ++++++++++ src/server/api/endpoints/meta.ts | 6 ++ src/server/api/index.ts | 2 + src/server/api/private/signup-pending.ts | 35 +++++++++ src/server/api/private/signup.ts | 62 +++++++++++++--- src/server/nodeinfo.ts | 1 + 22 files changed, 356 insertions(+), 37 deletions(-) create mode 100644 migration/1633068642000-email-required-for-signup.ts create mode 100644 migration/1633071909016-user-pending.ts create mode 100644 src/client/pages/signup-complete.vue create mode 100644 src/models/entities/user-pending.ts create mode 100644 src/server/api/endpoints/email-address/available.ts create mode 100644 src/server/api/private/signup-pending.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b6c357c4f..bd526fd694 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ ## 12.x.x (unreleased) ### Improvements +- アカウント登録にメールアドレスの設定を必須にするオプション - クライアント: アニメーションを減らす設定をメニューのアニメーションにも適用するように - クライアント: MFM関数構文のサジェストを実装 - ActivityPub: HTML -> MFMの変換を強化 diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 06b10ba278..0be87cf2c5 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -791,6 +791,12 @@ resolved: "解決済み" unresolved: "未解決" itsOn: "オンになっています" itsOff: "オフになっています" +emailRequiredForSignup: "アカウント登録にメールアドレスを必須にする" + +_signup: + almostThere: "ほとんど完了です" + emailAddressInfo: "あなたが使っているメールアドレスを入力してください。" + emailSent: "入力されたメールアドレス({email})宛に確認のメールが送信されました。メールに記載されたリンクにアクセスすると、アカウントの作成が完了します。" _accountDelete: accountDelete: "アカウントの削除" diff --git a/migration/1633068642000-email-required-for-signup.ts b/migration/1633068642000-email-required-for-signup.ts new file mode 100644 index 0000000000..ab7be7a0d1 --- /dev/null +++ b/migration/1633068642000-email-required-for-signup.ts @@ -0,0 +1,14 @@ +import {MigrationInterface, QueryRunner} from "typeorm"; + +export class emailRequiredForSignup1633068642000 implements MigrationInterface { + name = 'emailRequiredForSignup1633068642000' + + public async up(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(`ALTER TABLE "meta" ADD "emailRequiredForSignup" boolean NOT NULL DEFAULT false`); + } + + public async down(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "emailRequiredForSignup"`); + } + +} diff --git a/migration/1633071909016-user-pending.ts b/migration/1633071909016-user-pending.ts new file mode 100644 index 0000000000..28b556888a --- /dev/null +++ b/migration/1633071909016-user-pending.ts @@ -0,0 +1,16 @@ +import {MigrationInterface, QueryRunner} from "typeorm"; + +export class userPending1633071909016 implements MigrationInterface { + name = 'userPending1633071909016' + + public async up(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(`CREATE TABLE "user_pending" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "code" character varying(128) NOT NULL, "username" character varying(128) NOT NULL, "email" character varying(128) NOT NULL, "password" character varying(128) NOT NULL, CONSTRAINT "PK_d4c84e013c98ec02d19b8fbbafa" PRIMARY KEY ("id"))`); + await queryRunner.query(`CREATE UNIQUE INDEX "IDX_4e5c4c99175638ec0761714ab0" ON "user_pending" ("code") `); + } + + public async down(queryRunner: QueryRunner): Promise<void> { + await queryRunner.query(`DROP INDEX "IDX_4e5c4c99175638ec0761714ab0"`); + await queryRunner.query(`DROP TABLE "user_pending"`); + } + +} diff --git a/src/client/components/signup-dialog.vue b/src/client/components/signup-dialog.vue index df1a525055..9741e8c73b 100644 --- a/src/client/components/signup-dialog.vue +++ b/src/client/components/signup-dialog.vue @@ -9,7 +9,7 @@ <div class="_monolithic_"> <div class="_section"> - <XSignup :auto-set="autoSet" @signup="onSignup"/> + <XSignup :auto-set="autoSet" @signup="onSignup" @signupEmailPending="onSignupEmailPending"/> </div> </div> </XModalWindow> @@ -40,6 +40,10 @@ export default defineComponent({ onSignup(res) { this.$emit('done', res); this.$refs.dialog.close(); + }, + + onSignupEmailPending() { + this.$refs.dialog.close(); } } }); diff --git a/src/client/components/signup.vue b/src/client/components/signup.vue index f555c1df6d..b420bca5a3 100644 --- a/src/client/components/signup.vue +++ b/src/client/components/signup.vue @@ -10,13 +10,23 @@ <template #prefix>@</template> <template #suffix>@{{ host }}</template> <template #caption> - <span v-if="usernameState == 'wait'" style="color:#999"><i class="fas fa-spinner fa-pulse fa-fw"></i> {{ $ts.checking }}</span> - <span v-if="usernameState == 'ok'" style="color: var(--success)"><i class="fas fa-check fa-fw"></i> {{ $ts.available }}</span> - <span v-if="usernameState == 'unavailable'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.unavailable }}</span> - <span v-if="usernameState == 'error'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.error }}</span> - <span v-if="usernameState == 'invalid-format'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.usernameInvalidFormat }}</span> - <span v-if="usernameState == 'min-range'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.tooShort }}</span> - <span v-if="usernameState == 'max-range'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.tooLong }}</span> + <span v-if="usernameState === 'wait'" style="color:#999"><i class="fas fa-spinner fa-pulse fa-fw"></i> {{ $ts.checking }}</span> + <span v-else-if="usernameState === 'ok'" style="color: var(--success)"><i class="fas fa-check fa-fw"></i> {{ $ts.available }}</span> + <span v-else-if="usernameState === 'unavailable'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.unavailable }}</span> + <span v-else-if="usernameState === 'error'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.error }}</span> + <span v-else-if="usernameState === 'invalid-format'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.usernameInvalidFormat }}</span> + <span v-else-if="usernameState === 'min-range'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.tooShort }}</span> + <span v-else-if="usernameState === 'max-range'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.tooLong }}</span> + </template> + </MkInput> + <MkInput v-if="meta.emailRequiredForSignup" class="_formBlock" v-model="email" type="email" :autocomplete="Math.random()" spellcheck="false" required @update:modelValue="onChangeEmail" data-cy-signup-email> + <template #label>{{ $ts.emailAddress }} <div class="_button _help" v-tooltip:dialog="$ts._signup.emailAddressInfo"><i class="far fa-question-circle"></i></div></template> + <template #prefix><i class="fas fa-envelope"></i></template> + <template #caption> + <span v-if="emailState === 'wait'" style="color:#999"><i class="fas fa-spinner fa-pulse fa-fw"></i> {{ $ts.checking }}</span> + <span v-else-if="emailState === 'ok'" style="color: var(--success)"><i class="fas fa-check fa-fw"></i> {{ $ts.available }}</span> + <span v-else-if="emailState === 'unavailable'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.unavailable }}</span> + <span v-else-if="emailState === 'error'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.error }}</span> </template> </MkInput> <MkInput class="_formBlock" v-model="password" type="password" :autocomplete="Math.random()" required @update:modelValue="onChangePassword" data-cy-signup-password> @@ -87,8 +97,10 @@ export default defineComponent({ password: '', retypedPassword: '', invitationCode: '', + email: '', url, usernameState: null, + emailState: null, passwordStrength: '', passwordRetypeState: null, submitting: false, @@ -148,6 +160,23 @@ export default defineComponent({ }); }, + onChangeEmail() { + if (this.email == '') { + this.emailState = null; + return; + } + + this.emailState = 'wait'; + + os.api('email-address/available', { + emailAddress: this.email + }).then(result => { + this.emailState = result.available ? 'ok' : 'unavailable'; + }).catch(err => { + this.emailState = 'error'; + }); + }, + onChangePassword() { if (this.password == '') { this.passwordStrength = ''; @@ -174,20 +203,30 @@ export default defineComponent({ os.api('signup', { username: this.username, password: this.password, + emailAddress: this.email, invitationCode: this.invitationCode, 'hcaptcha-response': this.hCaptchaResponse, 'g-recaptcha-response': this.reCaptchaResponse, }).then(() => { - return os.api('signin', { - username: this.username, - password: this.password - }).then(res => { - this.$emit('signup', res); + if (this.meta.emailRequiredForSignup) { + os.dialog({ + type: 'success', + title: this.$ts._signup.almostThere, + text: this.$t('_signup.emailSent', { email: this.email }), + }); + this.$emit('signupEmailPending'); + } else { + os.api('signin', { + username: this.username, + password: this.password + }).then(res => { + this.$emit('signup', res); - if (this.autoSet) { - return login(res.i); - } - }); + if (this.autoSet) { + login(res.i); + } + }); + } }).catch(() => { this.submitting = false; this.$refs.hcaptcha?.reset?.(); diff --git a/src/client/pages/instance/security.vue b/src/client/pages/instance/security.vue index 53f923643a..2b525261ae 100644 --- a/src/client/pages/instance/security.vue +++ b/src/client/pages/instance/security.vue @@ -10,6 +10,8 @@ <FormSwitch v-model="enableRegistration">{{ $ts.enableRegistration }}</FormSwitch> + <FormSwitch v-model="emailRequiredForSignup">{{ $ts.emailRequiredForSignup }}</FormSwitch> + <FormButton @click="save" primary><i class="fas fa-save"></i> {{ $ts.save }}</FormButton> </FormSuspense> </FormBase> @@ -50,6 +52,7 @@ export default defineComponent({ enableHcaptcha: false, enableRecaptcha: false, enableRegistration: false, + emailRequiredForSignup: false, } }, @@ -63,11 +66,13 @@ export default defineComponent({ this.enableHcaptcha = meta.enableHcaptcha; this.enableRecaptcha = meta.enableRecaptcha; this.enableRegistration = !meta.disableRegistration; + this.emailRequiredForSignup = meta.emailRequiredForSignup; }, save() { os.apiWithDialog('admin/update-meta', { disableRegistration: !this.enableRegistration, + emailRequiredForSignup: this.emailRequiredForSignup, }).then(() => { fetchInstance(); }); diff --git a/src/client/pages/signup-complete.vue b/src/client/pages/signup-complete.vue new file mode 100644 index 0000000000..dada92031a --- /dev/null +++ b/src/client/pages/signup-complete.vue @@ -0,0 +1,50 @@ +<template> +<div> + {{ $ts.processing }} +</div> +</template> + +<script lang="ts"> +import { defineComponent } from 'vue'; +import * as os from '@client/os'; +import * as symbols from '@client/symbols'; +import { login } from '@client/account'; + +export default defineComponent({ + components: { + + }, + + props: { + code: { + type: String, + required: true + } + }, + + data() { + return { + [symbols.PAGE_INFO]: { + title: this.$ts.signup, + icon: 'fas fa-user' + }, + } + }, + + mounted() { + os.apiWithDialog('signup-pending', { + code: this.code, + }).then(res => { + login(res.i, '/'); + }); + }, + + methods: { + + } +}); +</script> + +<style lang="scss" scoped> + +</style> diff --git a/src/client/router.ts b/src/client/router.ts index 573f285c79..56dc948669 100644 --- a/src/client/router.ts +++ b/src/client/router.ts @@ -23,6 +23,7 @@ const defaultRoutes = [ { path: '/@:acct/room', props: true, component: page('room/room') }, { path: '/settings/:page(.*)?', name: 'settings', component: page('settings/index'), props: route => ({ initialPage: route.params.page || null }) }, { path: '/reset-password/:token?', component: page('reset-password'), props: route => ({ token: route.params.token }) }, + { path: '/signup-complete/:code', component: page('signup-complete'), props: route => ({ code: route.params.code }) }, { path: '/announcements', component: page('announcements') }, { path: '/about', component: page('about') }, { path: '/about-misskey', component: page('about-misskey') }, diff --git a/src/db/postgre.ts b/src/db/postgre.ts index c963242488..8948f22cdc 100644 --- a/src/db/postgre.ts +++ b/src/db/postgre.ts @@ -72,6 +72,7 @@ import { ChannelNotePining } from '@/models/entities/channel-note-pining'; import { RegistryItem } from '@/models/entities/registry-item'; import { Ad } from '@/models/entities/ad'; import { PasswordResetRequest } from '@/models/entities/password-reset-request'; +import { UserPending } from '@/models/entities/user-pending'; const sqlLogger = dbLogger.createSubLogger('sql', 'white', false); @@ -173,6 +174,7 @@ export const entities = [ RegistryItem, Ad, PasswordResetRequest, + UserPending, ...charts as any ]; diff --git a/src/models/entities/meta.ts b/src/models/entities/meta.ts index 6428aacdf1..9a1a87c155 100644 --- a/src/models/entities/meta.ts +++ b/src/models/entities/meta.ts @@ -148,6 +148,11 @@ export class Meta { @JoinColumn() public proxyAccount: User | null; + @Column('boolean', { + default: false, + }) + public emailRequiredForSignup: boolean; + @Column('boolean', { default: false, }) diff --git a/src/models/entities/user-pending.ts b/src/models/entities/user-pending.ts new file mode 100644 index 0000000000..40482af333 --- /dev/null +++ b/src/models/entities/user-pending.ts @@ -0,0 +1,32 @@ +import { PrimaryColumn, Entity, Index, Column } from 'typeorm'; +import { id } from '../id'; + +@Entity() +export class UserPending { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone') + public createdAt: Date; + + @Index({ unique: true }) + @Column('varchar', { + length: 128, + }) + public code: string; + + @Column('varchar', { + length: 128, + }) + public username: string; + + @Column('varchar', { + length: 128, + }) + public email: string; + + @Column('varchar', { + length: 128, + }) + public password: string; +} diff --git a/src/models/index.ts b/src/models/index.ts index 9f8bd104e9..059a3d7b87 100644 --- a/src/models/index.ts +++ b/src/models/index.ts @@ -62,6 +62,7 @@ import { ChannelNotePining } from './entities/channel-note-pining'; import { RegistryItem } from './entities/registry-item'; import { Ad } from './entities/ad'; import { PasswordResetRequest } from './entities/password-reset-request'; +import { UserPending } from './entities/user-pending'; export const Announcements = getRepository(Announcement); export const AnnouncementReads = getRepository(AnnouncementRead); @@ -76,6 +77,7 @@ export const PollVotes = getRepository(PollVote); export const Users = getCustomRepository(UserRepository); export const UserProfiles = getRepository(UserProfile); export const UserKeypairs = getRepository(UserKeypair); +export const UserPendings = getRepository(UserPending); export const AttestationChallenges = getRepository(AttestationChallenge); export const UserSecurityKeys = getRepository(UserSecurityKey); export const UserPublickeys = getRepository(UserPublickey); diff --git a/src/server/api/common/signup.ts b/src/server/api/common/signup.ts index eb3aa09c8c..2ba0d8e479 100644 --- a/src/server/api/common/signup.ts +++ b/src/server/api/common/signup.ts @@ -11,20 +11,30 @@ import { UserKeypair } from '@/models/entities/user-keypair'; import { usersChart } from '@/services/chart/index'; import { UsedUsername } from '@/models/entities/used-username'; -export async function signup(username: User['username'], password: UserProfile['password'], host: string | null = null) { +export async function signup(opts: { + username: User['username']; + password?: string | null; + passwordHash?: UserProfile['password'] | null; + host?: string | null; +}) { + const { username, password, passwordHash, host } = opts; + let hash = passwordHash; + // Validate username if (!Users.validateLocalUsername.ok(username)) { throw new Error('INVALID_USERNAME'); } - // Validate password - if (!Users.validatePassword.ok(password)) { - throw new Error('INVALID_PASSWORD'); - } + if (password != null && passwordHash == null) { + // Validate password + if (!Users.validatePassword.ok(password)) { + throw new Error('INVALID_PASSWORD'); + } - // Generate hash of password - const salt = await bcrypt.genSalt(8); - const hash = await bcrypt.hash(password, salt); + // Generate hash of password + const salt = await bcrypt.genSalt(8); + hash = await bcrypt.hash(password, salt); + } // Generate secret const secret = generateUserToken(); diff --git a/src/server/api/endpoints/admin/accounts/create.ts b/src/server/api/endpoints/admin/accounts/create.ts index 9691b9c7e3..fa15e84f77 100644 --- a/src/server/api/endpoints/admin/accounts/create.ts +++ b/src/server/api/endpoints/admin/accounts/create.ts @@ -35,7 +35,10 @@ export default define(meta, async (ps, _me) => { })) === 0; if (!noUsers && !me?.isAdmin) throw new Error('access denied'); - const { account, secret } = await signup(ps.username, ps.password); + const { account, secret } = await signup({ + username: ps.username, + password: ps.password, + }); const res = await Users.pack(account, account, { detail: true, diff --git a/src/server/api/endpoints/admin/update-meta.ts b/src/server/api/endpoints/admin/update-meta.ts index 46f30fef7d..55447098dc 100644 --- a/src/server/api/endpoints/admin/update-meta.ts +++ b/src/server/api/endpoints/admin/update-meta.ts @@ -93,6 +93,10 @@ export const meta = { validator: $.optional.bool, }, + emailRequiredForSignup: { + validator: $.optional.bool, + }, + enableHcaptcha: { validator: $.optional.bool, }, @@ -374,6 +378,10 @@ export default define(meta, async (ps, me) => { set.proxyRemoteFiles = ps.proxyRemoteFiles; } + if (ps.emailRequiredForSignup !== undefined) { + set.emailRequiredForSignup = ps.emailRequiredForSignup; + } + if (ps.enableHcaptcha !== undefined) { set.enableHcaptcha = ps.enableHcaptcha; } diff --git a/src/server/api/endpoints/email-address/available.ts b/src/server/api/endpoints/email-address/available.ts new file mode 100644 index 0000000000..65fe6f9178 --- /dev/null +++ b/src/server/api/endpoints/email-address/available.ts @@ -0,0 +1,37 @@ +import $ from 'cafy'; +import define from '../../define'; +import { UserProfiles } from '@/models/index'; + +export const meta = { + tags: ['users'], + + requireCredential: false as const, + + params: { + emailAddress: { + validator: $.str + } + }, + + res: { + type: 'object' as const, + optional: false as const, nullable: false as const, + properties: { + available: { + type: 'boolean' as const, + optional: false as const, nullable: false as const, + } + } + } +}; + +export default define(meta, async (ps) => { + const exist = await UserProfiles.count({ + emailVerified: true, + email: ps.emailAddress, + }); + + return { + available: exist === 0 + }; +}); diff --git a/src/server/api/endpoints/meta.ts b/src/server/api/endpoints/meta.ts index 3f422dff07..ce21556243 100644 --- a/src/server/api/endpoints/meta.ts +++ b/src/server/api/endpoints/meta.ts @@ -104,6 +104,10 @@ export const meta = { type: 'boolean' as const, optional: false as const, nullable: false as const }, + emailRequiredForSignup: { + type: 'boolean' as const, + optional: false as const, nullable: false as const + }, enableHcaptcha: { type: 'boolean' as const, optional: false as const, nullable: false as const @@ -488,6 +492,7 @@ export default define(meta, async (ps, me) => { disableGlobalTimeline: instance.disableGlobalTimeline, driveCapacityPerLocalUserMb: instance.localDriveCapacityMb, driveCapacityPerRemoteUserMb: instance.remoteDriveCapacityMb, + emailRequiredForSignup: instance.emailRequiredForSignup, enableHcaptcha: instance.enableHcaptcha, hcaptchaSiteKey: instance.hcaptchaSiteKey, enableRecaptcha: instance.enableRecaptcha, @@ -537,6 +542,7 @@ export default define(meta, async (ps, me) => { registration: !instance.disableRegistration, localTimeLine: !instance.disableLocalTimeline, globalTimeLine: !instance.disableGlobalTimeline, + emailRequiredForSignup: instance.emailRequiredForSignup, elasticsearch: config.elasticsearch ? true : false, hcaptcha: instance.enableHcaptcha, recaptcha: instance.enableRecaptcha, diff --git a/src/server/api/index.ts b/src/server/api/index.ts index db35fdf9e0..82579075eb 100644 --- a/src/server/api/index.ts +++ b/src/server/api/index.ts @@ -12,6 +12,7 @@ import endpoints from './endpoints'; import handler from './api-handler'; import signup from './private/signup'; import signin from './private/signin'; +import signupPending from './private/signup-pending'; import discord from './service/discord'; import github from './service/github'; import twitter from './service/twitter'; @@ -65,6 +66,7 @@ for (const endpoint of endpoints) { router.post('/signup', signup); router.post('/signin', signin); +router.post('/signup-pending', signupPending); router.use(discord.routes()); router.use(github.routes()); diff --git a/src/server/api/private/signup-pending.ts b/src/server/api/private/signup-pending.ts new file mode 100644 index 0000000000..c0638a1cda --- /dev/null +++ b/src/server/api/private/signup-pending.ts @@ -0,0 +1,35 @@ +import * as Koa from 'koa'; +import { Users, UserPendings, UserProfiles } from '@/models/index'; +import { signup } from '../common/signup'; +import signin from '../common/signin'; + +export default async (ctx: Koa.Context) => { + const body = ctx.request.body; + + const code = body['code']; + + try { + const pendingUser = await UserPendings.findOneOrFail({ code }); + + const { account, secret } = await signup({ + username: pendingUser.username, + passwordHash: pendingUser.password, + }); + + UserPendings.delete({ + id: pendingUser.id, + }); + + const profile = await UserProfiles.findOneOrFail(account.id); + + await UserProfiles.update({ userId: profile.userId }, { + email: pendingUser.email, + emailVerified: true, + emailVerifyCode: null, + }); + + signin(ctx, account); + } catch (e) { + ctx.throw(400, e); + } +}; diff --git a/src/server/api/private/signup.ts b/src/server/api/private/signup.ts index ef61767f65..93caaea935 100644 --- a/src/server/api/private/signup.ts +++ b/src/server/api/private/signup.ts @@ -1,8 +1,13 @@ import * as Koa from 'koa'; +import rndstr from 'rndstr'; +import * as bcrypt from 'bcryptjs'; import { fetchMeta } from '@/misc/fetch-meta'; import { verifyHcaptcha, verifyRecaptcha } from '@/misc/captcha'; -import { Users, RegistrationTickets } from '@/models/index'; +import { Users, RegistrationTickets, UserPendings } from '@/models/index'; import { signup } from '../common/signup'; +import config from '@/config'; +import { sendEmail } from '@/services/send-email'; +import { genId } from '@/misc/gen-id'; export default async (ctx: Koa.Context) => { const body = ctx.request.body; @@ -29,8 +34,16 @@ export default async (ctx: Koa.Context) => { const password = body['password']; const host: string | null = process.env.NODE_ENV === 'test' ? (body['host'] || null) : null; const invitationCode = body['invitationCode']; + const emailAddress = body['emailAddress']; - if (instance && instance.disableRegistration) { + if (instance.emailRequiredForSignup) { + if (emailAddress == null || typeof emailAddress != 'string') { + ctx.status = 400; + return; + } + } + + if (instance.disableRegistration) { if (invitationCode == null || typeof invitationCode != 'string') { ctx.status = 400; return; @@ -48,18 +61,45 @@ export default async (ctx: Koa.Context) => { RegistrationTickets.delete(ticket.id); } - try { - const { account, secret } = await signup(username, password, host); + if (instance.emailRequiredForSignup) { + const code = rndstr('a-z0-9', 16); - const res = await Users.pack(account, account, { - detail: true, - includeSecrets: true + // Generate hash of password + const salt = await bcrypt.genSalt(8); + const hash = await bcrypt.hash(password, salt); + + await UserPendings.insert({ + id: genId(), + createdAt: new Date(), + code, + email: emailAddress, + username: username, + password: hash, }); - (res as any).token = secret; + const link = `${config.url}/signup-complete/${code}`; - ctx.body = res; - } catch (e) { - ctx.throw(400, e); + sendEmail(emailAddress, 'Signup', + `To complete signup, please click this link:<br><a href="${link}">${link}</a>`, + `To complete signup, please click this link: ${link}`); + + ctx.status = 204; + } else { + try { + const { account, secret } = await signup({ + username, password, host + }); + + const res = await Users.pack(account, account, { + detail: true, + includeSecrets: true + }); + + (res as any).token = secret; + + ctx.body = res; + } catch (e) { + ctx.throw(400, e); + } } }; diff --git a/src/server/nodeinfo.ts b/src/server/nodeinfo.ts index dec2615086..6a864fcc52 100644 --- a/src/server/nodeinfo.ts +++ b/src/server/nodeinfo.ts @@ -68,6 +68,7 @@ const nodeinfo2 = async () => { disableRegistration: meta.disableRegistration, disableLocalTimeline: meta.disableLocalTimeline, disableGlobalTimeline: meta.disableGlobalTimeline, + emailRequiredForSignup: meta.emailRequiredForSignup, enableHcaptcha: meta.enableHcaptcha, enableRecaptcha: meta.enableRecaptcha, maxNoteTextLength: meta.maxNoteTextLength, From 597c9761cb09614cb05a0dfe5c2d126f549fa2dd Mon Sep 17 00:00:00 2001 From: tamaina <tamaina@hotmail.co.jp> Date: Fri, 8 Oct 2021 13:37:55 +0900 Subject: [PATCH 043/107] Revert "Revert "Fix idb"" (#7860) This reverts commit 67875e2afa65fb0be477099def0e3d9cdb08ea22. --- src/client/scripts/idb-proxy.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/client/scripts/idb-proxy.ts b/src/client/scripts/idb-proxy.ts index 21c4dcff65..5f76ae30bb 100644 --- a/src/client/scripts/idb-proxy.ts +++ b/src/client/scripts/idb-proxy.ts @@ -4,7 +4,6 @@ import { get as iget, set as iset, del as idel, - createStore, } from 'idb-keyval'; const fallbackName = (key: string) => `idbfallback::${key}`; @@ -13,9 +12,9 @@ let idbAvailable = typeof window !== 'undefined' ? !!window.indexedDB : true; if (idbAvailable) { try { - await createStore('keyval-store', 'keyval'); + await iset('idb-test', 'test'); } catch (e) { - console.error('idb open error', e); + console.error('idb error', e); idbAvailable = false; } } From 5bf69476f625f3c4764cfb242d7d6a21c808f8b8 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 8 Oct 2021 14:05:07 +0900 Subject: [PATCH 044/107] =?UTF-8?q?enhance(api):=20ap=E7=B3=BB=E3=81=AE?= =?UTF-8?q?=E3=82=A8=E3=83=B3=E3=83=89=E3=83=9D=E3=82=A4=E3=83=B3=E3=83=88?= =?UTF-8?q?=E3=82=92=E3=83=AD=E3=82=B0=E3=82=A4=E3=83=B3=E5=BF=85=E9=A0=88?= =?UTF-8?q?=E5=8C=96+=E3=83=AC=E3=83=BC=E3=83=88=E3=83=AA=E3=83=9F?= =?UTF-8?q?=E3=83=83=E3=83=88=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 他のサーバーにリクエストを送信するという性質上、攻撃の踏み台にされることがあるため --- CHANGELOG.md | 1 + src/server/api/endpoints/ap/get.ts | 8 +++++++- src/server/api/endpoints/ap/show.ts | 8 +++++++- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bd526fd694..f65de79116 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ - クライアント: アニメーションを減らす設定をメニューのアニメーションにも適用するように - クライアント: MFM関数構文のサジェストを実装 - ActivityPub: HTML -> MFMの変換を強化 +- API: ap系のエンドポイントをログイン必須化+レートリミット追加 ### Bugfixes - Fix createDeleteAccountJob diff --git a/src/server/api/endpoints/ap/get.ts b/src/server/api/endpoints/ap/get.ts index 2cffce1f16..2f97a24774 100644 --- a/src/server/api/endpoints/ap/get.ts +++ b/src/server/api/endpoints/ap/get.ts @@ -2,11 +2,17 @@ import $ from 'cafy'; import define from '../../define'; import Resolver from '@/remote/activitypub/resolver'; import { ApiError } from '../../error'; +import ms from 'ms'; export const meta = { tags: ['federation'], - requireCredential: false as const, + requireCredential: true as const, + + limit: { + duration: ms('1hour'), + max: 30 + }, params: { uri: { diff --git a/src/server/api/endpoints/ap/show.ts b/src/server/api/endpoints/ap/show.ts index aa0dae070c..32685d44bd 100644 --- a/src/server/api/endpoints/ap/show.ts +++ b/src/server/api/endpoints/ap/show.ts @@ -11,11 +11,17 @@ import { Note } from '@/models/entities/note'; import { User } from '@/models/entities/user'; import { fetchMeta } from '@/misc/fetch-meta'; import { isActor, isPost, getApId } from '@/remote/activitypub/type'; +import ms from 'ms'; export const meta = { tags: ['federation'], - requireCredential: false as const, + requireCredential: true as const, + + limit: { + duration: ms('1hour'), + max: 30 + }, params: { uri: { From 129f652dc2b242990d2825a6744f5a2a1dbcce0d Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 8 Oct 2021 19:34:31 +0900 Subject: [PATCH 045/107] use commander 4.1.1 --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 5f8900d7af..d12a0fc9ba 100644 --- a/package.json +++ b/package.json @@ -121,7 +121,7 @@ "chalk": "4.1.2", "chart.js": "2.9.4", "cli-highlight": "2.1.11", - "commander": "8.2.0", + "commander": "4.1.1", "compare-versions": "3.6.0", "concurrently": "6.3.0", "content-disposition": "0.5.3", diff --git a/yarn.lock b/yarn.lock index 71a2002c1b..e999c20552 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3093,10 +3093,10 @@ combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: dependencies: delayed-stream "~1.0.0" -commander@8.2.0: - version "8.2.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-8.2.0.tgz#37fe2bde301d87d47a53adeff8b5915db1381ca8" - integrity sha512-LLKxDvHeL91/8MIyTAD5BFMNtoIwztGPMiM/7Bl8rIPmHCZXRxmSWr91h57dpOpnQ6jIUqEWdXE/uBYMfiVZDA== +commander@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" + integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== commander@^2.12.1, commander@^2.19.0, commander@^2.20.0: version "2.20.3" From a38e4b0b144eed8b42e6d6c894a00ad3feca40aa Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 8 Oct 2021 21:24:05 +0900 Subject: [PATCH 046/107] =?UTF-8?q?server:=20=E3=82=B3=E3=83=9E=E3=83=B3?= =?UTF-8?q?=E3=83=89=E3=83=A9=E3=82=A4=E3=83=B3=E3=82=AA=E3=83=97=E3=82=B7?= =?UTF-8?q?=E3=83=A7=E3=83=B3=E5=BB=83=E6=AD=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolve #7863 Resolve #6337 --- CHANGELOG.md | 2 ++ package.json | 1 - src/argv.ts | 23 ----------------------- src/boot/index.ts | 8 ++++---- src/boot/master.ts | 8 ++++---- src/db/postgre.ts | 4 ++-- src/env.ts | 20 ++++++++++++++++++++ src/queue/index.ts | 4 ++-- src/server/index.ts | 4 ++-- src/services/logger.ts | 8 ++++---- yarn.lock | 5 ----- 11 files changed, 40 insertions(+), 47 deletions(-) delete mode 100644 src/argv.ts create mode 100644 src/env.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index f65de79116..215555df72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,8 @@ - クライアント: MFM関数構文のサジェストを実装 - ActivityPub: HTML -> MFMの変換を強化 - API: ap系のエンドポイントをログイン必須化+レートリミット追加 +- Misskeyのコマンドラインオプションを廃止 + - 代わりに環境変数で設定することができます ### Bugfixes - Fix createDeleteAccountJob diff --git a/package.json b/package.json index d12a0fc9ba..308e6242c8 100644 --- a/package.json +++ b/package.json @@ -121,7 +121,6 @@ "chalk": "4.1.2", "chart.js": "2.9.4", "cli-highlight": "2.1.11", - "commander": "4.1.1", "compare-versions": "3.6.0", "concurrently": "6.3.0", "content-disposition": "0.5.3", diff --git a/src/argv.ts b/src/argv.ts deleted file mode 100644 index 106ecf2675..0000000000 --- a/src/argv.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Command } from 'commander'; -import config from '@/config/index'; - -const program = new Command(); - -program.version(config.version); -program.option('--no-daemons', 'Disable daemon processes (for debbuging)'); -program.option('--disable-clustering', 'Disable clustering'); -program.option('--only-server', 'Run server only (without job queue processing)'); -program.option('--only-queue', 'Pocessing job queue only (without server)'); -program.option('--quiet', 'Suppress all logs'); -program.option('--verbose', 'Enable all logs'); -program.option('--with-log-time', 'Include timestamp for each logs'); -program.option('--slow', 'Delay all requests (for debbuging)'); -program.option('--color', 'This option is a dummy for some external program\'s (e.g. forever) issue.'); -program.parse(process.argv); - -if (process.env.MK_ONLY_QUEUE) program.onlyQueue = true; -if (process.env.NODE_ENV === 'test') program.disableClustering = true; -//if (process.env.NODE_ENV === 'test') program.quiet = true; -if (process.env.NODE_ENV === 'test') program.noDaemons = true; - -export { program }; diff --git a/src/boot/index.ts b/src/boot/index.ts index 20c53a366c..cb4c8536db 100644 --- a/src/boot/index.ts +++ b/src/boot/index.ts @@ -3,7 +3,7 @@ import * as chalk from 'chalk'; import Xev from 'xev'; import Logger from '@/services/logger'; -import { program } from '../argv'; +import { envOption } from '../env'; // for typeorm import 'reflect-metadata'; @@ -20,7 +20,7 @@ const ev = new Xev(); export default async function() { process.title = `Misskey (${cluster.isMaster ? 'master' : 'worker'})`; - if (cluster.isMaster || program.disableClustering) { + if (cluster.isMaster || envOption.disableClustering) { await masterMain(); if (cluster.isMaster) { @@ -28,7 +28,7 @@ export default async function() { } } - if (cluster.isWorker || program.disableClustering) { + if (cluster.isWorker || envOption.disableClustering) { await workerMain(); } @@ -60,7 +60,7 @@ cluster.on('exit', worker => { }); // Display detail of unhandled promise rejection -if (!program.quiet) { +if (!envOption.quiet) { process.on('unhandledRejection', console.dir); } diff --git a/src/boot/master.ts b/src/boot/master.ts index d9cc7c16be..071b37b76d 100644 --- a/src/boot/master.ts +++ b/src/boot/master.ts @@ -11,7 +11,7 @@ import Logger from '@/services/logger'; import loadConfig from '@/config/load'; import { Config } from '@/config/types'; import { lessThan } from '@/prelude/array'; -import { program } from '../argv'; +import { envOption } from '../env'; import { showMachineInfo } from '@/misc/show-machine-info'; import { initDb } from '../db/postgre'; @@ -25,7 +25,7 @@ const logger = new Logger('core', 'cyan'); const bootLogger = logger.createSubLogger('boot', 'magenta', false); function greet() { - if (!program.quiet) { + if (!envOption.quiet) { //#region Misskey logo const v = `v${meta.version}`; console.log(' _____ _ _ '); @@ -73,13 +73,13 @@ export async function masterMain() { bootLogger.succ('Misskey initialized'); - if (!program.disableClustering) { + if (!envOption.disableClustering) { await spawnWorkers(config.clusterLimit); } bootLogger.succ(`Now listening on port ${config.port} on ${config.url}`, null, true); - if (!program.noDaemons) { + if (!envOption.noDaemons) { require('../daemons/server-stats').default(); require('../daemons/queue-stats').default(); require('../daemons/janitor').default(); diff --git a/src/db/postgre.ts b/src/db/postgre.ts index 8948f22cdc..0b635ea18e 100644 --- a/src/db/postgre.ts +++ b/src/db/postgre.ts @@ -63,7 +63,7 @@ import { Antenna } from '@/models/entities/antenna'; import { AntennaNote } from '@/models/entities/antenna-note'; import { PromoNote } from '@/models/entities/promo-note'; import { PromoRead } from '@/models/entities/promo-read'; -import { program } from '../argv'; +import { envOption } from '../env'; import { Relay } from '@/models/entities/relay'; import { MutedNote } from '@/models/entities/muted-note'; import { Channel } from '@/models/entities/channel'; @@ -84,7 +84,7 @@ class MyCustomLogger implements Logger { } public logQuery(query: string, parameters?: any[]) { - if (program.verbose) { + if (envOption.verbose) { sqlLogger.info(this.highlight(query)); } } diff --git a/src/env.ts b/src/env.ts new file mode 100644 index 0000000000..1b678edc44 --- /dev/null +++ b/src/env.ts @@ -0,0 +1,20 @@ +const envOption = { + onlyQueue: false, + onlyServer: false, + noDaemons: false, + disableClustering: false, + verbose: false, + withLogTime: false, + quiet: false, + slow: false, +}; + +for (const key of Object.keys(envOption) as (keyof typeof envOption)[]) { + if (process.env['MK_' + key.replace(/[A-Z]/g, letter => `_${letter}`).toUpperCase()]) envOption[key] = true; +} + +if (process.env.NODE_ENV === 'test') envOption.disableClustering = true; +if (process.env.NODE_ENV === 'test') envOption.quiet = true; +if (process.env.NODE_ENV === 'test') envOption.noDaemons = true; + +export { envOption }; diff --git a/src/queue/index.ts b/src/queue/index.ts index c787253ded..1e1d5da5a2 100644 --- a/src/queue/index.ts +++ b/src/queue/index.ts @@ -1,7 +1,7 @@ import * as httpSignature from 'http-signature'; import config from '@/config/index'; -import { program } from '../argv'; +import { envOption } from '../env'; import processDeliver from './processors/deliver'; import processInbox from './processors/inbox'; @@ -200,7 +200,7 @@ export function createCleanRemoteFilesJob() { } export default function() { - if (!program.onlyServer) { + if (!envOption.onlyServer) { deliverQueue.process(config.deliverJobConcurrency || 128, processDeliver); inboxQueue.process(config.inboxJobConcurrency || 16, processInbox); processDb(dbQueue); diff --git a/src/server/index.ts b/src/server/index.ts index fb4e48c1c6..c891596140 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -20,7 +20,7 @@ import config from '@/config/index'; import apiServer from './api/index'; import { sum } from '@/prelude/array'; import Logger from '@/services/logger'; -import { program } from '../argv'; +import { envOption } from '../env'; import { UserProfiles, Users } from '@/models/index'; import { networkChart } from '@/services/chart/index'; import { genAvatar } from '@/misc/gen-avatar'; @@ -40,7 +40,7 @@ if (!['production', 'test'].includes(process.env.NODE_ENV || '')) { })); // Delay - if (program.slow) { + if (envOption.slow) { app.use(slow({ delay: 3000 })); diff --git a/src/services/logger.ts b/src/services/logger.ts index 229be891e1..8e783e67f6 100644 --- a/src/services/logger.ts +++ b/src/services/logger.ts @@ -2,7 +2,7 @@ import * as cluster from 'cluster'; import * as os from 'os'; import * as chalk from 'chalk'; import * as dateformat from 'dateformat'; -import { program } from '../argv'; +import { envOption } from '../env'; import { getRepository } from 'typeorm'; import { Log } from '@/models/entities/log'; import { genId } from '@/misc/gen-id'; @@ -52,7 +52,7 @@ export default class Logger { } private log(level: Level, message: string, data?: Record<string, any> | null, important = false, subDomains: Domain[] = [], store = true): void { - if (program.quiet) return; + if (envOption.quiet) return; if (!this.store) store = false; if (level === 'debug') store = false; @@ -80,7 +80,7 @@ export default class Logger { null; let log = `${l} ${worker}\t[${domains.join(' ')}]\t${m}`; - if (program.withLogTime) log = chalk.gray(time) + ' ' + log; + if (envOption.withLogTime) log = chalk.gray(time) + ' ' + log; console.log(important ? chalk.bold(log) : log); @@ -132,7 +132,7 @@ export default class Logger { } public debug(message: string, data?: Record<string, any> | null, important = false): void { // デバッグ用に使う(開発者に必要だが利用者に不要な情報) - if (process.env.NODE_ENV != 'production' || program.verbose) { + if (process.env.NODE_ENV != 'production' || envOption.verbose) { this.log('debug', message, data, important); } } diff --git a/yarn.lock b/yarn.lock index e999c20552..b5ef779125 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3093,11 +3093,6 @@ combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: dependencies: delayed-stream "~1.0.0" -commander@4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" - integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== - commander@^2.12.1, commander@^2.19.0, commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" From 8b1999dc5b68220a3e142812fadbc42f563a18b1 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 8 Oct 2021 21:24:53 +0900 Subject: [PATCH 047/107] fix(api): (0 , ms_1.default) is not a function --- src/server/api/endpoints/ap/get.ts | 2 +- src/server/api/endpoints/ap/show.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/server/api/endpoints/ap/get.ts b/src/server/api/endpoints/ap/get.ts index 2f97a24774..78919f43b0 100644 --- a/src/server/api/endpoints/ap/get.ts +++ b/src/server/api/endpoints/ap/get.ts @@ -2,7 +2,7 @@ import $ from 'cafy'; import define from '../../define'; import Resolver from '@/remote/activitypub/resolver'; import { ApiError } from '../../error'; -import ms from 'ms'; +import * as ms from 'ms'; export const meta = { tags: ['federation'], diff --git a/src/server/api/endpoints/ap/show.ts b/src/server/api/endpoints/ap/show.ts index 32685d44bd..2280d93724 100644 --- a/src/server/api/endpoints/ap/show.ts +++ b/src/server/api/endpoints/ap/show.ts @@ -11,7 +11,7 @@ import { Note } from '@/models/entities/note'; import { User } from '@/models/entities/user'; import { fetchMeta } from '@/misc/fetch-meta'; import { isActor, isPost, getApId } from '@/remote/activitypub/type'; -import ms from 'ms'; +import * as ms from 'ms'; export const meta = { tags: ['federation'], From 748a451e233f71dd493f7dd80062278dacaa5e1c Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 8 Oct 2021 22:03:06 +0900 Subject: [PATCH 048/107] :art: --- src/client/components/modal-page-window.vue | 3 +- src/client/components/page-window.vue | 10 ++++-- src/client/components/ui/window.vue | 21 ++++++++----- src/client/directives/get-size.ts | 34 ++++++++++++++++++++ src/client/directives/index.ts | 2 ++ src/client/pages/settings/index.vue | 1 + src/client/pages/settings/profile.vue | 3 +- src/client/ui/_common_/header.vue | 35 +++++++-------------- src/client/ui/chat/index.vue | 2 +- src/client/ui/chat/side.vue | 2 +- src/client/ui/deck/main-column.vue | 5 ++- src/client/ui/default.header.vue | 2 +- src/client/ui/default.side.vue | 3 +- src/client/ui/default.vue | 11 +++---- src/client/ui/universal.vue | 22 +++---------- 15 files changed, 93 insertions(+), 63 deletions(-) create mode 100644 src/client/directives/get-size.ts diff --git a/src/client/components/modal-page-window.vue b/src/client/components/modal-page-window.vue index e7d96f7a6f..f57516360d 100644 --- a/src/client/components/modal-page-window.vue +++ b/src/client/components/modal-page-window.vue @@ -3,10 +3,11 @@ <div class="hrmcaedk _window _narrow_" :style="{ width: `${width}px`, height: (height ? `min(${height}px, 100%)` : '100%') }"> <div class="header" @contextmenu="onContextmenu"> <span class="title"> - <XHeader :info="pageInfo" :back-button="history.length > 0" @back="back()" :close-button="true" @close="$refs.modal.close()"/> + </span> </div> <div class="body _flat_"> + <XHeader :info="pageInfo"/> <keep-alive> <component :is="component" v-bind="props" :ref="changePage"/> </keep-alive> diff --git a/src/client/components/page-window.vue b/src/client/components/page-window.vue index fbc9f0b7fd..a8a8b76100 100644 --- a/src/client/components/page-window.vue +++ b/src/client/components/page-window.vue @@ -3,14 +3,20 @@ :initial-width="500" :initial-height="500" :can-resize="true" - :close-button="false" + :close-button="true" :contextmenu="contextmenu" @closed="$emit('closed')" > <template #header> - <XHeader :info="pageInfo" :back-button="history.length > 0" @back="back()" :close-button="true" @close="close()" :title-only="true"/> + <template v-if="pageInfo"> + {{ pageInfo.title }} + </template> + </template> + <template #headerLeft> + <button v-if="history.length > 0" class="_button" @click="back()"><i class="fas fa-arrow-left"></i></button> </template> <div class="yrolvcoq _flat_"> + <XHeader :info="pageInfo"/> <component :is="component" v-bind="props" :ref="changePage"/> </div> </XWindow> diff --git a/src/client/components/ui/window.vue b/src/client/components/ui/window.vue index 773c3b9b13..6a7c61a7d5 100644 --- a/src/client/components/ui/window.vue +++ b/src/client/components/ui/window.vue @@ -3,11 +3,16 @@ <div class="ebkgocck" :class="{ front }" v-if="showing"> <div class="body _window _shadow _narrow_" @mousedown="onBodyMousedown" @keydown="onKeydown"> <div class="header" :class="{ mini }" @contextmenu.prevent.stop="onContextmenu"> - <button v-if="closeButton" class="_button" @click="close()"><i class="fas fa-times"></i></button> - + <span class="left"> + <slot name="headerLeft"></slot> + </span> <span class="title" @mousedown.prevent="onHeaderMousedown" @touchstart.prevent="onHeaderMousedown"> <slot name="header"></slot> </span> + <span class="right"> + <slot name="headerRight"></slot> + <button v-if="closeButton" class="_button" @click="close()"><i class="fas fa-times"></i></button> + </span> </div> <div class="body" v-if="padding"> <div class="_section"> @@ -418,12 +423,14 @@ export default defineComponent({ height: var(--height); border-bottom: solid 1px var(--divider); - > ::v-deep(button) { - height: var(--height); - width: var(--height); + > .left, > .right { + > ::v-deep(button) { + height: var(--height); + width: var(--height); - &:hover { - color: var(--fgHighlighted); + &:hover { + color: var(--fgHighlighted); + } } } diff --git a/src/client/directives/get-size.ts b/src/client/directives/get-size.ts new file mode 100644 index 0000000000..e3b5dea0f3 --- /dev/null +++ b/src/client/directives/get-size.ts @@ -0,0 +1,34 @@ +import { Directive } from 'vue'; + +export default { + mounted(src, binding, vn) { + const calc = () => { + const height = src.clientHeight; + const width = src.clientWidth; + + // 要素が(一時的に)DOMに存在しないときは計算スキップ + if (height === 0) return; + + binding.value(width, height); + }; + + calc(); + + // Vue3では使えなくなった + // 無くても大丈夫か...? + // TODO: ↑大丈夫じゃなかったので解決策を探す + //vn.context.$on('hook:activated', calc); + + const ro = new ResizeObserver((entries, observer) => { + calc(); + }); + ro.observe(src); + + src._get_size_ro_ = ro; + }, + + unmounted(src, binding, vn) { + binding.value(0, 0); + src._get_size_ro_.unobserve(src); + } +} as Directive; diff --git a/src/client/directives/index.ts b/src/client/directives/index.ts index f0a0123771..cd71bc26d3 100644 --- a/src/client/directives/index.ts +++ b/src/client/directives/index.ts @@ -2,6 +2,7 @@ import { App } from 'vue'; import userPreview from './user-preview'; import size from './size'; +import getSize from './get-size'; import particle from './particle'; import tooltip from './tooltip'; import hotkey from './hotkey'; @@ -14,6 +15,7 @@ export default function(app: App) { app.directive('userPreview', userPreview); app.directive('user-preview', userPreview); app.directive('size', size); + app.directive('get-size', getSize); app.directive('particle', particle); app.directive('tooltip', tooltip); app.directive('hotkey', hotkey); diff --git a/src/client/pages/settings/index.vue b/src/client/pages/settings/index.vue index cb86fdca71..3a8503ac55 100644 --- a/src/client/pages/settings/index.vue +++ b/src/client/pages/settings/index.vue @@ -74,6 +74,7 @@ export default defineComponent({ title: i18n.locale.settings, icon: 'fas fa-cog', bg: 'var(--bg)', + hide: true, }; const INFO = ref(indexInfo); const page = ref(props.initialPage); diff --git a/src/client/pages/settings/profile.vue b/src/client/pages/settings/profile.vue index 3c93e93480..eb9bc6565f 100644 --- a/src/client/pages/settings/profile.vue +++ b/src/client/pages/settings/profile.vue @@ -77,7 +77,8 @@ export default defineComponent({ [symbols.PAGE_INFO]: { title: this.$ts.profile, icon: 'fas fa-user', - bg: 'var(--bg)' + bg: 'var(--bg)', + hide: true, }, host, langs, diff --git a/src/client/ui/_common_/header.vue b/src/client/ui/_common_/header.vue index f21be2f9cd..5405c43f8c 100644 --- a/src/client/ui/_common_/header.vue +++ b/src/client/ui/_common_/header.vue @@ -1,10 +1,5 @@ <template> -<div class="fdidabkb" :class="{ slim: titleOnly || narrow }" :style="`--height:${height};`" :key="key"> - <transition :name="$store.state.animation ? 'header' : ''" mode="out-in" appear> - <div class="buttons left" v-if="backButton"> - <button class="_button button back" @click.stop="$emit('back')" @touchstart="preventDrag" v-tooltip="$ts.goBack"><i class="fas fa-chevron-left"></i></button> - </div> - </transition> +<div class="fdidabkb" :class="{ slim: narrow, thin }" :key="key"> <template v-if="info"> <div class="titleContainer" @click="showTabsPopup"> <i v-if="info.icon" class="icon" :class="info.icon"></i> @@ -34,7 +29,6 @@ <button v-for="action in info.actions" class="_button button" :class="{ highlighted: action.highlighted }" @click.stop="action.handler" @touchstart="preventDrag" v-tooltip="action.text"><i :class="action.icon"></i></button> </template> <button v-if="shouldShowMenu" class="_button button" @click.stop="showMenu" @touchstart="preventDrag" v-tooltip="$ts.menu"><i class="fas fa-ellipsis-h"></i></button> - <button v-if="closeButton" class="_button button" @click.stop="$emit('close')" @touchstart="preventDrag" v-tooltip="$ts.close"><i class="fas fa-times"></i></button> </div> </div> </template> @@ -52,20 +46,9 @@ export default defineComponent({ menu: { required: false }, - backButton: { - type: Boolean, + thin: { required: false, - default: false, - }, - closeButton: { - type: Boolean, - required: false, - default: false, - }, - titleOnly: { - type: Boolean, - required: false, - default: false, + default: false }, }, @@ -99,11 +82,9 @@ export default defineComponent({ }, mounted() { - this.height = this.$el.parentElement.offsetHeight + 'px'; - this.narrow = this.titleOnly || this.$el.parentElement.offsetWidth < 500; + this.narrow = this.$el.offsetWidth < 500; new ResizeObserver((entries, observer) => { - this.height = this.$el.parentElement.offsetHeight + 'px'; - this.narrow = this.titleOnly || this.$el.parentElement.offsetWidth < 500; + this.narrow = this.$el.offsetWidth < 500; }).observe(this.$el); }, @@ -161,8 +142,13 @@ export default defineComponent({ <style lang="scss" scoped> .fdidabkb { + --height: 60px; display: flex; + &.thin { + --height: 50px; + } + &.slim { text-align: center; @@ -220,6 +206,7 @@ export default defineComponent({ text-align: left; font-weight: bold; flex-shrink: 0; + margin-left: 24px; > .avatar { $size: 32px; diff --git a/src/client/ui/chat/index.vue b/src/client/ui/chat/index.vue index e8275def81..7090c9486a 100644 --- a/src/client/ui/chat/index.vue +++ b/src/client/ui/chat/index.vue @@ -74,7 +74,7 @@ <main class="main" @contextmenu.stop="onContextmenu"> <header class="header"> - <XHeader class="header" :info="pageInfo" :menu="menu" :center="false" :back-button="true" @back="back()" @click="onHeaderClick"/> + <XHeader class="header" :info="pageInfo" :menu="menu" :center="false" @click="onHeaderClick"/> </header> <router-view v-slot="{ Component }"> <transition :name="$store.state.animation ? 'page' : ''" mode="out-in" @enter="onTransition"> diff --git a/src/client/ui/chat/side.vue b/src/client/ui/chat/side.vue index ebf1cf9979..3e8904596d 100644 --- a/src/client/ui/chat/side.vue +++ b/src/client/ui/chat/side.vue @@ -1,7 +1,7 @@ <template> <div class="mrajymqm _narrow_" v-if="component"> <header class="header" @contextmenu.prevent.stop="onContextmenu"> - <XHeader class="title" :info="pageInfo" :center="false" :back-button="history.length > 0" @back="back()" :close-button="true" @close="close()"/> + <XHeader class="title" :info="pageInfo" :center="false"/> </header> <component :is="component" v-bind="props" :ref="changePage" class="body _flat_"/> </div> diff --git a/src/client/ui/deck/main-column.vue b/src/client/ui/deck/main-column.vue index 4c591022a5..42d963cda6 100644 --- a/src/client/ui/deck/main-column.vue +++ b/src/client/ui/deck/main-column.vue @@ -1,9 +1,12 @@ <template> <XColumn v-if="deckStore.state.alwaysShowMainColumn || $route.name !== 'index'" :column="column" :is-stacked="isStacked"> <template #header> - <XHeader :info="pageInfo" :back-button="true" @back="back()"/> + <template v-if="pageInfo"> + {{ pageInfo.title }} + </template> </template> + <XHeader :info="pageInfo"/> <router-view v-slot="{ Component }" class="_flat_"> <transition> <keep-alive :include="['timeline']"> diff --git a/src/client/ui/default.header.vue b/src/client/ui/default.header.vue index 6fbdd625c7..75c5c0c051 100644 --- a/src/client/ui/default.header.vue +++ b/src/client/ui/default.header.vue @@ -29,7 +29,7 @@ <MkAvatar :user="$i" class="avatar"/><MkAcct class="acct" :user="$i"/> </button> <div class="post" @click="post"> - <MkButton class="button" primary full> + <MkButton class="button" primary full rounded> <i class="fas fa-pencil-alt fa-fw"></i> </MkButton> </div> diff --git a/src/client/ui/default.side.vue b/src/client/ui/default.side.vue index 4d65779612..c453781e80 100644 --- a/src/client/ui/default.side.vue +++ b/src/client/ui/default.side.vue @@ -4,9 +4,10 @@ <header class="header" @contextmenu.prevent.stop="onContextmenu"> <button class="_button" @click="back()" v-if="history.length > 0"><i class="fas fa-chevron-left"></i></button> <button class="_button" style="pointer-events: none;" v-else><!-- マージンのバランスを取るためのダミー --></button> - <XHeader class="title" :info="pageInfo" :back-button="false"/> + <span class="title">{{ pageInfo.title }}</span> <button class="_button" @click="close()"><i class="fas fa-times"></i></button> </header> + <XHeader class="pageHeader" :info="pageInfo"/> <component :is="component" v-bind="props" :ref="changePage"/> </div> </div> diff --git a/src/client/ui/default.vue b/src/client/ui/default.vue index a5ec243e9e..4ceb3e1650 100644 --- a/src/client/ui/default.vue +++ b/src/client/ui/default.vue @@ -1,5 +1,5 @@ <template> -<div class="mk-app" :class="{ wallpaper, isMobile }"> +<div class="mk-app" :class="{ wallpaper, isMobile }" :style="`--headerHeight:` + headerHeight + 'px'"> <XHeaderMenu v-if="showMenuOnTop"/> <div class="columns" :class="{ fullView, withGlobalHeader: showMenuOnTop }"> @@ -14,7 +14,7 @@ <main class="main" @contextmenu.stop="onContextmenu" :style="{ background: pageInfo?.bg }"> <header class="header" @click="onHeaderClick"> - <XHeader :info="pageInfo" :back-button="true" @back="back()"/> + <XHeader :info="pageInfo" v-get-size="(w, h) => headerHeight = h" :thin="true"/> </header> <div class="content" :class="{ _flat_: !fullView }"> <router-view v-slot="{ Component }"> @@ -88,6 +88,7 @@ export default defineComponent({ data() { return { pageInfo: null, + headerHeight: 0, menuDef: menuDef, isMobile: window.innerWidth <= MOBILE_THRESHOLD, isDesktop: window.innerWidth >= DESKTOP_THRESHOLD, @@ -257,7 +258,6 @@ export default defineComponent({ } .mk-app { - $header-height: 50px; $ui-font-size: 1em; $widgets-hide-threshold: 1200px; $nav-icon-only-width: 78px; // TODO: どこかに集約したい @@ -330,7 +330,6 @@ export default defineComponent({ position: sticky; z-index: 1000; top: var(--globalHeaderHeight, 0px); - height: $header-height; -webkit-backdrop-filter: var(--blur, blur(32px)); backdrop-filter: var(--blur, blur(32px)); background-color: var(--header); @@ -338,11 +337,11 @@ export default defineComponent({ } > .content { - --stickyTop: calc(var(--globalHeaderHeight, 0px) + #{$header-height}); + --stickyTop: calc(var(--globalHeaderHeight, 0px) + var(--headerHeight)); } @media (max-width: 850px) { - padding-top: $header-height; + padding-top: var(--headerHeight); > .header { position: fixed; diff --git a/src/client/ui/universal.vue b/src/client/ui/universal.vue index ec9254b697..5b51752dda 100644 --- a/src/client/ui/universal.vue +++ b/src/client/ui/universal.vue @@ -1,10 +1,10 @@ <template> -<div class="mk-app" :class="{ wallpaper }"> +<div class="mk-app" :class="{ wallpaper }" :style="`--headerHeight:` + headerHeight + 'px'"> <XSidebar ref="nav" class="sidebar"/> <div class="contents" ref="contents" @contextmenu.stop="onContextmenu" :style="{ background: pageInfo?.bg }"> <header class="header" ref="header" @click="onHeaderClick" :style="{ background: pageInfo?.bg }"> - <XHeader :info="pageInfo" :back-button="true" @back="back()"/> + <XHeader v-if="!pageInfo?.hide" :info="pageInfo" v-get-size="(w, h) => headerHeight = h"/> </header> <main ref="main"> <div class="content"> @@ -86,6 +86,7 @@ export default defineComponent({ data() { return { pageInfo: null, + headerHeight: 0, isDesktop: window.innerWidth >= DESKTOP_THRESHOLD, menuDef: menuDef, navHidden: false, @@ -243,7 +244,6 @@ export default defineComponent({ } .mk-app { - $header-height: 58px; // TODO: どこかに集約したい $ui-font-size: 1em; // TODO: どこかに集約したい $widgets-hide-threshold: 1090px; @@ -263,19 +263,14 @@ export default defineComponent({ > .contents { width: 100%; min-width: 0; - --stickyTop: #{$header-height}; - padding-top: $header-height; + --stickyTop: var(--headerHeight); + padding-top: var(--headerHeight); background: var(--panel); > .header { position: fixed; z-index: 1000; top: 0; - height: $header-height; - width: 100%; - line-height: $header-height; - text-align: center; - font-weight: bold; //background-color: var(--panel); -webkit-backdrop-filter: var(--blur, blur(32px)); backdrop-filter: var(--blur, blur(32px)); @@ -287,13 +282,6 @@ export default defineComponent({ > main { min-width: 0; - > .content { - > * { - // ほんとは単に calc(100vh - #{$header-height}) と書きたいところだが... https://css-tricks.com/the-trick-to-viewport-units-on-mobile/ - min-height: calc((var(--vh, 1vh) * 100) - #{$header-height}); - } - } - > .spacer { height: 82px; From b3779875d0845cebc8cd0d8e937ce0979ba846f7 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 9 Oct 2021 00:46:52 +0900 Subject: [PATCH 049/107] :art: --- src/client/components/modal-page-window.vue | 46 ++++++++++++++++----- src/client/ui/_common_/header.vue | 8 ++-- 2 files changed, 40 insertions(+), 14 deletions(-) diff --git a/src/client/components/modal-page-window.vue b/src/client/components/modal-page-window.vue index f57516360d..31931a89b7 100644 --- a/src/client/components/modal-page-window.vue +++ b/src/client/components/modal-page-window.vue @@ -2,12 +2,16 @@ <MkModal ref="modal" @click="$emit('click')" @closed="$emit('closed')"> <div class="hrmcaedk _window _narrow_" :style="{ width: `${width}px`, height: (height ? `min(${height}px, 100%)` : '100%') }"> <div class="header" @contextmenu="onContextmenu"> - <span class="title"> - + <button v-if="history.length > 0" class="_button" @click="back()"><i class="fas fa-arrow-left"></i></button> + <span v-else style="display: inline-block; width: 20px"></span> + <span v-if="pageInfo" class="title"> + <i v-if="pageInfo.icon" class="icon" :class="pageInfo.icon"></i> + <span>{{ pageInfo.title }}</span> </span> + <button class="_button" @click="$refs.modal.close()"><i class="fas fa-times"></i></button> </div> <div class="body _flat_"> - <XHeader :info="pageInfo"/> + <XHeader v-if="!pageInfo?.hide" :info="pageInfo"/> <keep-alive> <component :is="component" v-bind="props" :ref="changePage"/> </keep-alive> @@ -173,19 +177,39 @@ export default defineComponent({ $height-narrow: 42px; display: flex; flex-shrink: 0; + height: $height; + line-height: $height; + font-weight: bold; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; box-shadow: 0px 1px var(--divider); + > button { + height: $height; + width: $height; + + &:hover { + color: var(--fgHighlighted); + } + } + + @media (max-width: 500px) { + height: $height-narrow; + line-height: $height-narrow; + padding-left: 16px; + + > button { + height: $height-narrow; + width: $height-narrow; + } + } + > .title { flex: 1; - height: $height; - font-weight: bold; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - @media (max-width: 500px) { - height: $height-narrow; - padding-left: 16px; + > .icon { + margin-right: 0.5em; } } } diff --git a/src/client/ui/_common_/header.vue b/src/client/ui/_common_/header.vue index 5405c43f8c..ca3d47dc76 100644 --- a/src/client/ui/_common_/header.vue +++ b/src/client/ui/_common_/header.vue @@ -82,10 +82,11 @@ export default defineComponent({ }, mounted() { - this.narrow = this.$el.offsetWidth < 500; + if (this.$el.parentElement == null) return; + this.narrow = this.$el.parentElement.offsetWidth < 500; new ResizeObserver((entries, observer) => { - this.narrow = this.$el.offsetWidth < 500; - }).observe(this.$el); + this.narrow = this.$el.parentElement.offsetWidth < 500; + }).observe(this.$el.parentElement); }, methods: { @@ -144,6 +145,7 @@ export default defineComponent({ .fdidabkb { --height: 60px; display: flex; + width: 100%; &.thin { --height: 50px; From 27c056cbbf6e85a033e85adbafd95a179a9db755 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 9 Oct 2021 12:33:08 +0900 Subject: [PATCH 050/107] tweak ui --- .../_common_ => components/global}/header.vue | 42 ++- src/client/components/index.ts | 2 + src/client/components/modal-page-window.vue | 3 - src/client/components/page-window.vue | 6 +- src/client/pages/announcements.vue | 34 +- src/client/pages/antenna-timeline.vue | 2 +- src/client/pages/emojis.vue | 12 +- src/client/pages/explore.vue | 132 +++---- src/client/pages/favorites.vue | 14 +- src/client/pages/featured.vue | 17 +- src/client/pages/federation.vue | 182 +++++----- src/client/pages/instance/ads.vue | 89 +++-- src/client/pages/instance/announcements.vue | 52 ++- src/client/pages/instance/emojis.vue | 30 +- src/client/pages/instance/index.vue | 6 +- src/client/pages/instance/users.vue | 29 +- src/client/pages/mentions.vue | 15 +- src/client/pages/messages.vue | 13 +- src/client/pages/messaging/index.vue | 71 ++-- src/client/pages/messaging/messaging-room.vue | 2 +- src/client/pages/my-lists/index.vue | 25 +- src/client/pages/my-lists/list.vue | 55 +-- src/client/pages/notifications.vue | 12 +- src/client/pages/pages.vue | 13 +- src/client/pages/search.vue | 15 +- src/client/pages/settings/index.vue | 10 +- src/client/pages/settings/profile.vue | 1 - src/client/pages/timeline.vue | 36 +- src/client/pages/user-list-timeline.vue | 2 +- src/client/pages/user/index.vue | 340 +++++++++--------- src/client/scripts/scroll.ts | 20 +- src/client/ui/chat/index.vue | 4 +- src/client/ui/chat/side.vue | 4 +- src/client/ui/deck/main-column.vue | 3 - src/client/ui/default.side.vue | 7 +- src/client/ui/default.vue | 45 +-- src/client/ui/universal.vue | 29 +- src/client/ui/zen.vue | 4 +- 38 files changed, 753 insertions(+), 625 deletions(-) rename src/client/{ui/_common_ => components/global}/header.vue (81%) diff --git a/src/client/ui/_common_/header.vue b/src/client/components/global/header.vue similarity index 81% rename from src/client/ui/_common_/header.vue rename to src/client/components/global/header.vue index ca3d47dc76..977912e2bd 100644 --- a/src/client/ui/_common_/header.vue +++ b/src/client/components/global/header.vue @@ -1,5 +1,5 @@ <template> -<div class="fdidabkb" :class="{ slim: narrow, thin }" :key="key"> +<div class="fdidabkb" :class="{ slim: narrow, thin }" :style="{ background: bg }" @click="onClick"> <template v-if="info"> <div class="titleContainer" @click="showTabsPopup"> <i v-if="info.icon" class="icon" :class="info.icon"></i> @@ -26,7 +26,10 @@ </template> <div class="buttons right"> <template v-if="info && info.actions && !narrow"> - <button v-for="action in info.actions" class="_button button" :class="{ highlighted: action.highlighted }" @click.stop="action.handler" @touchstart="preventDrag" v-tooltip="action.text"><i :class="action.icon"></i></button> + <template v-for="action in info.actions"> + <MkButton class="fullButton" v-if="action.asFullButton" @click.stop="action.handler" primary><i :class="action.icon" style="margin-right: 6px;"></i>{{ action.text }}</MkButton> + <button v-else class="_button button" :class="{ highlighted: action.highlighted }" @click.stop="action.handler" @touchstart="preventDrag" v-tooltip="action.text"><i :class="action.icon"></i></button> + </template> </template> <button v-if="shouldShowMenu" class="_button button" @click.stop="showMenu" @touchstart="preventDrag" v-tooltip="$ts.menu"><i class="fas fa-ellipsis-h"></i></button> </div> @@ -35,10 +38,17 @@ <script lang="ts"> import { defineComponent } from 'vue'; +import * as tinycolor from 'tinycolor2'; import { popupMenu } from '@client/os'; import { url } from '@client/config'; +import { scrollToTop } from '@client/scripts/scroll'; +import MkButton from '@client/components/ui/button.vue'; export default defineComponent({ + components: { + MkButton + }, + props: { info: { required: true @@ -54,9 +64,9 @@ export default defineComponent({ data() { return { + bg: null, narrow: false, height: 0, - key: 0, }; }, @@ -75,13 +85,12 @@ export default defineComponent({ } }, - watch: { - info() { - this.key++; - }, - }, - mounted() { + const rawBg = this.info?.bg || 'var(--bg)'; + const bg = tinycolor(rawBg.startsWith('var(') ? getComputedStyle(document.documentElement).getPropertyValue(rawBg.slice(4, -1)) : rawBg); + bg.setAlpha(0.85); + this.bg = bg.toRgbString(); + if (this.$el.parentElement == null) return; this.narrow = this.$el.parentElement.offsetWidth < 500; new ResizeObserver((entries, observer) => { @@ -136,6 +145,10 @@ export default defineComponent({ preventDrag(ev) { ev.stopPropagation(); + }, + + onClick(ev) { + scrollToTop(this.$el, { behavior: 'smooth' }); } } }); @@ -145,7 +158,12 @@ export default defineComponent({ .fdidabkb { --height: 60px; display: flex; + position: sticky; + top: var(--stickyTop, 0); + z-index: 1000; width: 100%; + -webkit-backdrop-filter: var(--blur, blur(15px)); + backdrop-filter: var(--blur, blur(15px)); &.thin { --height: 50px; @@ -198,6 +216,12 @@ export default defineComponent({ color: var(--accent); } } + + > .fullButton { + & + .fullButton { + margin-left: 12px; + } + } } > .titleContainer { diff --git a/src/client/components/index.ts b/src/client/components/index.ts index 8b914c5eec..8f071dfce1 100644 --- a/src/client/components/index.ts +++ b/src/client/components/index.ts @@ -13,6 +13,7 @@ import i18n from './global/i18n'; import loading from './global/loading.vue'; import error from './global/error.vue'; import ad from './global/ad.vue'; +import header from './global/header.vue'; export default function(app: App) { app.component('I18n', i18n); @@ -28,4 +29,5 @@ export default function(app: App) { app.component('MkLoading', loading); app.component('MkError', error); app.component('MkAd', ad); + app.component('MkHeader', header); } diff --git a/src/client/components/modal-page-window.vue b/src/client/components/modal-page-window.vue index 31931a89b7..c20e2b3087 100644 --- a/src/client/components/modal-page-window.vue +++ b/src/client/components/modal-page-window.vue @@ -11,7 +11,6 @@ <button class="_button" @click="$refs.modal.close()"><i class="fas fa-times"></i></button> </div> <div class="body _flat_"> - <XHeader v-if="!pageInfo?.hide" :info="pageInfo"/> <keep-alive> <component :is="component" v-bind="props" :ref="changePage"/> </keep-alive> @@ -23,7 +22,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; import MkModal from '@client/components/ui/modal.vue'; -import XHeader from '@client/ui/_common_/header.vue'; import { popout } from '@client/scripts/popout'; import copyToClipboard from '@client/scripts/copy-to-clipboard'; import { resolve } from '@client/router'; @@ -34,7 +32,6 @@ import * as os from '@client/os'; export default defineComponent({ components: { MkModal, - XHeader, }, inject: { diff --git a/src/client/components/page-window.vue b/src/client/components/page-window.vue index a8a8b76100..ec4c03c3d8 100644 --- a/src/client/components/page-window.vue +++ b/src/client/components/page-window.vue @@ -9,14 +9,14 @@ > <template #header> <template v-if="pageInfo"> - {{ pageInfo.title }} + <i v-if="pageInfo.icon" class="icon" :class="pageInfo.icon" style="margin-right: 0.5em;"></i> + <span>{{ pageInfo.title }}</span> </template> </template> <template #headerLeft> <button v-if="history.length > 0" class="_button" @click="back()"><i class="fas fa-arrow-left"></i></button> </template> <div class="yrolvcoq _flat_"> - <XHeader :info="pageInfo"/> <component :is="component" v-bind="props" :ref="changePage"/> </div> </XWindow> @@ -25,7 +25,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; import XWindow from '@client/components/ui/window.vue'; -import XHeader from '@client/ui/_common_/header.vue'; import { popout } from '@client/scripts/popout'; import copyToClipboard from '@client/scripts/copy-to-clipboard'; import { resolve } from '@client/router'; @@ -35,7 +34,6 @@ import * as symbols from '@client/symbols'; export default defineComponent({ components: { XWindow, - XHeader, }, inject: { diff --git a/src/client/pages/announcements.vue b/src/client/pages/announcements.vue index 327fa9b1f6..6a0cbd67ba 100644 --- a/src/client/pages/announcements.vue +++ b/src/client/pages/announcements.vue @@ -1,17 +1,20 @@ <template> -<div class="_section"> - <MkPagination :pagination="pagination" #default="{items}" class="ruryvtyk _content"> - <section class="_card announcement _gap" v-for="(announcement, i) in items" :key="announcement.id"> - <div class="_title"><span v-if="$i && !announcement.isRead">🆕 </span>{{ announcement.title }}</div> - <div class="_content"> - <Mfm :text="announcement.text"/> - <img v-if="announcement.imageUrl" :src="announcement.imageUrl"/> - </div> - <div class="_footer" v-if="$i && !announcement.isRead"> - <MkButton @click="read(items, announcement, i)" primary><i class="fas fa-check"></i> {{ $ts.gotIt }}</MkButton> - </div> - </section> - </MkPagination> +<div> + <MkHeader :info="header"/> + <div class="_section"> + <MkPagination :pagination="pagination" #default="{items}" class="ruryvtyk _content"> + <section class="_card announcement _gap" v-for="(announcement, i) in items" :key="announcement.id"> + <div class="_title"><span v-if="$i && !announcement.isRead">🆕 </span>{{ announcement.title }}</div> + <div class="_content"> + <Mfm :text="announcement.text"/> + <img v-if="announcement.imageUrl" :src="announcement.imageUrl"/> + </div> + <div class="_footer" v-if="$i && !announcement.isRead"> + <MkButton @click="read(items, announcement, i)" primary><i class="fas fa-check"></i> {{ $ts.gotIt }}</MkButton> + </div> + </section> + </MkPagination> + </div> </div> </template> @@ -35,6 +38,11 @@ export default defineComponent({ icon: 'fas fa-broadcast-tower', bg: 'var(--bg)', }, + header: { + title: this.$ts.announcements, + icon: 'fas fa-broadcast-tower', + bg: 'var(--bg)', + }, pagination: { endpoint: 'announcements', limit: 10, diff --git a/src/client/pages/antenna-timeline.vue b/src/client/pages/antenna-timeline.vue index 425bec6987..c99124dbdc 100644 --- a/src/client/pages/antenna-timeline.vue +++ b/src/client/pages/antenna-timeline.vue @@ -89,7 +89,7 @@ export default defineComponent({ }, top() { - scroll(this.$el, 0); + scroll(this.$el, { top: 0 }); }, async timetravel() { diff --git a/src/client/pages/emojis.vue b/src/client/pages/emojis.vue index 8918de2338..d61fd25d3c 100644 --- a/src/client/pages/emojis.vue +++ b/src/client/pages/emojis.vue @@ -1,6 +1,9 @@ <template> -<div :class="$style.root"> - <XCategory v-if="tab === 'category'"/> +<div> + <MkHeader :info="header"/> + <div :class="$style.root"> + <XCategory v-if="tab === 'category'"/> + </div> </div> </template> @@ -22,6 +25,11 @@ export default defineComponent({ icon: 'fas fa-laugh', bg: 'var(--bg)', })), + header: computed(() => ({ + title: this.$ts.customEmojis, + icon: 'fas fa-laugh', + bg: 'var(--bg)', + })), tab: 'category', } }, diff --git a/src/client/pages/explore.vue b/src/client/pages/explore.vue index 7e0e8ea27b..15ebf8efad 100644 --- a/src/client/pages/explore.vue +++ b/src/client/pages/explore.vue @@ -1,72 +1,76 @@ <template> -<div class="lznhrdub _root"> - <div> - <div class="_isolated"> - <MkInput v-model="query" :debounce="true" type="search"> - <template #prefix><i class="fas fa-search"></i></template> - <template #label>{{ $ts.searchUser }}</template> - </MkInput> - </div> +<div> + <MkHeader :info="header"/> - <XUserList v-if="query" class="_gap" :pagination="searchPagination" ref="search"/> - - <div class="localfedi7 _block _isolated" v-if="meta && stats && tag == null" :style="{ backgroundImage: meta.bannerUrl ? `url(${meta.bannerUrl})` : null }"> - <header><span>{{ $t('explore', { host: meta.name || 'Misskey' }) }}</span></header> - <div><span>{{ $t('exploreUsersCount', { count: num(stats.originalUsersCount) }) }}</span></div> - </div> - - <template v-if="tag == null"> - <MkFolder class="_gap" persist-key="explore-pinned-users"> - <template #header><i class="fas fa-bookmark fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.pinnedUsers }}</template> - <XUserList :pagination="pinnedUsers"/> - </MkFolder> - <MkFolder class="_gap" persist-key="explore-popular-users"> - <template #header><i class="fas fa-chart-line fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.popularUsers }}</template> - <XUserList :pagination="popularUsers"/> - </MkFolder> - <MkFolder class="_gap" persist-key="explore-recently-updated-users"> - <template #header><i class="fas fa-comment-alt fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.recentlyUpdatedUsers }}</template> - <XUserList :pagination="recentlyUpdatedUsers"/> - </MkFolder> - <MkFolder class="_gap" persist-key="explore-recently-registered-users"> - <template #header><i class="fas fa-plus fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.recentlyRegisteredUsers }}</template> - <XUserList :pagination="recentlyRegisteredUsers"/> - </MkFolder> - </template> - </div> - <div> - <div class="localfedi7 _block _isolated" v-if="tag == null" :style="{ backgroundImage: `url(/static-assets/client/fedi.jpg)` }"> - <header><span>{{ $ts.exploreFediverse }}</span></header> - </div> - - <MkFolder :foldable="true" :expanded="false" ref="tags" class="_gap"> - <template #header><i class="fas fa-hashtag fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.popularTags }}</template> - - <div class="vxjfqztj"> - <MkA v-for="tag in tagsLocal" :to="`/explore/tags/${tag.tag}`" :key="'local:' + tag.tag" class="local">{{ tag.tag }}</MkA> - <MkA v-for="tag in tagsRemote" :to="`/explore/tags/${tag.tag}`" :key="'remote:' + tag.tag">{{ tag.tag }}</MkA> + <div class="lznhrdub _root"> + <div> + <div class="_isolated"> + <MkInput v-model="query" :debounce="true" type="search"> + <template #prefix><i class="fas fa-search"></i></template> + <template #label>{{ $ts.searchUser }}</template> + </MkInput> </div> - </MkFolder> - <MkFolder v-if="tag != null" :key="`${tag}`" class="_gap"> - <template #header><i class="fas fa-hashtag fa-fw" style="margin-right: 0.5em;"></i>{{ tag }}</template> - <XUserList :pagination="tagUsers"/> - </MkFolder> + <XUserList v-if="query" class="_gap" :pagination="searchPagination" ref="search"/> - <template v-if="tag == null"> - <MkFolder class="_gap"> - <template #header><i class="fas fa-chart-line fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.popularUsers }}</template> - <XUserList :pagination="popularUsersF"/> + <div class="localfedi7 _block _isolated" v-if="meta && stats && tag == null" :style="{ backgroundImage: meta.bannerUrl ? `url(${meta.bannerUrl})` : null }"> + <header><span>{{ $t('explore', { host: meta.name || 'Misskey' }) }}</span></header> + <div><span>{{ $t('exploreUsersCount', { count: num(stats.originalUsersCount) }) }}</span></div> + </div> + + <template v-if="tag == null"> + <MkFolder class="_gap" persist-key="explore-pinned-users"> + <template #header><i class="fas fa-bookmark fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.pinnedUsers }}</template> + <XUserList :pagination="pinnedUsers"/> + </MkFolder> + <MkFolder class="_gap" persist-key="explore-popular-users"> + <template #header><i class="fas fa-chart-line fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.popularUsers }}</template> + <XUserList :pagination="popularUsers"/> + </MkFolder> + <MkFolder class="_gap" persist-key="explore-recently-updated-users"> + <template #header><i class="fas fa-comment-alt fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.recentlyUpdatedUsers }}</template> + <XUserList :pagination="recentlyUpdatedUsers"/> + </MkFolder> + <MkFolder class="_gap" persist-key="explore-recently-registered-users"> + <template #header><i class="fas fa-plus fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.recentlyRegisteredUsers }}</template> + <XUserList :pagination="recentlyRegisteredUsers"/> + </MkFolder> + </template> + </div> + <div> + <div class="localfedi7 _block _isolated" v-if="tag == null" :style="{ backgroundImage: `url(/static-assets/client/fedi.jpg)` }"> + <header><span>{{ $ts.exploreFediverse }}</span></header> + </div> + + <MkFolder :foldable="true" :expanded="false" ref="tags" class="_gap"> + <template #header><i class="fas fa-hashtag fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.popularTags }}</template> + + <div class="vxjfqztj"> + <MkA v-for="tag in tagsLocal" :to="`/explore/tags/${tag.tag}`" :key="'local:' + tag.tag" class="local">{{ tag.tag }}</MkA> + <MkA v-for="tag in tagsRemote" :to="`/explore/tags/${tag.tag}`" :key="'remote:' + tag.tag">{{ tag.tag }}</MkA> + </div> </MkFolder> - <MkFolder class="_gap"> - <template #header><i class="fas fa-comment-alt fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.recentlyUpdatedUsers }}</template> - <XUserList :pagination="recentlyUpdatedUsersF"/> + + <MkFolder v-if="tag != null" :key="`${tag}`" class="_gap"> + <template #header><i class="fas fa-hashtag fa-fw" style="margin-right: 0.5em;"></i>{{ tag }}</template> + <XUserList :pagination="tagUsers"/> </MkFolder> - <MkFolder class="_gap"> - <template #header><i class="fas fa-rocket fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.recentlyDiscoveredUsers }}</template> - <XUserList :pagination="recentlyRegisteredUsersF"/> - </MkFolder> - </template> + + <template v-if="tag == null"> + <MkFolder class="_gap"> + <template #header><i class="fas fa-chart-line fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.popularUsers }}</template> + <XUserList :pagination="popularUsersF"/> + </MkFolder> + <MkFolder class="_gap"> + <template #header><i class="fas fa-comment-alt fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.recentlyUpdatedUsers }}</template> + <XUserList :pagination="recentlyUpdatedUsersF"/> + </MkFolder> + <MkFolder class="_gap"> + <template #header><i class="fas fa-rocket fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.recentlyDiscoveredUsers }}</template> + <XUserList :pagination="recentlyRegisteredUsersF"/> + </MkFolder> + </template> + </div> </div> </div> </template> @@ -100,6 +104,10 @@ export default defineComponent({ title: this.$ts.explore, icon: 'fas fa-hashtag' }, + header: { + title: this.$ts.explore, + icon: 'fas fa-hashtag' + }, pinnedUsers: { endpoint: 'pinned-users' }, popularUsers: { endpoint: 'users', limit: 10, noPaging: true, params: { state: 'alive', diff --git a/src/client/pages/favorites.vue b/src/client/pages/favorites.vue index f13723c2d1..bed78d1dbe 100644 --- a/src/client/pages/favorites.vue +++ b/src/client/pages/favorites.vue @@ -1,7 +1,10 @@ <template> -<div class="jmelgwjh"> - <div class="body"> - <XNotes class="notes" :pagination="pagination" :detail="true" :prop="'note'" @before="before()" @after="after()"/> +<div> + <MkHeader :info="header"/> + <div class="jmelgwjh"> + <div class="body"> + <XNotes class="notes" :pagination="pagination" :detail="true" :prop="'note'" @before="before()" @after="after()"/> + </div> </div> </div> </template> @@ -25,6 +28,11 @@ export default defineComponent({ icon: 'fas fa-star', bg: 'var(--bg)', }, + header: { + title: this.$ts.favorites, + icon: 'fas fa-star', + bg: 'var(--bg)', + }, pagination: { endpoint: 'i/favorites', limit: 10, diff --git a/src/client/pages/featured.vue b/src/client/pages/featured.vue index 21818ba617..5d8da54541 100644 --- a/src/client/pages/featured.vue +++ b/src/client/pages/featured.vue @@ -1,6 +1,9 @@ <template> -<div class="_section"> - <XNotes class="_content" ref="notes" :pagination="pagination" @before="before" @after="after"/> +<div> + <MkHeader :info="header"/> + <div class="_section"> + <XNotes class="_content" ref="notes" :pagination="pagination" @before="before" @after="after"/> + </div> </div> </template> @@ -19,12 +22,18 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.featured, - icon: 'fas fa-fire-alt' + icon: 'fas fa-fire-alt', + bg: 'var(--bg)', + }, + header: { + title: this.$ts.featured, + icon: 'fas fa-fire-alt', + bg: 'var(--bg)', }, pagination: { endpoint: 'notes/featured', limit: 10, - offsetMode: true + offsetMode: true, }, }; }, diff --git a/src/client/pages/federation.vue b/src/client/pages/federation.vue index eae6a05367..ae0aed4cc7 100644 --- a/src/client/pages/federation.vue +++ b/src/client/pages/federation.vue @@ -1,95 +1,98 @@ <template> -<div class="taeiyria"> - <div class="query"> - <MkInput v-model="host" :debounce="true" class=""> - <template #prefix><i class="fas fa-search"></i></template> - <template #label>{{ $ts.host }}</template> - </MkInput> - <div class="_inputSplit"> - <MkSelect v-model="state"> - <template #label>{{ $ts.state }}</template> - <option value="all">{{ $ts.all }}</option> - <option value="federating">{{ $ts.federating }}</option> - <option value="subscribing">{{ $ts.subscribing }}</option> - <option value="publishing">{{ $ts.publishing }}</option> - <option value="suspended">{{ $ts.suspended }}</option> - <option value="blocked">{{ $ts.blocked }}</option> - <option value="notResponding">{{ $ts.notResponding }}</option> - </MkSelect> - <MkSelect v-model="sort"> - <template #label>{{ $ts.sort }}</template> - <option value="+pubSub">{{ $ts.pubSub }} ({{ $ts.descendingOrder }})</option> - <option value="-pubSub">{{ $ts.pubSub }} ({{ $ts.ascendingOrder }})</option> - <option value="+notes">{{ $ts.notes }} ({{ $ts.descendingOrder }})</option> - <option value="-notes">{{ $ts.notes }} ({{ $ts.ascendingOrder }})</option> - <option value="+users">{{ $ts.users }} ({{ $ts.descendingOrder }})</option> - <option value="-users">{{ $ts.users }} ({{ $ts.ascendingOrder }})</option> - <option value="+following">{{ $ts.following }} ({{ $ts.descendingOrder }})</option> - <option value="-following">{{ $ts.following }} ({{ $ts.ascendingOrder }})</option> - <option value="+followers">{{ $ts.followers }} ({{ $ts.descendingOrder }})</option> - <option value="-followers">{{ $ts.followers }} ({{ $ts.ascendingOrder }})</option> - <option value="+caughtAt">{{ $ts.registeredAt }} ({{ $ts.descendingOrder }})</option> - <option value="-caughtAt">{{ $ts.registeredAt }} ({{ $ts.ascendingOrder }})</option> - <option value="+lastCommunicatedAt">{{ $ts.lastCommunication }} ({{ $ts.descendingOrder }})</option> - <option value="-lastCommunicatedAt">{{ $ts.lastCommunication }} ({{ $ts.ascendingOrder }})</option> - <option value="+driveUsage">{{ $ts.driveUsage }} ({{ $ts.descendingOrder }})</option> - <option value="-driveUsage">{{ $ts.driveUsage }} ({{ $ts.ascendingOrder }})</option> - <option value="+driveFiles">{{ $ts.driveFilesCount }} ({{ $ts.descendingOrder }})</option> - <option value="-driveFiles">{{ $ts.driveFilesCount }} ({{ $ts.ascendingOrder }})</option> - </MkSelect> +<div> + <MkHeader :info="header"/> + <div class="taeiyria"> + <div class="query"> + <MkInput v-model="host" :debounce="true" class=""> + <template #prefix><i class="fas fa-search"></i></template> + <template #label>{{ $ts.host }}</template> + </MkInput> + <div class="_inputSplit"> + <MkSelect v-model="state"> + <template #label>{{ $ts.state }}</template> + <option value="all">{{ $ts.all }}</option> + <option value="federating">{{ $ts.federating }}</option> + <option value="subscribing">{{ $ts.subscribing }}</option> + <option value="publishing">{{ $ts.publishing }}</option> + <option value="suspended">{{ $ts.suspended }}</option> + <option value="blocked">{{ $ts.blocked }}</option> + <option value="notResponding">{{ $ts.notResponding }}</option> + </MkSelect> + <MkSelect v-model="sort"> + <template #label>{{ $ts.sort }}</template> + <option value="+pubSub">{{ $ts.pubSub }} ({{ $ts.descendingOrder }})</option> + <option value="-pubSub">{{ $ts.pubSub }} ({{ $ts.ascendingOrder }})</option> + <option value="+notes">{{ $ts.notes }} ({{ $ts.descendingOrder }})</option> + <option value="-notes">{{ $ts.notes }} ({{ $ts.ascendingOrder }})</option> + <option value="+users">{{ $ts.users }} ({{ $ts.descendingOrder }})</option> + <option value="-users">{{ $ts.users }} ({{ $ts.ascendingOrder }})</option> + <option value="+following">{{ $ts.following }} ({{ $ts.descendingOrder }})</option> + <option value="-following">{{ $ts.following }} ({{ $ts.ascendingOrder }})</option> + <option value="+followers">{{ $ts.followers }} ({{ $ts.descendingOrder }})</option> + <option value="-followers">{{ $ts.followers }} ({{ $ts.ascendingOrder }})</option> + <option value="+caughtAt">{{ $ts.registeredAt }} ({{ $ts.descendingOrder }})</option> + <option value="-caughtAt">{{ $ts.registeredAt }} ({{ $ts.ascendingOrder }})</option> + <option value="+lastCommunicatedAt">{{ $ts.lastCommunication }} ({{ $ts.descendingOrder }})</option> + <option value="-lastCommunicatedAt">{{ $ts.lastCommunication }} ({{ $ts.ascendingOrder }})</option> + <option value="+driveUsage">{{ $ts.driveUsage }} ({{ $ts.descendingOrder }})</option> + <option value="-driveUsage">{{ $ts.driveUsage }} ({{ $ts.ascendingOrder }})</option> + <option value="+driveFiles">{{ $ts.driveFilesCount }} ({{ $ts.descendingOrder }})</option> + <option value="-driveFiles">{{ $ts.driveFilesCount }} ({{ $ts.ascendingOrder }})</option> + </MkSelect> + </div> </div> - </div> - <MkPagination :pagination="pagination" #default="{items}" ref="instances" :key="host + state"> - <div class="dqokceoi"> - <MkA class="instance" v-for="instance in items" :key="instance.id" :to="`/instance-info/${instance.host}`"> - <div class="host"><img :src="instance.faviconUrl">{{ instance.host }}</div> - <div class="table"> - <div class="cell"> - <div class="key">{{ $ts.registeredAt }}</div> - <div class="value"><MkTime :time="instance.caughtAt"/></div> + <MkPagination :pagination="pagination" #default="{items}" ref="instances" :key="host + state"> + <div class="dqokceoi"> + <MkA class="instance" v-for="instance in items" :key="instance.id" :to="`/instance-info/${instance.host}`"> + <div class="host"><img :src="instance.faviconUrl">{{ instance.host }}</div> + <div class="table"> + <div class="cell"> + <div class="key">{{ $ts.registeredAt }}</div> + <div class="value"><MkTime :time="instance.caughtAt"/></div> + </div> + <div class="cell"> + <div class="key">{{ $ts.software }}</div> + <div class="value">{{ instance.softwareName || `(${$ts.unknown})` }}</div> + </div> + <div class="cell"> + <div class="key">{{ $ts.version }}</div> + <div class="value">{{ instance.softwareVersion || `(${$ts.unknown})` }}</div> + </div> + <div class="cell"> + <div class="key">{{ $ts.users }}</div> + <div class="value">{{ instance.usersCount }}</div> + </div> + <div class="cell"> + <div class="key">{{ $ts.notes }}</div> + <div class="value">{{ instance.notesCount }}</div> + </div> + <div class="cell"> + <div class="key">{{ $ts.sent }}</div> + <div class="value"><MkTime v-if="instance.latestRequestSentAt" :time="instance.latestRequestSentAt"/><span v-else>N/A</span></div> + </div> + <div class="cell"> + <div class="key">{{ $ts.received }}</div> + <div class="value"><MkTime v-if="instance.latestRequestReceivedAt" :time="instance.latestRequestReceivedAt"/><span v-else>N/A</span></div> + </div> </div> - <div class="cell"> - <div class="key">{{ $ts.software }}</div> - <div class="value">{{ instance.softwareName || `(${$ts.unknown})` }}</div> + <div class="footer"> + <span class="status" :class="getStatus(instance)">{{ getStatus(instance) }}</span> + <span class="pubSub"> + <span class="sub" v-if="instance.followersCount > 0"><i class="fas fa-caret-down icon"></i>Sub</span> + <span class="sub" v-else><i class="fas fa-caret-down icon"></i>-</span> + <span class="pub" v-if="instance.followingCount > 0"><i class="fas fa-caret-up icon"></i>Pub</span> + <span class="pub" v-else><i class="fas fa-caret-up icon"></i>-</span> + </span> + <span class="right"> + <span class="latestStatus">{{ instance.latestStatus || '-' }}</span> + <span class="lastCommunicatedAt"><MkTime :time="instance.lastCommunicatedAt"/></span> + </span> </div> - <div class="cell"> - <div class="key">{{ $ts.version }}</div> - <div class="value">{{ instance.softwareVersion || `(${$ts.unknown})` }}</div> - </div> - <div class="cell"> - <div class="key">{{ $ts.users }}</div> - <div class="value">{{ instance.usersCount }}</div> - </div> - <div class="cell"> - <div class="key">{{ $ts.notes }}</div> - <div class="value">{{ instance.notesCount }}</div> - </div> - <div class="cell"> - <div class="key">{{ $ts.sent }}</div> - <div class="value"><MkTime v-if="instance.latestRequestSentAt" :time="instance.latestRequestSentAt"/><span v-else>N/A</span></div> - </div> - <div class="cell"> - <div class="key">{{ $ts.received }}</div> - <div class="value"><MkTime v-if="instance.latestRequestReceivedAt" :time="instance.latestRequestReceivedAt"/><span v-else>N/A</span></div> - </div> - </div> - <div class="footer"> - <span class="status" :class="getStatus(instance)">{{ getStatus(instance) }}</span> - <span class="pubSub"> - <span class="sub" v-if="instance.followersCount > 0"><i class="fas fa-caret-down icon"></i>Sub</span> - <span class="sub" v-else><i class="fas fa-caret-down icon"></i>-</span> - <span class="pub" v-if="instance.followingCount > 0"><i class="fas fa-caret-up icon"></i>Pub</span> - <span class="pub" v-else><i class="fas fa-caret-up icon"></i>-</span> - </span> - <span class="right"> - <span class="latestStatus">{{ instance.latestStatus || '-' }}</span> - <span class="lastCommunicatedAt"><MkTime :time="instance.lastCommunicatedAt"/></span> - </span> - </div> - </MkA> - </div> - </MkPagination> + </MkA> + </div> + </MkPagination> + </div> </div> </template> @@ -119,6 +122,11 @@ export default defineComponent({ icon: 'fas fa-globe', bg: 'var(--bg)', }, + header: { + title: this.$ts.federation, + icon: 'fas fa-globe', + bg: 'var(--bg)', + }, host: '', state: 'federating', sort: '+pubSub', diff --git a/src/client/pages/instance/ads.vue b/src/client/pages/instance/ads.vue index c54c1c0280..e776f99a4c 100644 --- a/src/client/pages/instance/ads.vue +++ b/src/client/pages/instance/ads.vue @@ -1,43 +1,45 @@ <template> -<div class="uqshojas"> - <MkButton @click="add()" primary style="margin: 0 auto 16px auto;"><i class="fas fa-plus"></i> {{ $ts.add }}</MkButton> - <section class="_card _gap ads" v-for="ad in ads"> - <div class="_content ad"> - <MkAd v-if="ad.url" :specify="ad"/> - <MkInput v-model="ad.url" type="url"> - <template #label>URL</template> - </MkInput> - <MkInput v-model="ad.imageUrl"> - <template #label>{{ $ts.imageUrl }}</template> - </MkInput> - <div style="margin: 32px 0;"> - <MkRadio v-model="ad.place" value="square">square</MkRadio> - <MkRadio v-model="ad.place" value="horizontal">horizontal</MkRadio> - <MkRadio v-model="ad.place" value="horizontal-big">horizontal-big</MkRadio> +<div> + <MkHeader :info="header"/> + <div class="uqshojas"> + <section class="_card _gap ads" v-for="ad in ads"> + <div class="_content ad"> + <MkAd v-if="ad.url" :specify="ad"/> + <MkInput v-model="ad.url" type="url"> + <template #label>URL</template> + </MkInput> + <MkInput v-model="ad.imageUrl"> + <template #label>{{ $ts.imageUrl }}</template> + </MkInput> + <div style="margin: 32px 0;"> + <MkRadio v-model="ad.place" value="square">square</MkRadio> + <MkRadio v-model="ad.place" value="horizontal">horizontal</MkRadio> + <MkRadio v-model="ad.place" value="horizontal-big">horizontal-big</MkRadio> + </div> + <!-- + <div style="margin: 32px 0;"> + {{ $ts.priority }} + <MkRadio v-model="ad.priority" value="high">{{ $ts.high }}</MkRadio> + <MkRadio v-model="ad.priority" value="middle">{{ $ts.middle }}</MkRadio> + <MkRadio v-model="ad.priority" value="low">{{ $ts.low }}</MkRadio> + </div> + --> + <MkInput v-model="ad.ratio" type="number"> + <template #label>{{ $ts.ratio }}</template> + </MkInput> + <MkInput v-model="ad.expiresAt" type="date"> + <template #label>{{ $ts.expiration }}</template> + </MkInput> + <MkTextarea v-model="ad.memo"> + <template #label>{{ $ts.memo }}</template> + </MkTextarea> + <div class="buttons"> + <MkButton class="button" inline @click="save(ad)" primary><i class="fas fa-save"></i> {{ $ts.save }}</MkButton> + <MkButton class="button" inline @click="remove(ad)" danger><i class="fas fa-trash-alt"></i> {{ $ts.remove }}</MkButton> + </div> </div> - <!-- - <div style="margin: 32px 0;"> - {{ $ts.priority }} - <MkRadio v-model="ad.priority" value="high">{{ $ts.high }}</MkRadio> - <MkRadio v-model="ad.priority" value="middle">{{ $ts.middle }}</MkRadio> - <MkRadio v-model="ad.priority" value="low">{{ $ts.low }}</MkRadio> - </div> - --> - <MkInput v-model="ad.ratio" type="number"> - <template #label>{{ $ts.ratio }}</template> - </MkInput> - <MkInput v-model="ad.expiresAt" type="date"> - <template #label>{{ $ts.expiration }}</template> - </MkInput> - <MkTextarea v-model="ad.memo"> - <template #label>{{ $ts.memo }}</template> - </MkTextarea> - <div class="buttons"> - <MkButton class="button" inline @click="save(ad)" primary><i class="fas fa-save"></i> {{ $ts.save }}</MkButton> - <MkButton class="button" inline @click="remove(ad)" danger><i class="fas fa-trash-alt"></i> {{ $ts.remove }}</MkButton> - </div> - </div> - </section> + </section> + </div> </div> </template> @@ -67,6 +69,17 @@ export default defineComponent({ icon: 'fas fa-audio-description', bg: 'var(--bg)', }, + header: { + title: this.$ts.ads, + icon: 'fas fa-audio-description', + bg: 'var(--bg)', + actions: [{ + asFullButton: true, + icon: 'fas fa-plus', + text: this.$ts.add, + handler: this.add, + }], + }, ads: [], } }, diff --git a/src/client/pages/instance/announcements.vue b/src/client/pages/instance/announcements.vue index e4f7334c05..78637c095a 100644 --- a/src/client/pages/instance/announcements.vue +++ b/src/client/pages/instance/announcements.vue @@ -1,24 +1,27 @@ <template> -<div class="ztgjmzrw"> - <MkButton @click="add()" primary style="margin: 0 auto 16px auto;"><i class="fas fa-plus"></i> {{ $ts.add }}</MkButton> - <section class="_card _gap announcements" v-for="announcement in announcements"> - <div class="_content announcement"> - <MkInput v-model="announcement.title"> - <template #label>{{ $ts.title }}</template> - </MkInput> - <MkTextarea v-model="announcement.text"> - <template #label>{{ $ts.text }}</template> - </MkTextarea> - <MkInput v-model="announcement.imageUrl"> - <template #label>{{ $ts.imageUrl }}</template> - </MkInput> - <p v-if="announcement.reads">{{ $t('nUsersRead', { n: announcement.reads }) }}</p> - <div class="buttons"> - <MkButton class="button" inline @click="save(announcement)" primary><i class="fas fa-save"></i> {{ $ts.save }}</MkButton> - <MkButton class="button" inline @click="remove(announcement)"><i class="fas fa-trash-alt"></i> {{ $ts.remove }}</MkButton> +<div> + <MkHeader :info="header"/> + + <div class="ztgjmzrw"> + <section class="_card _gap announcements" v-for="announcement in announcements"> + <div class="_content announcement"> + <MkInput v-model="announcement.title"> + <template #label>{{ $ts.title }}</template> + </MkInput> + <MkTextarea v-model="announcement.text"> + <template #label>{{ $ts.text }}</template> + </MkTextarea> + <MkInput v-model="announcement.imageUrl"> + <template #label>{{ $ts.imageUrl }}</template> + </MkInput> + <p v-if="announcement.reads">{{ $t('nUsersRead', { n: announcement.reads }) }}</p> + <div class="buttons"> + <MkButton class="button" inline @click="save(announcement)" primary><i class="fas fa-save"></i> {{ $ts.save }}</MkButton> + <MkButton class="button" inline @click="remove(announcement)"><i class="fas fa-trash-alt"></i> {{ $ts.remove }}</MkButton> + </div> </div> - </div> - </section> + </section> + </div> </div> </template> @@ -46,6 +49,17 @@ export default defineComponent({ icon: 'fas fa-broadcast-tower', bg: 'var(--bg)', }, + header: { + title: this.$ts.announcements, + icon: 'fas fa-broadcast-tower', + bg: 'var(--bg)', + actions: [{ + asFullButton: true, + icon: 'fas fa-plus', + text: this.$ts.add, + handler: this.add, + }], + }, announcements: [], } }, diff --git a/src/client/pages/instance/emojis.vue b/src/client/pages/instance/emojis.vue index 219955dc45..4cd34b046d 100644 --- a/src/client/pages/instance/emojis.vue +++ b/src/client/pages/instance/emojis.vue @@ -1,12 +1,8 @@ <template> <div class="ogwlenmc"> - <MkTab v-model="tab"> - <option value="local">{{ $ts.local }}</option> - <option value="remote">{{ $ts.remote }}</option> - </MkTab> + <MkHeader :info="header"/> <div class="local" v-if="tab === 'local'"> - <MkButton primary @click="add" style="margin: var(--margin) auto;"><i class="fas fa-plus"></i> {{ $ts.addEmoji }}</MkButton> <MkInput v-model="query" :debounce="true" type="search" style="margin: var(--margin);"> <template #prefix><i class="fas fa-search"></i></template> <template #label>{{ $ts.search }}</template> @@ -79,11 +75,27 @@ export default defineComponent({ title: this.$ts.customEmojis, icon: 'fas fa-laugh', bg: 'var(--bg)', - action: { - icon: 'fas fa-plus', - handler: this.add - } }, + header: computed(() => ({ + title: this.$ts.customEmojis, + icon: 'fas fa-laugh', + bg: 'var(--bg)', + actions: [{ + asFullButton: true, + icon: 'fas fa-plus', + text: this.$ts.addEmoji, + handler: this.add, + }], + tabs: [{ + active: this.tab === 'local', + title: this.$ts.local, + onClick: () => { this.tab = 'local'; }, + }, { + active: this.tab === 'remote', + title: this.$ts.remote, + onClick: () => { this.tab = 'remote'; }, + },] + })), tab: 'local', query: null, queryRemote: null, diff --git a/src/client/pages/instance/index.vue b/src/client/pages/instance/index.vue index 867031eaf2..959c4be6cd 100644 --- a/src/client/pages/instance/index.vue +++ b/src/client/pages/instance/index.vue @@ -79,7 +79,8 @@ export default defineComponent({ setup(props, context) { const indexInfo = { title: i18n.locale.instance, - icon: 'fas fa-cog' + icon: 'fas fa-cog', + bg: 'var(--bg)', }; const INFO = ref(indexInfo); const page = ref(props.initialPage); @@ -126,7 +127,7 @@ export default defineComponent({ pageProps.value = {}; nextTick(() => { - scroll(el.value, 0); + scroll(el.value, { top: 0 }); }); }, { immediate: true }); @@ -234,7 +235,6 @@ export default defineComponent({ > .main { flex: 1; min-width: 0; - overflow: auto; --baseContentWidth: 100%; } } diff --git a/src/client/pages/instance/users.vue b/src/client/pages/instance/users.vue index b72d3f7d3c..69242f3786 100644 --- a/src/client/pages/instance/users.vue +++ b/src/client/pages/instance/users.vue @@ -1,9 +1,6 @@ <template> <div class="lknzcolw"> - <div class="actions"> - <MkButton inline primary @click="addUser()"><i class="fas fa-plus"></i> {{ $ts.addUser }}</MkButton> - <MkButton inline primary @click="lookupUser()"><i class="fas fa-search"></i> {{ $ts.lookup }}</MkButton> - </div> + <MkHeader :info="header"/> <div class="users"> <div class="inputs"> @@ -91,10 +88,26 @@ export default defineComponent({ title: this.$ts.users, icon: 'fas fa-users', bg: 'var(--bg)', - action: { + }, + header: { + title: this.$ts.users, + icon: 'fas fa-users', + bg: 'var(--bg)', + actions: [{ icon: 'fas fa-search', + text: this.$ts.search, handler: this.searchUser - } + }, { + asFullButton: true, + icon: 'fas fa-plus', + text: this.$ts.addUser, + handler: this.addUser + }, { + asFullButton: true, + icon: 'fas fa-search', + text: this.$ts.lookup, + handler: this.lookupUser + }] }, sort: '+createdAt', state: 'all', @@ -173,10 +186,6 @@ export default defineComponent({ <style lang="scss" scoped> .lknzcolw { - > .actions { - margin: var(--margin); - } - > .users { margin: var(--margin); diff --git a/src/client/pages/mentions.vue b/src/client/pages/mentions.vue index 798d3e342d..e1d2f096e1 100644 --- a/src/client/pages/mentions.vue +++ b/src/client/pages/mentions.vue @@ -1,6 +1,9 @@ <template> -<div class="_section"> - <XNotes class="_content" :pagination="pagination" @before="before()" @after="after()"/> +<div> + <MkHeader :info="header"/> + <div class="_section"> + <XNotes class="_content" :pagination="pagination" @before="before()" @after="after()"/> + </div> </div> </template> @@ -19,7 +22,13 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.mentions, - icon: 'fas fa-at' + icon: 'fas fa-at', + bg: 'var(--bg)', + }, + header: { + title: this.$ts.mentions, + icon: 'fas fa-at', + bg: 'var(--bg)', }, pagination: { endpoint: 'notes/mentions', diff --git a/src/client/pages/messages.vue b/src/client/pages/messages.vue index 6ac9746d4e..f4c68daab9 100644 --- a/src/client/pages/messages.vue +++ b/src/client/pages/messages.vue @@ -1,6 +1,9 @@ <template> <div> - <XNotes :pagination="pagination" @before="before()" @after="after()"/> + <MkHeader :info="header"/> + <div> + <XNotes :pagination="pagination" @before="before()" @after="after()"/> + </div> </div> </template> @@ -19,7 +22,13 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.directNotes, - icon: 'fas fa-envelope' + icon: 'fas fa-envelope', + bg: 'var(--bg)', + }, + header: { + title: this.$ts.directNotes, + icon: 'fas fa-envelope', + bg: 'var(--bg)', }, pagination: { endpoint: 'notes/mentions', diff --git a/src/client/pages/messaging/index.vue b/src/client/pages/messaging/index.vue index 84783360bb..fef3b76e10 100644 --- a/src/client/pages/messaging/index.vue +++ b/src/client/pages/messaging/index.vue @@ -1,38 +1,42 @@ <template> -<div class="yweeujhr _root" v-size="{ max: [400] }"> - <MkButton @click="start" primary class="start"><i class="fas fa-plus"></i> {{ $ts.startMessaging }}</MkButton> +<div> + <MkHeader :info="header"/> - <div class="history" v-if="messages.length > 0"> - <MkA v-for="(message, i) in messages" - class="message _block" - :class="{ isMe: isMe(message), isRead: message.groupId ? message.reads.includes($i.id) : message.isRead }" - :to="message.groupId ? `/my/messaging/group/${message.groupId}` : `/my/messaging/${getAcct(isMe(message) ? message.recipient : message.user)}`" - :data-index="i" - :key="message.id" - v-anim="i" - > - <div> - <MkAvatar class="avatar" :user="message.groupId ? message.user : isMe(message) ? message.recipient : message.user" :show-indicator="true"/> - <header v-if="message.groupId"> - <span class="name">{{ message.group.name }}</span> - <MkTime :time="message.createdAt" class="time"/> - </header> - <header v-else> - <span class="name"><MkUserName :user="isMe(message) ? message.recipient : message.user"/></span> - <span class="username">@{{ acct(isMe(message) ? message.recipient : message.user) }}</span> - <MkTime :time="message.createdAt" class="time"/> - </header> - <div class="body"> - <p class="text"><span class="me" v-if="isMe(message)">{{ $ts.you }}:</span>{{ message.text }}</p> + <div class="yweeujhr _root" v-size="{ max: [400] }"> + <MkButton @click="start" primary class="start"><i class="fas fa-plus"></i> {{ $ts.startMessaging }}</MkButton> + + <div class="history" v-if="messages.length > 0"> + <MkA v-for="(message, i) in messages" + class="message _block" + :class="{ isMe: isMe(message), isRead: message.groupId ? message.reads.includes($i.id) : message.isRead }" + :to="message.groupId ? `/my/messaging/group/${message.groupId}` : `/my/messaging/${getAcct(isMe(message) ? message.recipient : message.user)}`" + :data-index="i" + :key="message.id" + v-anim="i" + > + <div> + <MkAvatar class="avatar" :user="message.groupId ? message.user : isMe(message) ? message.recipient : message.user" :show-indicator="true"/> + <header v-if="message.groupId"> + <span class="name">{{ message.group.name }}</span> + <MkTime :time="message.createdAt" class="time"/> + </header> + <header v-else> + <span class="name"><MkUserName :user="isMe(message) ? message.recipient : message.user"/></span> + <span class="username">@{{ acct(isMe(message) ? message.recipient : message.user) }}</span> + <MkTime :time="message.createdAt" class="time"/> + </header> + <div class="body"> + <p class="text"><span class="me" v-if="isMe(message)">{{ $ts.you }}:</span>{{ message.text }}</p> + </div> </div> - </div> - </MkA> + </MkA> + </div> + <div class="_fullinfo" v-if="!fetching && messages.length == 0"> + <img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/> + <div>{{ $ts.noHistory }}</div> + </div> + <MkLoading v-if="fetching"/> </div> - <div class="_fullinfo" v-if="!fetching && messages.length == 0"> - <img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/> - <div>{{ $ts.noHistory }}</div> - </div> - <MkLoading v-if="fetching"/> </div> </template> @@ -56,6 +60,11 @@ export default defineComponent({ icon: 'fas fa-comments', bg: 'var(--bg)', }, + header: { + title: this.$ts.messaging, + icon: 'fas fa-comments', + bg: 'var(--bg)', + }, fetching: true, moreFetching: false, messages: [], diff --git a/src/client/pages/messaging/messaging-room.vue b/src/client/pages/messaging/messaging-room.vue index b6a2fbd3d4..76e58d5bc9 100644 --- a/src/client/pages/messaging/messaging-room.vue +++ b/src/client/pages/messaging/messaging-room.vue @@ -284,7 +284,7 @@ const Component = defineComponent({ }, scrollToBottom() { - scroll(this.$el, this.$el.offsetHeight); + scroll(this.$el, { top: this.$el.offsetHeight }); }, onIndicatorClick() { diff --git a/src/client/pages/my-lists/index.vue b/src/client/pages/my-lists/index.vue index 7de31bb308..b0e9bf9d54 100644 --- a/src/client/pages/my-lists/index.vue +++ b/src/client/pages/my-lists/index.vue @@ -1,13 +1,16 @@ <template> -<div class="qkcjvfiv"> - <MkButton @click="create" primary class="add"><i class="fas fa-plus"></i> {{ $ts.createList }}</MkButton> +<div> + <MkHeader :info="header"/> + <div class="qkcjvfiv"> + <MkButton @click="create" primary class="add"><i class="fas fa-plus"></i> {{ $ts.createList }}</MkButton> - <MkPagination :pagination="pagination" #default="{items}" class="lists _content" ref="list"> - <MkA v-for="list in items" :key="list.id" class="list _panel" :to="`/my/lists/${ list.id }`"> - <div class="name">{{ list.name }}</div> - <MkAvatars :user-ids="list.userIds"/> - </MkA> - </MkPagination> + <MkPagination :pagination="pagination" #default="{items}" class="lists _content" ref="list"> + <MkA v-for="list in items" :key="list.id" class="list _panel" :to="`/my/lists/${ list.id }`"> + <div class="name">{{ list.name }}</div> + <MkAvatars :user-ids="list.userIds"/> + </MkA> + </MkPagination> + </div> </div> </template> @@ -31,6 +34,12 @@ export default defineComponent({ [symbols.PAGE_INFO]: { title: this.$ts.manageLists, icon: 'fas fa-list-ul', + bg: 'var(--bg)', + }, + header: { + title: this.$ts.manageLists, + icon: 'fas fa-list-ul', + bg: 'var(--bg)', action: { icon: 'fas fa-plus', handler: this.create diff --git a/src/client/pages/my-lists/list.vue b/src/client/pages/my-lists/list.vue index 049d370b4e..27c979bc88 100644 --- a/src/client/pages/my-lists/list.vue +++ b/src/client/pages/my-lists/list.vue @@ -1,34 +1,37 @@ <template> -<div class="mk-list-page"> - <transition name="zoom" mode="out-in"> - <div v-if="list" class="_section"> - <div class="_content"> - <MkButton inline @click="addUser()">{{ $ts.addUser }}</MkButton> - <MkButton inline @click="renameList()">{{ $ts.rename }}</MkButton> - <MkButton inline @click="deleteList()">{{ $ts.delete }}</MkButton> +<div> + <MkHeader v-if="header" :info="header"/> + <div class="mk-list-page"> + <transition name="zoom" mode="out-in"> + <div v-if="list" class="_section"> + <div class="_content"> + <MkButton inline @click="addUser()">{{ $ts.addUser }}</MkButton> + <MkButton inline @click="renameList()">{{ $ts.rename }}</MkButton> + <MkButton inline @click="deleteList()">{{ $ts.delete }}</MkButton> + </div> </div> - </div> - </transition> + </transition> - <transition name="zoom" mode="out-in"> - <div v-if="list" class="_section members _gap"> - <div class="_title">{{ $ts.members }}</div> - <div class="_content"> - <div class="users"> - <div class="user _panel" v-for="user in users" :key="user.id"> - <MkAvatar :user="user" class="avatar" :show-indicator="true"/> - <div class="body"> - <MkUserName :user="user" class="name"/> - <MkAcct :user="user" class="acct"/> - </div> - <div class="action"> - <button class="_button" @click="removeUser(user)"><i class="fas fa-times"></i></button> + <transition name="zoom" mode="out-in"> + <div v-if="list" class="_section members _gap"> + <div class="_title">{{ $ts.members }}</div> + <div class="_content"> + <div class="users"> + <div class="user _panel" v-for="user in users" :key="user.id"> + <MkAvatar :user="user" class="avatar" :show-indicator="true"/> + <div class="body"> + <MkUserName :user="user" class="name"/> + <MkAcct :user="user" class="acct"/> + </div> + <div class="action"> + <button class="_button" @click="removeUser(user)"><i class="fas fa-times"></i></button> + </div> </div> </div> </div> </div> - </div> - </transition> + </transition> + </div> </div> </template> @@ -50,6 +53,10 @@ export default defineComponent({ title: this.list.name, icon: 'fas fa-list-ul', } : null), + header: computed(() => this.list ? { + title: this.list.name, + icon: 'fas fa-list-ul', + } : null), list: null, users: [], }; diff --git a/src/client/pages/notifications.vue b/src/client/pages/notifications.vue index 06f8ad3cba..6cbcc9b8e5 100644 --- a/src/client/pages/notifications.vue +++ b/src/client/pages/notifications.vue @@ -1,6 +1,9 @@ <template> -<div class="clupoqwt" v-size="{ min: [800] }"> - <XNotifications class="notifications" @before="before" @after="after" page/> +<div> + <MkHeader :info="header"/> + <div class="clupoqwt" v-size="{ min: [800] }"> + <XNotifications class="notifications" @before="before" @after="after" page/> + </div> </div> </template> @@ -22,6 +25,11 @@ export default defineComponent({ title: this.$ts.notifications, icon: 'fas fa-bell', bg: 'var(--bg)', + }, + header: { + title: this.$ts.notifications, + icon: 'fas fa-bell', + bg: 'var(--bg)', actions: [{ text: this.$ts.markAllAsRead, icon: 'fas fa-check', diff --git a/src/client/pages/pages.vue b/src/client/pages/pages.vue index 80fc53a961..988a759b91 100644 --- a/src/client/pages/pages.vue +++ b/src/client/pages/pages.vue @@ -1,5 +1,8 @@ <template> <div> + <MkHeader :info="header"/> + + <!-- TODO: MkHeaderに統合 --> <MkTab v-model="tab" v-if="$i"> <option value="featured"><i class="fas fa-fire-alt"></i> {{ $ts._pages.featured }}</option> <option value="my"><i class="fas fa-edit"></i> {{ $ts._pages.my }}</option> @@ -46,11 +49,17 @@ export default defineComponent({ [symbols.PAGE_INFO]: { title: this.$ts.pages, icon: 'fas fa-sticky-note', + bg: 'var(--bg)', + }, + header: { + title: this.$ts.pages, + icon: 'fas fa-sticky-note', + bg: 'var(--bg)', actions: [{ icon: 'fas fa-plus', text: this.$ts.create, - handler: this.create - }] + handler: this.create, + }], }, tab: 'featured', featuredPagesPagination: { diff --git a/src/client/pages/search.vue b/src/client/pages/search.vue index bf228576be..fec138726f 100644 --- a/src/client/pages/search.vue +++ b/src/client/pages/search.vue @@ -1,7 +1,10 @@ <template> -<div class="_section"> - <div class="_content"> - <XNotes ref="notes" :pagination="pagination" @before="before" @after="after"/> +<div> + <MkHeader :info="header"/> + <div class="_section"> + <div class="_content"> + <XNotes ref="notes" :pagination="pagination" @before="before" @after="after"/> + </div> </div> </div> </template> @@ -21,7 +24,11 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: computed(() => this.$t('searchWith', { q: this.$route.query.q })), - icon: 'fas fa-search' + icon: 'fas fa-search', + }, + header: { + title: computed(() => this.$t('searchWith', { q: this.$route.query.q })), + icon: 'fas fa-search', }, pagination: { endpoint: 'notes/search', diff --git a/src/client/pages/settings/index.vue b/src/client/pages/settings/index.vue index 3a8503ac55..2d5ced2181 100644 --- a/src/client/pages/settings/index.vue +++ b/src/client/pages/settings/index.vue @@ -39,7 +39,7 @@ </div> </div> <div class="main"> - <component :is="component" :key="page" @info="onInfo" v-bind="pageProps"/> + <component :is="component" :key="page" v-bind="pageProps"/> </div> </div> </template> @@ -74,16 +74,13 @@ export default defineComponent({ title: i18n.locale.settings, icon: 'fas fa-cog', bg: 'var(--bg)', - hide: true, }; const INFO = ref(indexInfo); const page = ref(props.initialPage); const narrow = ref(false); const view = ref(null); const el = ref(null); - const onInfo = (viewInfo) => { - INFO.value = viewInfo; - }; + const pageProps = ref({}); const component = computed(() => { if (page.value == null) return null; @@ -146,7 +143,7 @@ export default defineComponent({ } nextTick(() => { - scroll(el.value, 0); + scroll(el.value, { top: 0 }); }); }, { immediate: true }); @@ -176,7 +173,6 @@ export default defineComponent({ narrow, view, el, - onInfo, pageProps, component, emailNotConfigured, diff --git a/src/client/pages/settings/profile.vue b/src/client/pages/settings/profile.vue index eb9bc6565f..b993b5fc72 100644 --- a/src/client/pages/settings/profile.vue +++ b/src/client/pages/settings/profile.vue @@ -78,7 +78,6 @@ export default defineComponent({ title: this.$ts.profile, icon: 'fas fa-user', bg: 'var(--bg)', - hide: true, }, host, langs, diff --git a/src/client/pages/timeline.vue b/src/client/pages/timeline.vue index 9dda82462d..abe9ccd7e0 100644 --- a/src/client/pages/timeline.vue +++ b/src/client/pages/timeline.vue @@ -1,18 +1,21 @@ <template> -<div class="cmuxhskf" v-hotkey.global="keymap" v-size="{ min: [800] }"> - <XTutorial v-if="$store.reactiveState.tutorial.value != -1" class="tutorial _block"/> - <XPostForm v-if="$store.reactiveState.showFixedPostForm.value" class="post-form _block" fixed/> +<div v-hotkey.global="keymap"> + <MkHeader :info="header"/> + <div class="cmuxhskf" v-size="{ min: [800] }"> + <XTutorial v-if="$store.reactiveState.tutorial.value != -1" class="tutorial _block"/> + <XPostForm v-if="$store.reactiveState.showFixedPostForm.value" class="post-form _block" fixed/> - <div class="new" v-if="queue > 0"><button class="_buttonPrimary" @click="top()">{{ $ts.newNoteRecived }}</button></div> - <div class="tl _block"> - <XTimeline ref="tl" class="tl" - :key="src" - :src="src" - :sound="true" - @before="before()" - @after="after()" - @queue="queueUpdated" - /> + <div class="new" v-if="queue > 0"><button class="_buttonPrimary" @click="top()">{{ $ts.newNoteRecived }}</button></div> + <div class="tl _block"> + <XTimeline ref="tl" class="tl" + :key="src" + :src="src" + :sound="true" + @before="before()" + @after="after()" + @queue="queueUpdated" + /> + </div> </div> </div> </template> @@ -43,6 +46,11 @@ export default defineComponent({ title: this.$ts.timeline, icon: this.src === 'local' ? 'fas fa-comments' : this.src === 'social' ? 'fas fa-share-alt' : this.src === 'global' ? 'fas fa-globe' : 'fas fa-home', bg: 'var(--bg)', + })), + header: computed(() => ({ + title: this.$ts.timeline, + icon: this.src === 'local' ? 'fas fa-comments' : this.src === 'social' ? 'fas fa-share-alt' : this.src === 'global' ? 'fas fa-globe' : 'fas fa-home', + bg: 'var(--bg)', actions: [{ icon: 'fas fa-list-ul', text: this.$ts.lists, @@ -129,7 +137,7 @@ export default defineComponent({ }, top() { - scroll(this.$el, 0); + scroll(this.$el, { top: 0 }); }, async chooseList(ev) { diff --git a/src/client/pages/user-list-timeline.vue b/src/client/pages/user-list-timeline.vue index 491fe948c1..b5e37d4843 100644 --- a/src/client/pages/user-list-timeline.vue +++ b/src/client/pages/user-list-timeline.vue @@ -89,7 +89,7 @@ export default defineComponent({ }, top() { - scroll(this.$el, 0); + scroll(this.$el, { top: 0 }); }, settings() { diff --git a/src/client/pages/user/index.vue b/src/client/pages/user/index.vue index 458ce4b618..f34c42797a 100644 --- a/src/client/pages/user/index.vue +++ b/src/client/pages/user/index.vue @@ -1,98 +1,117 @@ <template> -<transition name="fade" mode="out-in"> - <div class="ftskorzw wide" v-if="user && narrow === false"> - <MkRemoteCaution v-if="user.host != null" :href="user.url"/> +<div> + <MkHeader :info="header"/> + <transition name="fade" mode="out-in"> + <div class="ftskorzw wide" v-if="user && narrow === false"> + <MkRemoteCaution v-if="user.host != null" :href="user.url"/> - <div class="banner-container" :style="style"> - <div class="banner" ref="banner" :style="style"></div> - </div> - <div class="contents"> - <div class="side _forceContainerFull_"> - <MkAvatar class="avatar" :user="user" :disable-preview="true" :show-indicator="true"/> - <div class="name"> - <MkUserName :user="user" :nowrap="false" class="name"/> - <MkAcct :user="user" :detail="true" class="acct"/> - </div> - <div class="followed" v-if="$i && $i.id != user.id && user.isFollowed"><span>{{ $ts.followsYou }}</span></div> - <div class="status"> - <MkA :to="userPage(user)" :class="{ active: page === 'index' }"> - <b>{{ number(user.notesCount) }}</b> - <span>{{ $ts.notes }}</span> - </MkA> - <MkA :to="userPage(user, 'following')" :class="{ active: page === 'following' }"> - <b>{{ number(user.followingCount) }}</b> - <span>{{ $ts.following }}</span> - </MkA> - <MkA :to="userPage(user, 'followers')" :class="{ active: page === 'followers' }"> - <b>{{ number(user.followersCount) }}</b> - <span>{{ $ts.followers }}</span> - </MkA> - </div> - <div class="description"> - <Mfm v-if="user.description" :text="user.description" :is-note="false" :author="user" :i="$i" :custom-emojis="user.emojis"/> - <p v-else class="empty">{{ $ts.noAccountDescription }}</p> - </div> - <div class="fields system"> - <dl class="field" v-if="user.location"> - <dt class="name"><i class="fas fa-map-marker fa-fw"></i> {{ $ts.location }}</dt> - <dd class="value">{{ user.location }}</dd> - </dl> - <dl class="field" v-if="user.birthday"> - <dt class="name"><i class="fas fa-birthday-cake fa-fw"></i> {{ $ts.birthday }}</dt> - <dd class="value">{{ user.birthday.replace('-', '/').replace('-', '/') }} ({{ $t('yearsOld', { age }) }})</dd> - </dl> - <dl class="field"> - <dt class="name"><i class="fas fa-calendar-alt fa-fw"></i> {{ $ts.registeredDate }}</dt> - <dd class="value">{{ new Date(user.createdAt).toLocaleString() }} (<MkTime :time="user.createdAt"/>)</dd> - </dl> - </div> - <div class="fields" v-if="user.fields.length > 0"> - <dl class="field" v-for="(field, i) in user.fields" :key="i"> - <dt class="name"> - <Mfm :text="field.name" :plain="true" :custom-emojis="user.emojis" :colored="false"/> - </dt> - <dd class="value"> - <Mfm :text="field.value" :author="user" :i="$i" :custom-emojis="user.emojis" :colored="false"/> - </dd> - </dl> - </div> - <XActivity :user="user" :key="user.id" class="_gap"/> - <XPhotos :user="user" :key="user.id" class="_gap"/> + <div class="banner-container" :style="style"> + <div class="banner" ref="banner" :style="style"></div> </div> - <div class="main"> - <div class="actions"> - <button @click="menu" class="menu _button"><i class="fas fa-ellipsis-h"></i></button> - <MkFollowButton v-if="!$i || $i.id != user.id" :user="user" :inline="true" :transparent="false" :full="true" large class="koudoku"/> + <div class="contents"> + <div class="side _forceContainerFull_"> + <MkAvatar class="avatar" :user="user" :disable-preview="true" :show-indicator="true"/> + <div class="name"> + <MkUserName :user="user" :nowrap="false" class="name"/> + <MkAcct :user="user" :detail="true" class="acct"/> + </div> + <div class="followed" v-if="$i && $i.id != user.id && user.isFollowed"><span>{{ $ts.followsYou }}</span></div> + <div class="status"> + <MkA :to="userPage(user)" :class="{ active: page === 'index' }"> + <b>{{ number(user.notesCount) }}</b> + <span>{{ $ts.notes }}</span> + </MkA> + <MkA :to="userPage(user, 'following')" :class="{ active: page === 'following' }"> + <b>{{ number(user.followingCount) }}</b> + <span>{{ $ts.following }}</span> + </MkA> + <MkA :to="userPage(user, 'followers')" :class="{ active: page === 'followers' }"> + <b>{{ number(user.followersCount) }}</b> + <span>{{ $ts.followers }}</span> + </MkA> + </div> + <div class="description"> + <Mfm v-if="user.description" :text="user.description" :is-note="false" :author="user" :i="$i" :custom-emojis="user.emojis"/> + <p v-else class="empty">{{ $ts.noAccountDescription }}</p> + </div> + <div class="fields system"> + <dl class="field" v-if="user.location"> + <dt class="name"><i class="fas fa-map-marker fa-fw"></i> {{ $ts.location }}</dt> + <dd class="value">{{ user.location }}</dd> + </dl> + <dl class="field" v-if="user.birthday"> + <dt class="name"><i class="fas fa-birthday-cake fa-fw"></i> {{ $ts.birthday }}</dt> + <dd class="value">{{ user.birthday.replace('-', '/').replace('-', '/') }} ({{ $t('yearsOld', { age }) }})</dd> + </dl> + <dl class="field"> + <dt class="name"><i class="fas fa-calendar-alt fa-fw"></i> {{ $ts.registeredDate }}</dt> + <dd class="value">{{ new Date(user.createdAt).toLocaleString() }} (<MkTime :time="user.createdAt"/>)</dd> + </dl> + </div> + <div class="fields" v-if="user.fields.length > 0"> + <dl class="field" v-for="(field, i) in user.fields" :key="i"> + <dt class="name"> + <Mfm :text="field.name" :plain="true" :custom-emojis="user.emojis" :colored="false"/> + </dt> + <dd class="value"> + <Mfm :text="field.value" :author="user" :i="$i" :custom-emojis="user.emojis" :colored="false"/> + </dd> + </dl> + </div> + <XActivity :user="user" :key="user.id" class="_gap"/> + <XPhotos :user="user" :key="user.id" class="_gap"/> </div> - <template v-if="page === 'index'"> - <div v-if="user.pinnedNotes.length > 0" class="_gap"> - <XNote v-for="note in user.pinnedNotes" class="note _gap" :note="note" @update:note="pinnedNoteUpdated(note, $event)" :key="note.id" :pinned="true"/> + <div class="main"> + <div class="actions"> + <button @click="menu" class="menu _button"><i class="fas fa-ellipsis-h"></i></button> + <MkFollowButton v-if="!$i || $i.id != user.id" :user="user" :inline="true" :transparent="false" :full="true" large class="koudoku"/> </div> - <div class="_gap"> - <XUserTimeline :user="user"/> - </div> - </template> - <XFollowList v-else-if="page === 'following'" type="following" :user="user" class="_gap"/> - <XFollowList v-else-if="page === 'followers'" type="followers" :user="user" class="_gap"/> - <XClips v-else-if="page === 'clips'" :user="user" class="_gap"/> - <XPages v-else-if="page === 'pages'" :user="user" class="_gap"/> + <template v-if="page === 'index'"> + <div v-if="user.pinnedNotes.length > 0" class="_gap"> + <XNote v-for="note in user.pinnedNotes" class="note _gap" :note="note" @update:note="pinnedNoteUpdated(note, $event)" :key="note.id" :pinned="true"/> + </div> + <div class="_gap"> + <XUserTimeline :user="user"/> + </div> + </template> + <XFollowList v-else-if="page === 'following'" type="following" :user="user" class="_gap"/> + <XFollowList v-else-if="page === 'followers'" type="followers" :user="user" class="_gap"/> + <XClips v-else-if="page === 'clips'" :user="user" class="_gap"/> + <XPages v-else-if="page === 'pages'" :user="user" class="_gap"/> + </div> </div> </div> - </div> - <div class="ftskorzw narrow _root" v-else-if="user && narrow === true" v-size="{ max: [500] }"> - <!-- TODO --> - <!-- <div class="punished" v-if="user.isSuspended"><i class="fas fa-exclamation-triangle" style="margin-right: 8px;"></i> {{ $ts.userSuspended }}</div> --> - <!-- <div class="punished" v-if="user.isSilenced"><i class="fas fa-exclamation-triangle" style="margin-right: 8px;"></i> {{ $ts.userSilenced }}</div> --> + <div class="ftskorzw narrow _root" v-else-if="user && narrow === true" v-size="{ max: [500] }"> + <!-- TODO --> + <!-- <div class="punished" v-if="user.isSuspended"><i class="fas fa-exclamation-triangle" style="margin-right: 8px;"></i> {{ $ts.userSuspended }}</div> --> + <!-- <div class="punished" v-if="user.isSilenced"><i class="fas fa-exclamation-triangle" style="margin-right: 8px;"></i> {{ $ts.userSilenced }}</div> --> - <div class="profile"> - <MkRemoteCaution v-if="user.host != null" :href="user.url" class="warn"/> + <div class="profile"> + <MkRemoteCaution v-if="user.host != null" :href="user.url" class="warn"/> - <div class="_block main" :key="user.id"> - <div class="banner-container" :style="style"> - <div class="banner" ref="banner" :style="style"></div> - <div class="fade"></div> + <div class="_block main" :key="user.id"> + <div class="banner-container" :style="style"> + <div class="banner" ref="banner" :style="style"></div> + <div class="fade"></div> + <div class="title"> + <MkUserName class="name" :user="user" :nowrap="true"/> + <div class="bottom"> + <span class="username"><MkAcct :user="user" :detail="true" /></span> + <span v-if="user.isAdmin" :title="$ts.isAdmin" style="color: var(--badge);"><i class="fas fa-bookmark"></i></span> + <span v-if="!user.isAdmin && user.isModerator" :title="$ts.isModerator" style="color: var(--badge);"><i class="far fa-bookmark"></i></span> + <span v-if="user.isLocked" :title="$ts.isLocked"><i class="fas fa-lock"></i></span> + <span v-if="user.isBot" :title="$ts.isBot"><i class="fas fa-robot"></i></span> + </div> + </div> + <span class="followed" v-if="$i && $i.id != user.id && user.isFollowed">{{ $ts.followsYou }}</span> + <div class="actions" v-if="$i"> + <button @click="menu" class="menu _button"><i class="fas fa-ellipsis-h"></i></button> + <MkFollowButton v-if="$i.id != user.id" :user="user" :inline="true" :transparent="false" :full="true" class="koudoku"/> + </div> + </div> + <MkAvatar class="avatar" :user="user" :disable-preview="true" :show-indicator="true"/> <div class="title"> - <MkUserName class="name" :user="user" :nowrap="true"/> + <MkUserName :user="user" :nowrap="false" class="name"/> <div class="bottom"> <span class="username"><MkAcct :user="user" :detail="true" /></span> <span v-if="user.isAdmin" :title="$ts.isAdmin" style="color: var(--badge);"><i class="fas fa-bookmark"></i></span> @@ -101,92 +120,76 @@ <span v-if="user.isBot" :title="$ts.isBot"><i class="fas fa-robot"></i></span> </div> </div> - <span class="followed" v-if="$i && $i.id != user.id && user.isFollowed">{{ $ts.followsYou }}</span> - <div class="actions" v-if="$i"> - <button @click="menu" class="menu _button"><i class="fas fa-ellipsis-h"></i></button> - <MkFollowButton v-if="$i.id != user.id" :user="user" :inline="true" :transparent="false" :full="true" class="koudoku"/> + <div class="description"> + <Mfm v-if="user.description" :text="user.description" :is-note="false" :author="user" :i="$i" :custom-emojis="user.emojis"/> + <p v-else class="empty">{{ $ts.noAccountDescription }}</p> </div> - </div> - <MkAvatar class="avatar" :user="user" :disable-preview="true" :show-indicator="true"/> - <div class="title"> - <MkUserName :user="user" :nowrap="false" class="name"/> - <div class="bottom"> - <span class="username"><MkAcct :user="user" :detail="true" /></span> - <span v-if="user.isAdmin" :title="$ts.isAdmin" style="color: var(--badge);"><i class="fas fa-bookmark"></i></span> - <span v-if="!user.isAdmin && user.isModerator" :title="$ts.isModerator" style="color: var(--badge);"><i class="far fa-bookmark"></i></span> - <span v-if="user.isLocked" :title="$ts.isLocked"><i class="fas fa-lock"></i></span> - <span v-if="user.isBot" :title="$ts.isBot"><i class="fas fa-robot"></i></span> + <div class="fields system"> + <dl class="field" v-if="user.location"> + <dt class="name"><i class="fas fa-map-marker fa-fw"></i> {{ $ts.location }}</dt> + <dd class="value">{{ user.location }}</dd> + </dl> + <dl class="field" v-if="user.birthday"> + <dt class="name"><i class="fas fa-birthday-cake fa-fw"></i> {{ $ts.birthday }}</dt> + <dd class="value">{{ user.birthday.replace('-', '/').replace('-', '/') }} ({{ $t('yearsOld', { age }) }})</dd> + </dl> + <dl class="field"> + <dt class="name"><i class="fas fa-calendar-alt fa-fw"></i> {{ $ts.registeredDate }}</dt> + <dd class="value">{{ new Date(user.createdAt).toLocaleString() }} (<MkTime :time="user.createdAt"/>)</dd> + </dl> + </div> + <div class="fields" v-if="user.fields.length > 0"> + <dl class="field" v-for="(field, i) in user.fields" :key="i"> + <dt class="name"> + <Mfm :text="field.name" :plain="true" :custom-emojis="user.emojis" :colored="false"/> + </dt> + <dd class="value"> + <Mfm :text="field.value" :author="user" :i="$i" :custom-emojis="user.emojis" :colored="false"/> + </dd> + </dl> + </div> + <div class="status"> + <MkA :to="userPage(user)" :class="{ active: page === 'index' }" v-click-anime> + <b>{{ number(user.notesCount) }}</b> + <span>{{ $ts.notes }}</span> + </MkA> + <MkA :to="userPage(user, 'following')" :class="{ active: page === 'following' }" v-click-anime> + <b>{{ number(user.followingCount) }}</b> + <span>{{ $ts.following }}</span> + </MkA> + <MkA :to="userPage(user, 'followers')" :class="{ active: page === 'followers' }" v-click-anime> + <b>{{ number(user.followersCount) }}</b> + <span>{{ $ts.followers }}</span> + </MkA> </div> - </div> - <div class="description"> - <Mfm v-if="user.description" :text="user.description" :is-note="false" :author="user" :i="$i" :custom-emojis="user.emojis"/> - <p v-else class="empty">{{ $ts.noAccountDescription }}</p> - </div> - <div class="fields system"> - <dl class="field" v-if="user.location"> - <dt class="name"><i class="fas fa-map-marker fa-fw"></i> {{ $ts.location }}</dt> - <dd class="value">{{ user.location }}</dd> - </dl> - <dl class="field" v-if="user.birthday"> - <dt class="name"><i class="fas fa-birthday-cake fa-fw"></i> {{ $ts.birthday }}</dt> - <dd class="value">{{ user.birthday.replace('-', '/').replace('-', '/') }} ({{ $t('yearsOld', { age }) }})</dd> - </dl> - <dl class="field"> - <dt class="name"><i class="fas fa-calendar-alt fa-fw"></i> {{ $ts.registeredDate }}</dt> - <dd class="value">{{ new Date(user.createdAt).toLocaleString() }} (<MkTime :time="user.createdAt"/>)</dd> - </dl> - </div> - <div class="fields" v-if="user.fields.length > 0"> - <dl class="field" v-for="(field, i) in user.fields" :key="i"> - <dt class="name"> - <Mfm :text="field.name" :plain="true" :custom-emojis="user.emojis" :colored="false"/> - </dt> - <dd class="value"> - <Mfm :text="field.value" :author="user" :i="$i" :custom-emojis="user.emojis" :colored="false"/> - </dd> - </dl> - </div> - <div class="status"> - <MkA :to="userPage(user)" :class="{ active: page === 'index' }" v-click-anime> - <b>{{ number(user.notesCount) }}</b> - <span>{{ $ts.notes }}</span> - </MkA> - <MkA :to="userPage(user, 'following')" :class="{ active: page === 'following' }" v-click-anime> - <b>{{ number(user.followingCount) }}</b> - <span>{{ $ts.following }}</span> - </MkA> - <MkA :to="userPage(user, 'followers')" :class="{ active: page === 'followers' }" v-click-anime> - <b>{{ number(user.followersCount) }}</b> - <span>{{ $ts.followers }}</span> - </MkA> </div> </div> - </div> - <div class="contents"> - <template v-if="page === 'index'"> - <div> - <div v-if="user.pinnedNotes.length > 0" class="_gap"> - <XNote v-for="note in user.pinnedNotes" class="note _block" :note="note" @update:note="pinnedNoteUpdated(note, $event)" :key="note.id" :pinned="true"/> + <div class="contents"> + <template v-if="page === 'index'"> + <div> + <div v-if="user.pinnedNotes.length > 0" class="_gap"> + <XNote v-for="note in user.pinnedNotes" class="note _block" :note="note" @update:note="pinnedNoteUpdated(note, $event)" :key="note.id" :pinned="true"/> + </div> + <MkInfo v-else-if="$i && $i.id === user.id">{{ $ts.userPagePinTip }}</MkInfo> + <XPhotos :user="user" :key="user.id"/> + <XActivity :user="user" :key="user.id"/> </div> - <MkInfo v-else-if="$i && $i.id === user.id">{{ $ts.userPagePinTip }}</MkInfo> - <XPhotos :user="user" :key="user.id"/> - <XActivity :user="user" :key="user.id"/> - </div> - <div> - <XUserTimeline :user="user"/> - </div> - </template> - <XFollowList v-else-if="page === 'following'" type="following" :user="user" class="_content _gap"/> - <XFollowList v-else-if="page === 'followers'" type="followers" :user="user" class="_content _gap"/> - <XClips v-else-if="page === 'clips'" :user="user" class="_gap"/> - <XPages v-else-if="page === 'pages'" :user="user" class="_gap"/> - <XGallery v-else-if="page === 'gallery'" :user="user" class="_gap"/> + <div> + <XUserTimeline :user="user"/> + </div> + </template> + <XFollowList v-else-if="page === 'following'" type="following" :user="user" class="_content _gap"/> + <XFollowList v-else-if="page === 'followers'" type="followers" :user="user" class="_content _gap"/> + <XClips v-else-if="page === 'clips'" :user="user" class="_gap"/> + <XPages v-else-if="page === 'pages'" :user="user" class="_gap"/> + <XGallery v-else-if="page === 'gallery'" :user="user" class="_gap"/> + </div> </div> - </div> - <MkError v-else-if="error" @retry="fetch()"/> - <MkLoading v-else/> -</transition> + <MkError v-else-if="error" @retry="fetch()"/> + <MkLoading v-else/> + </transition> +</div> </template> <script lang="ts"> @@ -242,6 +245,15 @@ export default defineComponent({ data() { return { [symbols.PAGE_INFO]: computed(() => this.user ? { + icon: 'fas fa-user', + title: this.user.name ? `${this.user.name} (@${this.user.username})` : `@${this.user.username}`, + path: `/@${this.user.username}`, + share: { + title: this.user.name, + }, + bg: 'var(--bg)', + } : null), + header: computed(() => this.user ? { title: this.user.name ? `${this.user.name} (@${this.user.username})` : `@${this.user.username}`, subtitle: `@${getAcct(this.user)}`, userName: this.user, diff --git a/src/client/scripts/scroll.ts b/src/client/scripts/scroll.ts index bc6d1530c5..621fe88105 100644 --- a/src/client/scripts/scroll.ts +++ b/src/client/scripts/scroll.ts @@ -1,3 +1,5 @@ +type ScrollBehavior = 'auto' | 'smooth' | 'instant'; + export function getScrollContainer(el: Element | null): Element | null { if (el == null || el.tagName === 'BODY') return null; const overflow = window.getComputedStyle(el).getPropertyValue('overflow'); @@ -45,21 +47,25 @@ export function onScrollBottom(el: Element, cb) { container.addEventListener('scroll', onScroll, { passive: true }); } -export function scroll(el: Element, top: number) { +export function scroll(el: Element, options: { + top?: number; + left?: number; + behavior?: ScrollBehavior; +}) { const container = getScrollContainer(el); if (container == null) { - window.scroll({ top: top, behavior: 'instant' }); + window.scroll(options); } else { - container.scrollTop = top; + container.scroll(options); } } -export function scrollToTop(el: Element) { - scroll(el, 0); +export function scrollToTop(el: Element, options: { behavior?: ScrollBehavior; } = {}) { + scroll(el, { top: 0, ...options }); } -export function scrollToBottom(el: Element) { - scroll(el, 99999); // TODO: ちゃんと計算する +export function scrollToBottom(el: Element, options: { behavior?: ScrollBehavior; } = {}) { + scroll(el, { top: 99999, ...options }); // TODO: ちゃんと計算する } export function isBottom(el: Element, asobi = 0) { diff --git a/src/client/ui/chat/index.vue b/src/client/ui/chat/index.vue index 7090c9486a..4194a9919f 100644 --- a/src/client/ui/chat/index.vue +++ b/src/client/ui/chat/index.vue @@ -74,7 +74,7 @@ <main class="main" @contextmenu.stop="onContextmenu"> <header class="header"> - <XHeader class="header" :info="pageInfo" :menu="menu" :center="false" @click="onHeaderClick"/> + <MkHeader class="header" :info="pageInfo" :menu="menu" :center="false" @click="onHeaderClick"/> </header> <router-view v-slot="{ Component }"> <transition :name="$store.state.animation ? 'page' : ''" mode="out-in" @enter="onTransition"> @@ -101,7 +101,6 @@ import XSidebar from '@client/ui/_common_/sidebar.vue'; import XWidgets from './widgets.vue'; import XCommon from '../_common_/common.vue'; import XSide from './side.vue'; -import XHeader from '../_common_/header.vue'; import XHeaderClock from './header-clock.vue'; import * as os from '@client/os'; import { router } from '@client/router'; @@ -117,7 +116,6 @@ export default defineComponent({ XSidebar, XWidgets, XSide, // NOTE: dynamic importするとAsyncComponentWrapperが間に入るせいでref取得できなくて面倒になる - XHeader, XHeaderClock, }, diff --git a/src/client/ui/chat/side.vue b/src/client/ui/chat/side.vue index 3e8904596d..d920e5b77c 100644 --- a/src/client/ui/chat/side.vue +++ b/src/client/ui/chat/side.vue @@ -1,7 +1,7 @@ <template> <div class="mrajymqm _narrow_" v-if="component"> <header class="header" @contextmenu.prevent.stop="onContextmenu"> - <XHeader class="title" :info="pageInfo" :center="false"/> + <MkHeader class="title" :info="pageInfo" :center="false"/> </header> <component :is="component" v-bind="props" :ref="changePage" class="body _flat_"/> </div> @@ -9,7 +9,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import XHeader from '../_common_/header.vue'; import * as os from '@client/os'; import copyToClipboard from '@client/scripts/copy-to-clipboard'; import { resolve } from '@client/router'; @@ -18,7 +17,6 @@ import * as symbols from '@client/symbols'; export default defineComponent({ components: { - XHeader }, provide() { diff --git a/src/client/ui/deck/main-column.vue b/src/client/ui/deck/main-column.vue index 42d963cda6..4c1fa255a6 100644 --- a/src/client/ui/deck/main-column.vue +++ b/src/client/ui/deck/main-column.vue @@ -6,7 +6,6 @@ </template> </template> - <XHeader :info="pageInfo"/> <router-view v-slot="{ Component }" class="_flat_"> <transition> <keep-alive :include="['timeline']"> @@ -21,7 +20,6 @@ import { defineComponent } from 'vue'; import XColumn from './column.vue'; import XNotes from '@client/components/notes.vue'; -import XHeader from '@client/ui/_common_/header.vue'; import { deckStore } from '@client/ui/deck/deck-store'; import * as os from '@client/os'; import * as symbols from '@client/symbols'; @@ -29,7 +27,6 @@ import * as symbols from '@client/symbols'; export default defineComponent({ components: { XColumn, - XHeader, XNotes }, diff --git a/src/client/ui/default.side.vue b/src/client/ui/default.side.vue index c453781e80..c7d2abff26 100644 --- a/src/client/ui/default.side.vue +++ b/src/client/ui/default.side.vue @@ -7,7 +7,7 @@ <span class="title">{{ pageInfo.title }}</span> <button class="_button" @click="close()"><i class="fas fa-times"></i></button> </header> - <XHeader class="pageHeader" :info="pageInfo"/> + <MkHeader class="pageHeader" :info="pageInfo"/> <component :is="component" v-bind="props" :ref="changePage"/> </div> </div> @@ -15,7 +15,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import XHeader from './_common_/header.vue'; import * as os from '@client/os'; import copyToClipboard from '@client/scripts/copy-to-clipboard'; import { resolve } from '@client/router'; @@ -23,10 +22,6 @@ import { url } from '@client/config'; import * as symbols from '@client/symbols'; export default defineComponent({ - components: { - XHeader - }, - provide() { return { navHook: (path) => { diff --git a/src/client/ui/default.vue b/src/client/ui/default.vue index 4ceb3e1650..6fc8cba3c9 100644 --- a/src/client/ui/default.vue +++ b/src/client/ui/default.vue @@ -1,6 +1,6 @@ <template> -<div class="mk-app" :class="{ wallpaper, isMobile }" :style="`--headerHeight:` + headerHeight + 'px'"> - <XHeaderMenu v-if="showMenuOnTop"/> +<div class="mk-app" :class="{ wallpaper, isMobile }" :style="`--globalHeaderHeight:${globalHeaderHeight}px`"> + <XHeaderMenu v-if="showMenuOnTop" v-get-size="(w, h) => globalHeaderHeight = h"/> <div class="columns" :class="{ fullView, withGlobalHeader: showMenuOnTop }"> <template v-if="!isMobile"> @@ -13,9 +13,6 @@ </template> <main class="main" @contextmenu.stop="onContextmenu" :style="{ background: pageInfo?.bg }"> - <header class="header" @click="onHeaderClick"> - <XHeader :info="pageInfo" v-get-size="(w, h) => headerHeight = h" :thin="true"/> - </header> <div class="content" :class="{ _flat_: !fullView }"> <router-view v-slot="{ Component }"> <transition :name="$store.state.animation ? 'page' : ''" mode="out-in" @enter="onTransition"> @@ -67,7 +64,6 @@ import { StickySidebar } from '@client/scripts/sticky-sidebar'; import XSidebar from './default.sidebar.vue'; import XDrawerSidebar from '@client/ui/_common_/sidebar.vue'; import XCommon from './_common_/common.vue'; -import XHeader from './_common_/header.vue'; import * as os from '@client/os'; import { menuDef } from '@client/menu'; import * as symbols from '@client/symbols'; @@ -80,7 +76,6 @@ export default defineComponent({ XCommon, XSidebar, XDrawerSidebar, - XHeader, XHeaderMenu: defineAsyncComponent(() => import('./default.header.vue')), XWidgets: defineAsyncComponent(() => import('./default.widgets.vue')), }, @@ -88,8 +83,8 @@ export default defineComponent({ data() { return { pageInfo: null, - headerHeight: 0, menuDef: menuDef, + globalHeaderHeight: 0, isMobile: window.innerWidth <= MOBILE_THRESHOLD, isDesktop: window.innerWidth >= DESKTOP_THRESHOLD, widgetsShowing: false, @@ -194,10 +189,6 @@ export default defineComponent({ if (window._scroll) window._scroll(); }, - onHeaderClick() { - window.scroll({ top: 0, behavior: 'smooth' }); - }, - onContextmenu(e) { const isLink = (el: HTMLElement) => { if (el.tagName === 'A') return true; @@ -282,10 +273,6 @@ export default defineComponent({ border: none; width: 100%; border-radius: 0; - - > .header { - width: 100%; - } } } } @@ -325,29 +312,6 @@ export default defineComponent({ border-radius: 0; overflow: clip; --margin: 12px; - - > .header { - position: sticky; - z-index: 1000; - top: var(--globalHeaderHeight, 0px); - -webkit-backdrop-filter: var(--blur, blur(32px)); - backdrop-filter: var(--blur, blur(32px)); - background-color: var(--header); - border-bottom: solid 0.5px var(--divider); - } - - > .content { - --stickyTop: calc(var(--globalHeaderHeight, 0px) + var(--headerHeight)); - } - - @media (max-width: 850px) { - padding-top: var(--headerHeight); - - > .header { - position: fixed; - width: calc(100% - #{$nav-icon-only-width}); - } - } } > .widgets { @@ -369,12 +333,11 @@ export default defineComponent({ } &.withGlobalHeader { - --globalHeaderHeight: 60px; // TODO: 60pxと決め打ちしているのを直す - > .main { margin-top: 0; border: solid 1px var(--divider); border-radius: var(--radius); + --stickyTop: var(--globalHeaderHeight); } > .widgets { diff --git a/src/client/ui/universal.vue b/src/client/ui/universal.vue index 5b51752dda..7c25d71bb3 100644 --- a/src/client/ui/universal.vue +++ b/src/client/ui/universal.vue @@ -1,11 +1,8 @@ <template> -<div class="mk-app" :class="{ wallpaper }" :style="`--headerHeight:` + headerHeight + 'px'"> +<div class="mk-app" :class="{ wallpaper }"> <XSidebar ref="nav" class="sidebar"/> <div class="contents" ref="contents" @contextmenu.stop="onContextmenu" :style="{ background: pageInfo?.bg }"> - <header class="header" ref="header" @click="onHeaderClick" :style="{ background: pageInfo?.bg }"> - <XHeader v-if="!pageInfo?.hide" :info="pageInfo" v-get-size="(w, h) => headerHeight = h"/> - </header> <main ref="main"> <div class="content"> <router-view v-slot="{ Component }"> @@ -58,7 +55,6 @@ import { instanceName } from '@client/config'; import { StickySidebar } from '@client/scripts/sticky-sidebar'; import XSidebar from '@client/ui/_common_/sidebar.vue'; import XCommon from './_common_/common.vue'; -import XHeader from './_common_/header.vue'; import XSide from './default.side.vue'; import * as os from '@client/os'; import { menuDef } from '@client/menu'; @@ -70,7 +66,6 @@ export default defineComponent({ components: { XCommon, XSidebar, - XHeader, XWidgets: defineAsyncComponent(() => import('./universal.widgets.vue')), XSide, // NOTE: dynamic importするとAsyncComponentWrapperが間に入るせいでref取得できなくて面倒になる }, @@ -86,7 +81,6 @@ export default defineComponent({ data() { return { pageInfo: null, - headerHeight: 0, isDesktop: window.innerWidth >= DESKTOP_THRESHOLD, menuDef: menuDef, navHidden: false, @@ -152,9 +146,6 @@ export default defineComponent({ adjustUI() { const navWidth = this.$refs.nav.$el.offsetWidth; this.navHidden = navWidth === 0; - if (this.$refs.contents == null) return; - const width = this.$refs.contents.offsetWidth; - if (this.$refs.header) this.$refs.header.style.width = `${width}px`; }, showNav() { @@ -184,10 +175,6 @@ export default defineComponent({ if (window._scroll) window._scroll(); }, - onHeaderClick() { - window.scroll({ top: 0, behavior: 'smooth' }); - }, - onContextmenu(e) { const isLink = (el: HTMLElement) => { if (el.tagName === 'A') return true; @@ -263,22 +250,8 @@ export default defineComponent({ > .contents { width: 100%; min-width: 0; - --stickyTop: var(--headerHeight); - padding-top: var(--headerHeight); background: var(--panel); - > .header { - position: fixed; - z-index: 1000; - top: 0; - //background-color: var(--panel); - -webkit-backdrop-filter: var(--blur, blur(32px)); - backdrop-filter: var(--blur, blur(32px)); - background-color: var(--header); - border-bottom: solid 0.5px var(--divider); - user-select: none; - } - > main { min-width: 0; diff --git a/src/client/ui/zen.vue b/src/client/ui/zen.vue index 3756ddb5c3..98e2b8dac6 100644 --- a/src/client/ui/zen.vue +++ b/src/client/ui/zen.vue @@ -2,7 +2,7 @@ <div class="mk-app"> <div class="contents"> <header class="header"> - <XHeader :info="pageInfo"/> + <MkHeader :info="pageInfo"/> </header> <main ref="main"> <div class="content"> @@ -24,14 +24,12 @@ <script lang="ts"> import { defineComponent, defineAsyncComponent } from 'vue'; import { host } from '@client/config'; -import XHeader from './_common_/header.vue'; import XCommon from './_common_/common.vue'; import * as symbols from '@client/symbols'; export default defineComponent({ components: { XCommon, - XHeader, }, data() { From ec05c073217a0c754032ac92de9d91fe03808dd0 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 9 Oct 2021 12:44:19 +0900 Subject: [PATCH 051/107] =?UTF-8?q?feat:=20=E6=9C=AA=E8=AA=AD=E3=81=AE?= =?UTF-8?q?=E9=80=9A=E7=9F=A5=E3=81=AE=E3=81=BF=E8=A1=A8=E7=A4=BA=E3=81=99?= =?UTF-8?q?=E3=82=8B=E6=A9=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 2 ++ locales/ja-JP.yml | 1 + src/client/components/notifications.vue | 11 +++++++++++ src/client/pages/notifications.vue | 22 +++++++++++++++------ src/server/api/endpoints/i/notifications.ts | 9 +++++++++ 5 files changed, 39 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 215555df72..6550db9803 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,9 @@ - アカウント登録にメールアドレスの設定を必須にするオプション - クライアント: アニメーションを減らす設定をメニューのアニメーションにも適用するように - クライアント: MFM関数構文のサジェストを実装 +- クライアント: 未読の通知のみ表示する機能 - ActivityPub: HTML -> MFMの変換を強化 +- API: i/notifications に unreadOnly オプションを追加 - API: ap系のエンドポイントをログイン必須化+レートリミット追加 - Misskeyのコマンドラインオプションを廃止 - 代わりに環境変数で設定することができます diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 0be87cf2c5..eb72c7ca17 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -792,6 +792,7 @@ unresolved: "未解決" itsOn: "オンになっています" itsOff: "オフになっています" emailRequiredForSignup: "アカウント登録にメールアドレスを必須にする" +unread: "未読" _signup: almostThere: "ほとんど完了です" diff --git a/src/client/components/notifications.vue b/src/client/components/notifications.vue index e91f18a693..8be1e191b9 100644 --- a/src/client/components/notifications.vue +++ b/src/client/components/notifications.vue @@ -48,6 +48,11 @@ export default defineComponent({ required: false, default: null, }, + unreadOnly: { + type: Boolean, + required: false, + default: false, + }, }, data() { @@ -58,6 +63,7 @@ export default defineComponent({ limit: 10, params: () => ({ includeTypes: this.allIncludeTypes || undefined, + unreadOnly: this.unreadOnly, }) }, }; @@ -76,6 +82,11 @@ export default defineComponent({ }, deep: true }, + unreadOnly: { + handler() { + this.reload(); + }, + }, // TODO: vue/vuexのバグか仕様かは不明なものの、プロフィール更新するなどして $i が更新されると、 // mutingNotificationTypes に変化が無くてもこのハンドラーが呼び出され無駄なリロードが発生するのを直す '$i.mutingNotificationTypes': { diff --git a/src/client/pages/notifications.vue b/src/client/pages/notifications.vue index 6cbcc9b8e5..9728b87bf1 100644 --- a/src/client/pages/notifications.vue +++ b/src/client/pages/notifications.vue @@ -2,13 +2,13 @@ <div> <MkHeader :info="header"/> <div class="clupoqwt" v-size="{ min: [800] }"> - <XNotifications class="notifications" @before="before" @after="after" page/> + <XNotifications class="notifications" @before="before" @after="after" :unread-only="tab === 'unread'"/> </div> </div> </template> <script lang="ts"> -import { defineComponent } from 'vue'; +import { computed, defineComponent } from 'vue'; import Progress from '@client/scripts/loading'; import XNotifications from '@client/components/notifications.vue'; import * as os from '@client/os'; @@ -26,7 +26,8 @@ export default defineComponent({ icon: 'fas fa-bell', bg: 'var(--bg)', }, - header: { + tab: 'all', + header: computed(() => ({ title: this.$ts.notifications, icon: 'fas fa-bell', bg: 'var(--bg)', @@ -35,9 +36,18 @@ export default defineComponent({ icon: 'fas fa-check', handler: () => { os.apiWithDialog('notifications/mark-all-as-read'); - } - }] - }, + }, + }], + tabs: [{ + active: this.tab === 'all', + title: this.$ts.all, + onClick: () => { this.tab = 'all'; }, + }, { + active: this.tab === 'unread', + title: this.$ts.unread, + onClick: () => { this.tab = 'unread'; }, + },] + })), }; }, diff --git a/src/server/api/endpoints/i/notifications.ts b/src/server/api/endpoints/i/notifications.ts index 3c265a10c1..0c5586054f 100644 --- a/src/server/api/endpoints/i/notifications.ts +++ b/src/server/api/endpoints/i/notifications.ts @@ -33,6 +33,11 @@ export const meta = { default: false }, + unreadOnly: { + validator: $.optional.bool, + default: false + }, + markAsRead: { validator: $.optional.bool, default: true @@ -105,6 +110,10 @@ export default define(meta, async (ps, user) => { query.andWhere(`notification.type NOT IN (:...excludeTypes)`, { excludeTypes: ps.excludeTypes }); } + if (ps.unreadOnly) { + query.andWhere(`notification.isRead = false`); + } + const notifications = await query.take(ps.limit!).getMany(); // Mark all as read From abc45ded9be06e01c95d9ac9966ab8ee0dacf138 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 9 Oct 2021 12:47:40 +0900 Subject: [PATCH 052/107] refactor: use path alias --- src/client/components/notification-setting-window.vue | 2 +- src/client/components/notifications.vue | 2 +- src/client/pages/settings/notifications.vue | 2 +- src/models/entities/notification.ts | 2 +- src/models/entities/user-profile.ts | 2 +- src/server/api/endpoints/i/notifications.ts | 2 +- src/server/api/endpoints/i/update.ts | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/client/components/notification-setting-window.vue b/src/client/components/notification-setting-window.vue index 0b41672170..14e0b76cc6 100644 --- a/src/client/components/notification-setting-window.vue +++ b/src/client/components/notification-setting-window.vue @@ -32,7 +32,7 @@ import XModalWindow from '@client/components/ui/modal-window.vue'; import MkSwitch from './form/switch.vue'; import MkInfo from './ui/info.vue'; import MkButton from './ui/button.vue'; -import { notificationTypes } from '../../types'; +import { notificationTypes } from '@/types'; export default defineComponent({ components: { diff --git a/src/client/components/notifications.vue b/src/client/components/notifications.vue index 8be1e191b9..78c1cce0c7 100644 --- a/src/client/components/notifications.vue +++ b/src/client/components/notifications.vue @@ -26,7 +26,7 @@ import paging from '@client/scripts/paging'; import XNotification from './notification.vue'; import XList from './date-separated-list.vue'; import XNote from './note.vue'; -import { notificationTypes } from '../../types'; +import { notificationTypes } from '@/types'; import * as os from '@client/os'; import MkButton from '@client/components/ui/button.vue'; diff --git a/src/client/pages/settings/notifications.vue b/src/client/pages/settings/notifications.vue index 1ef350335c..5f84349474 100644 --- a/src/client/pages/settings/notifications.vue +++ b/src/client/pages/settings/notifications.vue @@ -15,7 +15,7 @@ import FormButton from '@client/components/debobigego/button.vue'; import FormLink from '@client/components/debobigego/link.vue'; import FormBase from '@client/components/debobigego/base.vue'; import FormGroup from '@client/components/debobigego/group.vue'; -import { notificationTypes } from '../../../types'; +import { notificationTypes } from '@/types'; import * as os from '@client/os'; import * as symbols from '@client/symbols'; diff --git a/src/models/entities/notification.ts b/src/models/entities/notification.ts index 988fdb341f..47184caacc 100644 --- a/src/models/entities/notification.ts +++ b/src/models/entities/notification.ts @@ -5,7 +5,7 @@ import { Note } from './note'; import { FollowRequest } from './follow-request'; import { UserGroupInvitation } from './user-group-invitation'; import { AccessToken } from './access-token'; -import { notificationTypes } from '../../types'; +import { notificationTypes } from '@/types'; @Entity() export class Notification { diff --git a/src/models/entities/user-profile.ts b/src/models/entities/user-profile.ts index 3a9043fac6..a2da07d76f 100644 --- a/src/models/entities/user-profile.ts +++ b/src/models/entities/user-profile.ts @@ -2,7 +2,7 @@ import { Entity, Column, Index, OneToOne, JoinColumn, PrimaryColumn } from 'type import { id } from '../id'; import { User } from './user'; import { Page } from './page'; -import { notificationTypes } from '../../types'; +import { notificationTypes } from '@/types'; // TODO: このテーブルで管理している情報すべてレジストリで管理するようにしても良いかも // ただ、「emailVerified が true なユーザーを find する」のようなクエリは書けなくなるからウーン diff --git a/src/server/api/endpoints/i/notifications.ts b/src/server/api/endpoints/i/notifications.ts index 0c5586054f..fcabbbc3dd 100644 --- a/src/server/api/endpoints/i/notifications.ts +++ b/src/server/api/endpoints/i/notifications.ts @@ -4,7 +4,7 @@ import { readNotification } from '../../common/read-notification'; import define from '../../define'; import { makePaginationQuery } from '../../common/make-pagination-query'; import { Notifications, Followings, Mutings, Users } from '@/models/index'; -import { notificationTypes } from '../../../../types'; +import { notificationTypes } from '@/types'; import read from '@/services/note/read'; export const meta = { diff --git a/src/server/api/endpoints/i/update.ts b/src/server/api/endpoints/i/update.ts index fb7e12760e..9dd637251d 100644 --- a/src/server/api/endpoints/i/update.ts +++ b/src/server/api/endpoints/i/update.ts @@ -13,7 +13,7 @@ import { ApiError } from '../../error'; import { Users, DriveFiles, UserProfiles, Pages } from '@/models/index'; import { User } from '@/models/entities/user'; import { UserProfile } from '@/models/entities/user-profile'; -import { notificationTypes } from '../../../../types'; +import { notificationTypes } from '@/types'; import { normalizeForSearch } from '@/misc/normalize-for-search'; export const meta = { From 8006e7a34d4894e7ae91e5301f9d4f8fe50473de Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 9 Oct 2021 13:12:41 +0900 Subject: [PATCH 053/107] =?UTF-8?q?feat(client):=20=E9=80=9A=E7=9F=A5?= =?UTF-8?q?=E3=83=9A=E3=83=BC=E3=82=B8=E3=81=A7=E9=80=9A=E7=9F=A5=E3=81=AE?= =?UTF-8?q?=E7=A8=AE=E9=A1=9E=E3=81=AB=E3=82=88=E3=82=8B=E3=83=95=E3=82=A3?= =?UTF-8?q?=E3=83=AB=E3=82=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + src/client/components/ui/menu.vue | 22 +++++++++++++++------- src/client/pages/notifications.vue | 27 ++++++++++++++++++++++++++- 3 files changed, 42 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6550db9803..36cbcb01de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ - クライアント: アニメーションを減らす設定をメニューのアニメーションにも適用するように - クライアント: MFM関数構文のサジェストを実装 - クライアント: 未読の通知のみ表示する機能 +- クライアント: 通知ページで通知の種類によるフィルタ - ActivityPub: HTML -> MFMの変換を強化 - API: i/notifications に unreadOnly オプションを追加 - API: ap系のエンドポイントをログイン必須化+レートリミット追加 diff --git a/src/client/components/ui/menu.vue b/src/client/components/ui/menu.vue index a55dccdd71..d144e65c83 100644 --- a/src/client/components/ui/menu.vue +++ b/src/client/components/ui/menu.vue @@ -27,7 +27,7 @@ <MkAvatar :user="item.user" class="avatar"/><MkUserName :user="item.user"/> <span v-if="item.indicate" class="indicator"><i class="fas fa-circle"></i></span> </button> - <button v-else @click="clicked(item.action, $event)" :tabindex="i" class="_button item" :class="{ danger: item.danger }"> + <button v-else @click="clicked(item.action, $event)" :tabindex="i" class="_button item" :class="{ danger: item.danger, active: item.active }" :disabled="item.active"> <i v-if="item.icon" class="fa-fw" :class="item.icon"></i> <MkAvatar v-if="item.avatar" :user="item.avatar" class="avatar"/> <span>{{ item.text }}</span> @@ -175,6 +175,10 @@ export default defineComponent({ border-radius: 6px; } + > * { + position: relative; + } + &.danger { color: #ff2a2a; @@ -195,7 +199,16 @@ export default defineComponent({ } } - &:hover { + &.active { + color: var(--fgOnAccent); + opacity: 1; + + &:before { + background: var(--accent); + } + } + + &:not(:disabled):hover { color: var(--accent); text-decoration: none; @@ -204,11 +217,6 @@ export default defineComponent({ } } - &:active { - //color: var(--fgOnAccent); - //background: var(--accentDarken); - } - &:not(:active):focus-visible { box-shadow: 0 0 0 2px var(--focus) inset; } diff --git a/src/client/pages/notifications.vue b/src/client/pages/notifications.vue index 9728b87bf1..c6d7ea9b31 100644 --- a/src/client/pages/notifications.vue +++ b/src/client/pages/notifications.vue @@ -2,7 +2,7 @@ <div> <MkHeader :info="header"/> <div class="clupoqwt" v-size="{ min: [800] }"> - <XNotifications class="notifications" @before="before" @after="after" :unread-only="tab === 'unread'"/> + <XNotifications class="notifications" @before="before" @after="after" :include-types="includeTypes" :unread-only="tab === 'unread'"/> </div> </div> </template> @@ -13,6 +13,7 @@ import Progress from '@client/scripts/loading'; import XNotifications from '@client/components/notifications.vue'; import * as os from '@client/os'; import * as symbols from '@client/symbols'; +import { notificationTypes } from '@/types'; export default defineComponent({ components: { @@ -27,11 +28,17 @@ export default defineComponent({ bg: 'var(--bg)', }, tab: 'all', + includeTypes: null, header: computed(() => ({ title: this.$ts.notifications, icon: 'fas fa-bell', bg: 'var(--bg)', actions: [{ + text: this.$ts.filter, + icon: 'fas fa-filter', + highlighted: this.includeTypes != null, + handler: this.setFilter, + }, { text: this.$ts.markAllAsRead, icon: 'fas fa-check', handler: () => { @@ -58,6 +65,24 @@ export default defineComponent({ after() { Progress.done(); + }, + + setFilter(ev) { + const typeItems = notificationTypes.map(t => ({ + text: this.$t(`_notification._types.${t}`), + active: this.includeTypes && this.includeTypes.includes(t), + action: () => { + this.includeTypes = [t]; + } + })); + const items = this.includeTypes != null ? [{ + icon: 'fas fa-times', + text: this.$ts.clear, + action: () => { + this.includeTypes = null; + } + }, null, ...typeItems] : typeItems; + os.popupMenu(items, ev.currentTarget || ev.target); } } }); From 79a591d72dfebb21f8c47dd5c6d515d62fcfafb1 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 9 Oct 2021 14:29:49 +0900 Subject: [PATCH 054/107] Update ja-JP.yml --- locales/ja-JP.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index eb72c7ca17..9a5ec30787 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -793,6 +793,7 @@ itsOn: "オンになっています" itsOff: "オフになっています" emailRequiredForSignup: "アカウント登録にメールアドレスを必須にする" unread: "未読" +filter: "フィルタ" _signup: almostThere: "ほとんど完了です" From b77167a4a122d8dfce3abdd10e340e56a03ac43c Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 9 Oct 2021 14:29:57 +0900 Subject: [PATCH 055/107] :art: --- src/client/components/ui/menu.vue | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/client/components/ui/menu.vue b/src/client/components/ui/menu.vue index d144e65c83..88c4ce5b99 100644 --- a/src/client/components/ui/menu.vue +++ b/src/client/components/ui/menu.vue @@ -170,9 +170,10 @@ export default defineComponent({ left: 0; right: 0; margin: auto; - width: calc(100% - 16px); + //width: calc(100% - 16px); + width: 100%; height: 100%; - border-radius: 6px; + //border-radius: 6px; } > * { From 6b22b7a31f45b2d6d8fae1552caf70aac9e4e2c1 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 9 Oct 2021 14:43:40 +0900 Subject: [PATCH 056/107] :art: --- src/client/pages/instance/emoji-edit-dialog.vue | 6 +++--- src/client/pages/settings/deck.vue | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/client/pages/instance/emoji-edit-dialog.vue b/src/client/pages/instance/emoji-edit-dialog.vue index bd8b2061ae..4854c69884 100644 --- a/src/client/pages/instance/emoji-edit-dialog.vue +++ b/src/client/pages/instance/emoji-edit-dialog.vue @@ -11,13 +11,13 @@ <div class="_monolithic_"> <div class="yigymqpb _section"> <img :src="emoji.url" class="img"/> - <MkInput v-model="name"> + <MkInput class="_formBlock" v-model="name"> <template #label>{{ $ts.name }}</template> </MkInput> - <MkInput v-model="category" :datalist="categories"> + <MkInput class="_formBlock" v-model="category" :datalist="categories"> <template #label>{{ $ts.category }}</template> </MkInput> - <MkInput v-model="aliases"> + <MkInput class="_formBlock" v-model="aliases"> <template #label>{{ $ts.tags }}</template> <template #caption>{{ $ts.setMultipleBySeparatingWithSpace }}</template> </MkInput> diff --git a/src/client/pages/settings/deck.vue b/src/client/pages/settings/deck.vue index 2b49ef956c..e4b5c697c4 100644 --- a/src/client/pages/settings/deck.vue +++ b/src/client/pages/settings/deck.vue @@ -31,10 +31,10 @@ <script lang="ts"> import { defineComponent } from 'vue'; -import FormSwitch from '@client/components/form/switch.vue'; +import FormSwitch from '@client/components/debobigego/switch.vue'; import FormLink from '@client/components/debobigego/link.vue'; -import FormRadios from '@client/components/form/radios.vue'; -import FormInput from '@client/components/form/input.vue'; +import FormRadios from '@client/components/debobigego/radios.vue'; +import FormInput from '@client/components/debobigego/input.vue'; import FormBase from '@client/components/debobigego/base.vue'; import FormGroup from '@client/components/debobigego/group.vue'; import { deckStore } from '@client/ui/deck/deck-store'; From 49b43eb3c829fe10ea09e2c78fd774bfd99e0419 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 9 Oct 2021 14:47:52 +0900 Subject: [PATCH 057/107] :art: --- src/client/components/global/header.vue | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/client/components/global/header.vue b/src/client/components/global/header.vue index 977912e2bd..7953d8a6d6 100644 --- a/src/client/components/global/header.vue +++ b/src/client/components/global/header.vue @@ -91,11 +91,15 @@ export default defineComponent({ bg.setAlpha(0.85); this.bg = bg.toRgbString(); - if (this.$el.parentElement == null) return; - this.narrow = this.$el.parentElement.offsetWidth < 500; - new ResizeObserver((entries, observer) => { + if (this.$el.parentElement) { this.narrow = this.$el.parentElement.offsetWidth < 500; - }).observe(this.$el.parentElement); + new ResizeObserver((entries, observer) => { + this.narrow = this.$el.parentElement.offsetWidth < 500; + }).observe(this.$el.parentElement); + const currentStickyTop = getComputedStyle(this.$el).getPropertyValue('--stickyTop') || '0px'; + this.$el.style.setProperty('--stickyTop', currentStickyTop); + this.$el.parentElement.style.setProperty('--stickyTop', `calc(${currentStickyTop} + ${this.$el.offsetHeight}px)`); + } }, methods: { From c33e93c66282839c3e721d651720a7573da41b25 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 10 Oct 2021 15:19:16 +0900 Subject: [PATCH 058/107] improve ui --- locales/ja-JP.yml | 2 + src/client/account.ts | 74 +++++- src/client/components/ui/folder.vue | 23 +- src/client/components/ui/super-menu.vue | 151 +++++++++++++ src/client/pages/emojis.category.vue | 1 - src/client/pages/explore.vue | 48 ++-- src/client/pages/instance/database.vue | 3 +- src/client/pages/instance/email-settings.vue | 3 +- src/client/pages/instance/files-settings.vue | 3 +- src/client/pages/instance/index.link.vue | 97 -------- src/client/pages/instance/index.vue | 225 ++++++++++++++----- src/client/pages/instance/instance-block.vue | 3 +- src/client/pages/instance/integrations.vue | 3 +- src/client/pages/instance/object-storage.vue | 3 +- src/client/pages/instance/other-settings.vue | 3 +- src/client/pages/instance/overview.vue | 10 - src/client/pages/instance/proxy-account.vue | 3 +- src/client/pages/instance/queue.vue | 1 + src/client/pages/instance/relays.vue | 1 + src/client/pages/instance/security.vue | 3 +- src/client/pages/instance/service-worker.vue | 3 +- src/client/pages/instance/settings.vue | 3 +- src/client/pages/instance/users.vue | 2 + src/client/pages/settings/index.link.vue | 105 --------- src/client/pages/settings/index.vue | 181 ++++++++++----- src/client/ui/_common_/sidebar.vue | 68 +----- src/client/ui/chat/index.vue | 3 + src/client/ui/default.header.vue | 68 +----- src/client/ui/default.sidebar.vue | 68 +----- 29 files changed, 609 insertions(+), 552 deletions(-) create mode 100644 src/client/components/ui/super-menu.vue delete mode 100644 src/client/pages/instance/index.link.vue delete mode 100644 src/client/pages/settings/index.link.vue diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 9a5ec30787..5798ce7ec2 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -794,6 +794,8 @@ itsOff: "オフになっています" emailRequiredForSignup: "アカウント登録にメールアドレスを必須にする" unread: "未読" filter: "フィルタ" +controllPanel: "コントロールパネル" +manageAccounts: "アカウントを管理" _signup: almostThere: "ほとんど完了です" diff --git a/src/client/account.ts b/src/client/account.ts index 6e26ac1f7d..a3fe082a22 100644 --- a/src/client/account.ts +++ b/src/client/account.ts @@ -1,9 +1,10 @@ import { del, get, set } from '@client/scripts/idb-proxy'; import { reactive } from 'vue'; import { apiUrl } from '@client/config'; -import { waiting } from '@client/os'; +import { waiting, api, popup, popupMenu, success } from '@client/os'; import { unisonReload, reloadChannel } from '@client/scripts/unison-reload'; import { showSuspendedDialog } from './scripts/show-suspended-dialog'; +import { i18n } from './i18n'; // TODO: 他のタブと永続化されたstateを同期 @@ -129,6 +130,77 @@ export async function login(token: Account['token'], redirect?: string) { unisonReload(); } +export async function openAccountMenu(ev: MouseEvent) { + function showSigninDialog() { + popup(import('@client/components/signin-dialog.vue'), {}, { + done: res => { + addAccount(res.id, res.i); + success(); + }, + }, 'closed'); + } + + function createAccount() { + popup(import('@client/components/signup-dialog.vue'), {}, { + done: res => { + addAccount(res.id, res.i); + switchAccountWithToken(res.i); + }, + }, 'closed'); + } + + async function switchAccount(account: any) { + const storedAccounts = await getAccounts(); + const token = storedAccounts.find(x => x.id === account.id).token; + switchAccountWithToken(token); + } + + function switchAccountWithToken(token: string) { + login(token); + } + + const storedAccounts = await getAccounts().then(accounts => accounts.filter(x => x.id !== $i.id)); + const accountsPromise = api('users/show', { userIds: storedAccounts.map(x => x.id) }); + + const accountItemPromises = storedAccounts.map(a => new Promise(res => { + accountsPromise.then(accounts => { + const account = accounts.find(x => x.id === a.id); + if (account == null) return res(null); + res({ + type: 'user', + user: account, + action: () => { switchAccount(account); } + }); + }); + })); + + popupMenu([...[{ + type: 'link', + text: i18n.locale.profile, + to: `/@${ $i.username }`, + avatar: $i, + }, null, ...accountItemPromises, { + icon: 'fas fa-plus', + text: i18n.locale.addAccount, + action: () => { + popupMenu([{ + text: i18n.locale.existingAccount, + action: () => { showSigninDialog(); }, + }, { + text: i18n.locale.createAccount, + action: () => { createAccount(); }, + }], ev.currentTarget || ev.target); + }, + }, { + type: 'link', + icon: 'fas fa-users', + text: i18n.locale.manageAccounts, + to: `/settings/accounts`, + }]], ev.currentTarget || ev.target, { + align: 'left' + }); +} + // このファイルに書きたくないけどここに書かないと何故かVeturが認識しない declare module '@vue/runtime-core' { interface ComponentCustomProperties { diff --git a/src/client/components/ui/folder.vue b/src/client/components/ui/folder.vue index eecf1d8be1..cc3da083c5 100644 --- a/src/client/components/ui/folder.vue +++ b/src/client/components/ui/folder.vue @@ -1,6 +1,6 @@ <template> <div class="ssazuxis" v-size="{ max: [500] }"> - <header @click="showBody = !showBody" class="_button"> + <header @click="showBody = !showBody" class="_button" :style="{ background: bg }"> <div class="title"><slot name="header"></slot></div> <div class="divider"></div> <button class="_button"> @@ -23,6 +23,7 @@ <script lang="ts"> import { defineComponent } from 'vue'; +import * as tinycolor from 'tinycolor2'; const localStoragePrefix = 'ui:folder:'; @@ -41,6 +42,7 @@ export default defineComponent({ }, data() { return { + bg: null, showBody: (this.persistKey && localStorage.getItem(localStoragePrefix + this.persistKey)) ? localStorage.getItem(localStoragePrefix + this.persistKey) === 't' : this.expanded, }; }, @@ -51,6 +53,21 @@ export default defineComponent({ } } }, + mounted() { + function getParentBg(el: Element | null): string { + if (el == null || el.tagName === 'BODY') return 'var(--bg)'; + const bg = el.style.background || el.style.backgroundColor; + if (bg) { + return bg; + } else { + return getParentBg(el.parentElement); + } + } + const rawBg = getParentBg(this.$el); + const bg = tinycolor(rawBg.startsWith('var(') ? getComputedStyle(document.documentElement).getPropertyValue(rawBg.slice(4, -1)) : rawBg); + bg.setAlpha(0.85); + this.bg = bg.toRgbString(); + }, methods: { toggleContent(show: boolean) { this.showBody = show; @@ -100,12 +117,8 @@ export default defineComponent({ position: sticky; top: var(--stickyTop, 0px); padding: var(--x-padding); - background: var(--x-header, var(--panel)); - /* TODO panelの半透明バージョンをプログラマティックに作りたい - background: var(--X17); -webkit-backdrop-filter: var(--blur, blur(8px)); backdrop-filter: var(--blur, blur(20px)); - */ > .title { margin: 0; diff --git a/src/client/components/ui/super-menu.vue b/src/client/components/ui/super-menu.vue new file mode 100644 index 0000000000..f43b545fd6 --- /dev/null +++ b/src/client/components/ui/super-menu.vue @@ -0,0 +1,151 @@ +<template> +<div class="rrevdjwu" :class="{ grid }"> + <div class="group" v-for="group in def"> + <div class="title" v-if="group.title">{{ group.title }}</div> + + <div class="items"> + <template v-for="(item, i) in group.items"> + <a v-if="item.type === 'a'" :href="item.href" :target="item.target" :tabindex="i" class="_button item" :class="{ danger: item.danger, active: item.active }"> + <i v-if="item.icon" class="icon fa-fw" :class="item.icon"></i> + <span class="text">{{ item.text }}</span> + </a> + <button v-else-if="item.type === 'button'" @click="ev => item.action(ev)" :tabindex="i" class="_button item" :class="{ danger: item.danger, active: item.active }" :disabled="item.active"> + <i v-if="item.icon" class="icon fa-fw" :class="item.icon"></i> + <span class="text">{{ item.text }}</span> + </button> + <MkA v-else :to="item.to" :tabindex="i" class="_button item" :class="{ danger: item.danger, active: item.active }"> + <i v-if="item.icon" class="icon fa-fw" :class="item.icon"></i> + <span class="text">{{ item.text }}</span> + </MkA> + </template> + </div> + </div> +</div> +</template> + +<script lang="ts"> +import { defineComponent, ref, unref } from 'vue'; + +export default defineComponent({ + props: { + def: { + type: Array, + required: true + }, + grid: { + type: Boolean, + required: false, + default: false, + }, + }, +}); +</script> + +<style lang="scss" scoped> +.rrevdjwu { + > .group { + & + .group { + margin-top: 16px; + padding-top: 16px; + border-top: solid 0.5px var(--divider); + } + + margin-left: 16px; + margin-right: 16px; + + > .title { + font-size: 0.9em; + opacity: 0.7; + margin: 0 0 8px 12px; + } + + > .items { + > .item { + display: flex; + align-items: center; + width: 100%; + box-sizing: border-box; + padding: 10px 16px 10px 14px; + border-radius: 999px; + font-size: 0.9em; + + &:hover { + text-decoration: none; + background: var(--panelHighlight); + } + + &.active { + color: var(--accent); + background: var(--accentedBg); + } + + &.danger { + color: var(--error); + } + + > .icon { + width: 32px; + margin-right: 2px; + flex-shrink: 0; + text-align: center; + opacity: 0.8; + } + + > .text { + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + padding-right: 12px; + } + + } + } + } + + &.grid { + > .group { + & + .group { + padding-top: 0; + border-top: none; + } + + margin-left: 0; + margin-right: 0; + + > .title { + font-size: 1em; + opacity: 0.7; + margin: 0 0 8px 16px; + } + + > .items { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(110px, 1fr)); + grid-gap: 8px; + padding: 0 16px; + + > .item { + flex-direction: column; + padding: 18px 16px 16px 16px; + background: var(--panel); + border-radius: 8px; + text-align: center; + + > .icon { + display: block; + margin-right: 0; + margin-bottom: 12px; + font-size: 1.5em; + } + + > .text { + padding-right: 0; + width: 100%; + font-size: 0.8em; + } + } + } + } + } +} +</style> diff --git a/src/client/pages/emojis.category.vue b/src/client/pages/emojis.category.vue index d7737523d2..e725bcb31f 100644 --- a/src/client/pages/emojis.category.vue +++ b/src/client/pages/emojis.category.vue @@ -122,7 +122,6 @@ export default defineComponent({ } > .emojis { - --x-header: var(--bg); --x-padding: 0 16px; .zuvgdzyt { diff --git a/src/client/pages/explore.vue b/src/client/pages/explore.vue index 15ebf8efad..26ce412612 100644 --- a/src/client/pages/explore.vue +++ b/src/client/pages/explore.vue @@ -3,16 +3,7 @@ <MkHeader :info="header"/> <div class="lznhrdub _root"> - <div> - <div class="_isolated"> - <MkInput v-model="query" :debounce="true" type="search"> - <template #prefix><i class="fas fa-search"></i></template> - <template #label>{{ $ts.searchUser }}</template> - </MkInput> - </div> - - <XUserList v-if="query" class="_gap" :pagination="searchPagination" ref="search"/> - + <div v-if="tab === 'local'"> <div class="localfedi7 _block _isolated" v-if="meta && stats && tag == null" :style="{ backgroundImage: meta.bannerUrl ? `url(${meta.bannerUrl})` : null }"> <header><span>{{ $t('explore', { host: meta.name || 'Misskey' }) }}</span></header> <div><span>{{ $t('exploreUsersCount', { count: num(stats.originalUsersCount) }) }}</span></div> @@ -37,7 +28,7 @@ </MkFolder> </template> </div> - <div> + <div v-else-if="tab === 'remote'"> <div class="localfedi7 _block _isolated" v-if="tag == null" :style="{ backgroundImage: `url(/static-assets/client/fedi.jpg)` }"> <header><span>{{ $ts.exploreFediverse }}</span></header> </div> @@ -71,6 +62,16 @@ </MkFolder> </template> </div> + <div v-else-if="tab === 'search'"> + <div class="_isolated"> + <MkInput v-model="query" :debounce="true" type="search"> + <template #prefix><i class="fas fa-search"></i></template> + <template #label>{{ $ts.searchUser }}</template> + </MkInput> + </div> + + <XUserList v-if="query" class="_gap" :pagination="searchPagination" ref="search"/> + </div> </div> </div> </template> @@ -102,12 +103,28 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.explore, - icon: 'fas fa-hashtag' + icon: 'fas fa-hashtag', + bg: 'var(--bg)', }, - header: { + tab: 'local', + header: computed(() => ({ title: this.$ts.explore, - icon: 'fas fa-hashtag' - }, + icon: 'fas fa-hashtag', + bg: 'var(--bg)', + tabs: [{ + active: this.tab === 'local', + title: this.$ts.local, + onClick: () => { this.tab = 'local'; }, + }, { + active: this.tab === 'remote', + title: this.$ts.remote, + onClick: () => { this.tab = 'remote'; }, + }, { + active: this.tab === 'search', + title: this.$ts.search, + onClick: () => { this.tab = 'search'; }, + },] + })), pinnedUsers: { endpoint: 'pinned-users' }, popularUsers: { endpoint: 'users', limit: 10, noPaging: true, params: { state: 'alive', @@ -200,6 +217,7 @@ export default defineComponent({ .lznhrdub { max-width: 1400px; margin: 0 auto; + padding: 16px; } .localfedi7 { diff --git a/src/client/pages/instance/database.vue b/src/client/pages/instance/database.vue index a8a1e9a54a..ffbeed8b30 100644 --- a/src/client/pages/instance/database.vue +++ b/src/client/pages/instance/database.vue @@ -43,7 +43,8 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.database, - icon: 'fas fa-database' + icon: 'fas fa-database', + bg: 'var(--bg)', }, databasePromiseFactory: () => os.api('admin/get-table-stats', {}).then(res => Object.entries(res).sort((a, b) => b[1].size - a[1].size)), } diff --git a/src/client/pages/instance/email-settings.vue b/src/client/pages/instance/email-settings.vue index 251354a43a..ebf724fcdd 100644 --- a/src/client/pages/instance/email-settings.vue +++ b/src/client/pages/instance/email-settings.vue @@ -66,7 +66,8 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.emailServer, - icon: 'fas fa-envelope' + icon: 'fas fa-envelope', + bg: 'var(--bg)', }, enableEmail: false, email: null, diff --git a/src/client/pages/instance/files-settings.vue b/src/client/pages/instance/files-settings.vue index 8bf4613a76..8aefa9e90d 100644 --- a/src/client/pages/instance/files-settings.vue +++ b/src/client/pages/instance/files-settings.vue @@ -56,7 +56,8 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.files, - icon: 'fas fa-cloud' + icon: 'fas fa-cloud', + bg: 'var(--bg)', }, cacheRemoteFiles: false, proxyRemoteFiles: false, diff --git a/src/client/pages/instance/index.link.vue b/src/client/pages/instance/index.link.vue deleted file mode 100644 index e1f4773800..0000000000 --- a/src/client/pages/instance/index.link.vue +++ /dev/null @@ -1,97 +0,0 @@ -<template> -<div class="qmfkfnzh"> - <a class="main _button" :href="to" target="_blank" v-if="external"> - <span class="icon"><slot name="icon"></slot></span> - <span class="text"><slot></slot></span> - </a> - <MkA class="main _button" :class="{ active }" :to="to" :behavior="behavior" v-else> - <span class="icon"><slot name="icon"></slot></span> - <span class="text"><slot></slot></span> - </MkA> -</div> -</template> - -<script lang="ts"> -import { defineComponent } from 'vue'; - -export default defineComponent({ - props: { - to: { - type: String, - required: true - }, - active: { - type: Boolean, - required: false - }, - external: { - type: Boolean, - required: false - }, - behavior: { - type: String, - required: false, - }, - }, - data() { - return { - }; - } -}); -</script> - -<style lang="scss" scoped> -.qmfkfnzh { - > .main { - display: flex; - align-items: center; - width: 100%; - box-sizing: border-box; - padding: 10px 16px 10px 14px; - border-radius: 999px; - font-size: 0.9em; - - &:hover { - text-decoration: none; - background: var(--panelHighlight); - } - - &.active { - color: var(--accent); - background: var(--accentedBg); - } - - > .icon { - width: 32px; - margin-right: 2px; - flex-shrink: 0; - text-align: center; - opacity: 0.8; - - &:empty { - display: none; - - & + .text { - padding-left: 4px; - } - } - } - - > .text { - white-space: nowrap; - text-overflow: ellipsis; - overflow: hidden; - padding-right: 12px; - } - - > .right { - margin-left: auto; - opacity: 0.7; - - > .text:not(:empty) { - margin-right: 0.75em; - } - } - } -} -</style> diff --git a/src/client/pages/instance/index.vue b/src/client/pages/instance/index.vue index 959c4be6cd..e2cb5b8f58 100644 --- a/src/client/pages/instance/index.vue +++ b/src/client/pages/instance/index.vue @@ -1,47 +1,15 @@ <template> <div class="hiyeyicy" :class="{ wide: !narrow }" ref="el"> <div class="nav" v-if="!narrow || page == null"> - <div class="group"> - <div class="lxpfedzu"> - <img :src="$instance.iconUrl || '/favicon.ico'" alt="" class="icon"/> - </div> - <XLink :active="page === 'overview'" replace to="/instance/overview"><template #icon><i class="fas fa-tachometer-alt"></i></template>{{ $ts.overview }}</XLink> - </div> - <div class="group"> - <div class="label">{{ $ts.quickAction }}</div> - <FormButton @click="lookup"><i class="fas fa-search"></i> {{ $ts.lookup }}</FormButton> - <FormButton v-if="$instance.disableRegistration" @click="invite"><i class="fas fa-user"></i> {{ $ts.invite }}</FormButton> - </div> - <div class="group"> - <div class="label">{{ $ts.administration }}</div> - <XLink :active="page === 'users'" replace to="/instance/users"><template #icon><i class="fas fa-users"></i></template>{{ $ts.users }}</XLink> - <XLink :active="page === 'emojis'" replace to="/instance/emojis"><template #icon><i class="fas fa-laugh"></i></template>{{ $ts.customEmojis }}</XLink> - <XLink :active="page === 'federation'" replace to="/instance/federation"><template #icon><i class="fas fa-globe"></i></template>{{ $ts.federation }}</XLink> - <XLink :active="page === 'queue'" replace to="/instance/queue"><template #icon><i class="fas fa-clipboard-list"></i></template>{{ $ts.jobQueue }}</XLink> - <XLink :active="page === 'files'" replace to="/instance/files"><template #icon><i class="fas fa-cloud"></i></template>{{ $ts.files }}</XLink> - <XLink :active="page === 'announcements'" replace to="/instance/announcements"><template #icon><i class="fas fa-broadcast-tower"></i></template>{{ $ts.announcements }}</XLink> - <XLink :active="page === 'ads'" replace to="/instance/ads"><template #icon><i class="fas fa-audio-description"></i></template>{{ $ts.ads }}</XLink> - <XLink :active="page === 'abuses'" replace to="/instance/abuses"><template #icon><i class="fas fa-exclamation-circle"></i></template>{{ $ts.abuseReports }}</XLink> - </div> - <div class="group"> - <div class="label">{{ $ts.settings }}</div> - <XLink :active="page === 'settings'" replace to="/instance/settings"><template #icon><i class="fas fa-cog"></i></template>{{ $ts.general }}</XLink> - <XLink :active="page === 'files-settings'" replace to="/instance/files-settings"><template #icon><i class="fas fa-cloud"></i></template>{{ $ts.files }}</XLink> - <XLink :active="page === 'email-settings'" replace to="/instance/email-settings"><template #icon><i class="fas fa-envelope"></i></template>{{ $ts.emailServer }}</XLink> - <XLink :active="page === 'object-storage'" replace to="/instance/object-storage"><template #icon><i class="fas fa-cloud"></i></template>{{ $ts.objectStorage }}</XLink> - <XLink :active="page === 'security'" replace to="/instance/security"><template #icon><i class="fas fa-lock"></i></template>{{ $ts.security }}</XLink> - <XLink :active="page === 'service-worker'" replace to="/instance/service-worker"><template #icon><i class="fas fa-bolt"></i></template>ServiceWorker</XLink> - <XLink :active="page === 'relays'" replace to="/instance/relays"><template #icon><i class="fas fa-globe"></i></template>{{ $ts.relays }}</XLink> - <XLink :active="page === 'integrations'" replace to="/instance/integrations"><template #icon><i class="fas fa-share-alt"></i></template>{{ $ts.integration }}</XLink> - <XLink :active="page === 'instance-block'" replace to="/instance/instance-block"><template #icon><i class="fas fa-ban"></i></template>{{ $ts.instanceBlocking }}</XLink> - <XLink :active="page === 'proxy-account'" replace to="/instance/proxy-account"><template #icon><i class="fas fa-ghost"></i></template>{{ $ts.proxyAccount }}</XLink> - <XLink :active="page === 'other-settings'" replace to="/instance/other-settings"><template #icon><i class="fas fa-cogs"></i></template>{{ $ts.other }}</XLink> - </div> - <div class="group"> - <div class="label">{{ $ts.info }}</div> - <XLink :active="page === 'database'" replace to="/instance/database"><template #icon><i class="fas fa-database"></i></template>{{ $ts.database }}</XLink> - <XLink :active="page === 'logs'" replace to="/instance/logs"><template #icon><i class="fas fa-stream"></i></template>{{ $ts.logs }}</XLink> + <MkHeader :info="header"></MkHeader> + + <MkInfo v-if="noMaintainerInformation" warn class="info">{{ $ts.noMaintainerInformationWarning }} <MkA to="/instance/settings" class="_link">{{ $ts.configure }}</MkA></MkInfo> + <MkInfo v-if="noBotProtection" warn class="info">{{ $ts.noBotProtectionWarning }} <MkA to="/instance/bot-protection" class="_link">{{ $ts.configure }}</MkA></MkInfo> + + <div class="lxpfedzu"> + <img :src="$instance.iconUrl || '/favicon.ico'" alt="" class="icon"/> </div> + <MkSuperMenu :def="menuDef" :grid="page == null"></MkSuperMenu> </div> <div class="main"> <component :is="component" :key="page" @info="onInfo" v-bind="pageProps"/> @@ -52,11 +20,13 @@ <script lang="ts"> import { computed, defineAsyncComponent, defineComponent, nextTick, onMounted, reactive, ref, watch } from 'vue'; import { i18n } from '@client/i18n'; -import XLink from './index.link.vue'; +import MkSuperMenu from '@client/components/ui/super-menu.vue'; import FormGroup from '@client/components/debobigego/group.vue'; import FormBase from '@client/components/debobigego/base.vue'; import FormButton from '@client/components/debobigego/button.vue'; +import MkInfo from '@client/components/ui/info.vue'; import { scroll } from '@client/scripts/scroll'; +import { instance } from '@client/instance'; import * as symbols from '@client/symbols'; import * as os from '@client/os'; import { lookupUser } from '@client/scripts/lookup-user'; @@ -64,9 +34,10 @@ import { lookupUser } from '@client/scripts/lookup-user'; export default defineComponent({ components: { FormBase, - XLink, + MkSuperMenu, FormGroup, FormButton, + MkInfo, }, props: { @@ -91,6 +62,151 @@ export default defineComponent({ INFO.value = viewInfo; }; const pageProps = ref({}); + + const isEmpty = (x: any) => x == null || x == ''; + + const noMaintainerInformation = ref(false); + const noBotProtection = ref(false); + + os.api('meta', { detail: true }).then(meta => { + // TODO: 設定が完了しても残ったままになるので、ストリーミングでmeta更新イベントを受け取ってよしなに更新する + noMaintainerInformation.value = isEmpty(meta.maintainerName) || isEmpty(meta.maintainerEmail); + noBotProtection.value = !meta.enableHcaptcha && !meta.enableRecaptcha; + }); + + const menuDef = computed(() => [{ + title: i18n.locale.quickAction, + items: [{ + type: 'button', + icon: 'fas fa-search', + text: i18n.locale.lookup, + action: lookup, + }, ...(instance.disableRegistration ? [{ + type: 'button', + icon: 'fas fa-user', + text: i18n.locale.invite, + action: invite, + }] : [])], + }, { + title: i18n.locale.administration, + items: [{ + icon: 'fas fa-tachometer-alt', + text: i18n.locale.dashboard, + to: '/instance/overview', + active: page.value === 'overview', + }, { + icon: 'fas fa-users', + text: i18n.locale.users, + to: '/instance/users', + active: page.value === 'users', + }, { + icon: 'fas fa-laugh', + text: i18n.locale.customEmojis, + to: '/instance/emojis', + active: page.value === 'emojis', + }, { + icon: 'fas fa-globe', + text: i18n.locale.federation, + to: '/instance/federation', + active: page.value === 'federation', + }, { + icon: 'fas fa-clipboard-list', + text: i18n.locale.jobQueue, + to: '/instance/queue', + active: page.value === 'queue', + }, { + icon: 'fas fa-cloud', + text: i18n.locale.files, + to: '/instance/files', + active: page.value === 'files', + }, { + icon: 'fas fa-broadcast-tower', + text: i18n.locale.announcements, + to: '/instance/announcements', + active: page.value === 'announcements', + }, { + icon: 'fas fa-audio-description', + text: i18n.locale.ads, + to: '/instance/ads', + active: page.value === 'ads', + }, { + icon: 'fas fa-exclamation-circle', + text: i18n.locale.abuseReports, + to: '/instance/abuses', + active: page.value === 'abuses', + }], + }, { + title: i18n.locale.settings, + items: [{ + icon: 'fas fa-cog', + text: i18n.locale.general, + to: '/instance/settings', + active: page.value === 'settings', + }, { + icon: 'fas fa-cloud', + text: i18n.locale.files, + to: '/instance/files-settings', + active: page.value === 'files-settings', + }, { + icon: 'fas fa-envelope', + text: i18n.locale.emailServer, + to: '/instance/email-settings', + active: page.value === 'email-settings', + }, { + icon: 'fas fa-cloud', + text: i18n.locale.objectStorage, + to: '/instance/object-storage', + active: page.value === 'object-storage', + }, { + icon: 'fas fa-lock', + text: i18n.locale.security, + to: '/instance/security', + active: page.value === 'security', + }, { + icon: 'fas fa-bolt', + text: 'ServiceWorker', + to: '/instance/service-worker', + active: page.value === 'service-worker', + }, { + icon: 'fas fa-globe', + text: i18n.locale.relays, + to: '/instance/relays', + active: page.value === 'relays', + }, { + icon: 'fas fa-share-alt', + text: i18n.locale.integration, + to: '/instance/integrations', + active: page.value === 'integrations', + }, { + icon: 'fas fa-ban', + text: i18n.locale.instanceBlocking, + to: '/instance/instance-block', + active: page.value === 'instance-block', + }, { + icon: 'fas fa-ghost', + text: i18n.locale.proxyAccount, + to: '/instance/proxy-account', + active: page.value === 'proxy-account', + }, { + icon: 'fas fa-cogs', + text: i18n.locale.other, + to: '/instance/other-settings', + active: page.value === 'other-settings', + }], + }, { + title: i18n.locale.info, + items: [{ + icon: 'fas fa-database', + text: i18n.locale.database, + to: '/instance/database', + active: page.value === 'database', + }, { + icon: 'fas fa-stream', + text: i18n.locale.logs, + to: '/instance/logs', + active: page.value === 'logs', + }], + }]); const component = computed(() => { if (page.value == null) return null; switch (page.value) { @@ -193,6 +309,12 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: INFO, + menuDef, + header: { + title: i18n.locale.controllPanel, + }, + noMaintainerInformation, + noBotProtection, page, narrow, view, @@ -216,20 +338,11 @@ export default defineComponent({ > .nav { width: 32%; - max-width: 320px; + max-width: 280px; box-sizing: border-box; border-right: solid 0.5px var(--divider); overflow: auto; - - > .group { - padding: 16px; - - > .label { - font-size: 0.9em; - opacity: 0.7; - margin: 0 0 8px 12px; - } - } + height: 100%; } > .main { @@ -238,6 +351,12 @@ export default defineComponent({ --baseContentWidth: 100%; } } + + > .nav { + > .info { + margin: 16px; + } + } } .lxpfedzu { diff --git a/src/client/pages/instance/instance-block.vue b/src/client/pages/instance/instance-block.vue index a00970d85b..105cdb4941 100644 --- a/src/client/pages/instance/instance-block.vue +++ b/src/client/pages/instance/instance-block.vue @@ -43,7 +43,8 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.instanceBlocking, - icon: 'fas fa-ban' + icon: 'fas fa-ban', + bg: 'var(--bg)', }, blockedHosts: '', } diff --git a/src/client/pages/instance/integrations.vue b/src/client/pages/instance/integrations.vue index bfd9e2f349..6964ae5704 100644 --- a/src/client/pages/instance/integrations.vue +++ b/src/client/pages/instance/integrations.vue @@ -49,7 +49,8 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.integration, - icon: 'fas fa-share-alt' + icon: 'fas fa-share-alt', + bg: 'var(--bg)', }, enableTwitterIntegration: false, enableGithubIntegration: false, diff --git a/src/client/pages/instance/object-storage.vue b/src/client/pages/instance/object-storage.vue index ba6a249685..2d765270e6 100644 --- a/src/client/pages/instance/object-storage.vue +++ b/src/client/pages/instance/object-storage.vue @@ -91,7 +91,8 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.objectStorage, - icon: 'fas fa-cloud' + icon: 'fas fa-cloud', + bg: 'var(--bg)', }, useObjectStorage: false, objectStorageBaseUrl: null, diff --git a/src/client/pages/instance/other-settings.vue b/src/client/pages/instance/other-settings.vue index b9f9ce30f7..4e55df41fb 100644 --- a/src/client/pages/instance/other-settings.vue +++ b/src/client/pages/instance/other-settings.vue @@ -49,7 +49,8 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.other, - icon: 'fas fa-cogs' + icon: 'fas fa-cogs', + bg: 'var(--bg)', }, summalyProxy: '', deeplAuthKey: '', diff --git a/src/client/pages/instance/overview.vue b/src/client/pages/instance/overview.vue index 61fbc03c64..c6db9d0c04 100644 --- a/src/client/pages/instance/overview.vue +++ b/src/client/pages/instance/overview.vue @@ -1,9 +1,6 @@ <template> <FormBase> <FormSuspense :p="init"> - <FormInfo v-if="noMaintainerInformation" warn>{{ $ts.noMaintainerInformationWarning }} <MkA to="/instance/settings" class="_link">{{ $ts.configure }}</MkA></FormInfo> - <FormInfo v-if="noBotProtection" warn>{{ $ts.noBotProtectionWarning }} <MkA to="/instance/bot-protection" class="_link">{{ $ts.configure }}</MkA></FormInfo> - <FormSuspense :p="fetchStats" v-slot="{ result: stats }"> <FormGroup> <FormKeyValueView> @@ -98,8 +95,6 @@ export default defineComponent({ fetchServerInfo: () => os.api('admin/server-info', {}), fetchJobs: () => os.api('admin/queue/deliver-delayed', {}), fetchModLogs: () => os.api('admin/show-moderation-logs', {}), - noMaintainerInformation: false, - noBotProtection: false, } }, @@ -110,11 +105,6 @@ export default defineComponent({ methods: { async init() { this.meta = await os.api('meta', { detail: true }); - - const isEmpty = (x: any) => x == null || x == ''; - - this.noMaintainerInformation = isEmpty(this.meta.maintainerName) || isEmpty(this.meta.maintainerEmail); - this.noBotProtection = !this.meta.enableHcaptcha && !this.meta.enableRecaptcha; }, async showInstanceInfo(q) { diff --git a/src/client/pages/instance/proxy-account.vue b/src/client/pages/instance/proxy-account.vue index a80ecccb05..b1ece19710 100644 --- a/src/client/pages/instance/proxy-account.vue +++ b/src/client/pages/instance/proxy-account.vue @@ -46,7 +46,8 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.proxyAccount, - icon: 'fas fa-ghost' + icon: 'fas fa-ghost', + bg: 'var(--bg)', }, proxyAccount: null, proxyAccountId: null, diff --git a/src/client/pages/instance/queue.vue b/src/client/pages/instance/queue.vue index 031bda2bed..f88825eb19 100644 --- a/src/client/pages/instance/queue.vue +++ b/src/client/pages/instance/queue.vue @@ -34,6 +34,7 @@ export default defineComponent({ [symbols.PAGE_INFO]: { title: this.$ts.jobQueue, icon: 'fas fa-clipboard-list', + bg: 'var(--bg)', }, connection: markRaw(os.stream.useChannel('queueStats')), } diff --git a/src/client/pages/instance/relays.vue b/src/client/pages/instance/relays.vue index cb9b27a625..7d7888eaa8 100644 --- a/src/client/pages/instance/relays.vue +++ b/src/client/pages/instance/relays.vue @@ -36,6 +36,7 @@ export default defineComponent({ [symbols.PAGE_INFO]: { title: this.$ts.relays, icon: 'fas fa-globe', + bg: 'var(--bg)', }, relays: [], inbox: '', diff --git a/src/client/pages/instance/security.vue b/src/client/pages/instance/security.vue index 2b525261ae..a854b6dbd0 100644 --- a/src/client/pages/instance/security.vue +++ b/src/client/pages/instance/security.vue @@ -47,7 +47,8 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.security, - icon: 'fas fa-lock' + icon: 'fas fa-lock', + bg: 'var(--bg)', }, enableHcaptcha: false, enableRecaptcha: false, diff --git a/src/client/pages/instance/service-worker.vue b/src/client/pages/instance/service-worker.vue index 9fa10def07..430e02ad2e 100644 --- a/src/client/pages/instance/service-worker.vue +++ b/src/client/pages/instance/service-worker.vue @@ -51,7 +51,8 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: 'ServiceWorker', - icon: 'fas fa-bolt' + icon: 'fas fa-bolt', + bg: 'var(--bg)', }, enableServiceWorker: false, swPublicKey: null, diff --git a/src/client/pages/instance/settings.vue b/src/client/pages/instance/settings.vue index 2ce9361d08..c9a948da7d 100644 --- a/src/client/pages/instance/settings.vue +++ b/src/client/pages/instance/settings.vue @@ -84,7 +84,8 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: { title: this.$ts.general, - icon: 'fas fa-cog' + icon: 'fas fa-cog', + bg: 'var(--bg)', }, name: null, description: null, diff --git a/src/client/pages/instance/users.vue b/src/client/pages/instance/users.vue index 69242f3786..f7f9306b70 100644 --- a/src/client/pages/instance/users.vue +++ b/src/client/pages/instance/users.vue @@ -29,9 +29,11 @@ </div> <div class="inputs"> <MkInput v-model="searchUsername" style="flex: 1;" type="text" spellcheck="false" @update:modelValue="$refs.users.reload()"> + <template #prefix>@</template> <template #label>{{ $ts.username }}</template> </MkInput> <MkInput v-model="searchHost" style="flex: 1;" type="text" spellcheck="false" @update:modelValue="$refs.users.reload()" :disabled="pagination.params().origin === 'local'"> + <template #prefix>@</template> <template #label>{{ $ts.host }}</template> </MkInput> </div> diff --git a/src/client/pages/settings/index.link.vue b/src/client/pages/settings/index.link.vue deleted file mode 100644 index 895efffc9c..0000000000 --- a/src/client/pages/settings/index.link.vue +++ /dev/null @@ -1,105 +0,0 @@ -<template> -<div class="qmfkfnzj"> - <a v-if="external" class="main _button" :href="to" target="_blank"> - <span class="icon"><slot name="icon"></slot></span> - <span class="text"><slot></slot></span> - </a> - <MkA v-else-if="to" class="main _button" :class="{ active }" :to="to" :behavior="behavior"> - <span class="icon"><slot name="icon"></slot></span> - <span class="text"><slot></slot></span> - </MkA> - <button v-else class="main _button button" :class="{ danger }"> - <span class="icon"><slot name="icon"></slot></span> - <span class="text"><slot></slot></span> - </button> -</div> -</template> - -<script lang="ts"> -import { defineComponent } from 'vue'; - -export default defineComponent({ - props: { - to: { - type: String, - required: false - }, - active: { - type: Boolean, - required: false - }, - danger: { - type: Boolean, - required: false - }, - external: { - type: Boolean, - required: false - }, - behavior: { - type: String, - required: false, - }, - }, -}); -</script> - -<style lang="scss" scoped> -.qmfkfnzj { - > .main { - display: flex; - align-items: center; - width: 100%; - box-sizing: border-box; - padding: 10px 16px 10px 14px; - border-radius: 999px; - font-size: 0.9em; - - &:hover { - text-decoration: none; - background: var(--panelHighlight); - } - - &.active { - color: var(--accent); - background: var(--accentedBg); - } - - &.danger { - color: var(--error); - } - - > .icon { - width: 32px; - margin-right: 2px; - flex-shrink: 0; - text-align: center; - opacity: 0.8; - - &:empty { - display: none; - - & + .text { - padding-left: 4px; - } - } - } - - > .text { - white-space: nowrap; - text-overflow: ellipsis; - overflow: hidden; - padding-right: 12px; - } - - > .right { - margin-left: auto; - opacity: 0.7; - - > .text:not(:empty) { - margin-right: 0.75em; - } - } - } -} -</style> diff --git a/src/client/pages/settings/index.vue b/src/client/pages/settings/index.vue index 2d5ced2181..39b9a85618 100644 --- a/src/client/pages/settings/index.vue +++ b/src/client/pages/settings/index.vue @@ -1,42 +1,9 @@ <template> <div class="vvcocwet" :class="{ wide: !narrow }" ref="el"> <div class="nav" v-if="!narrow || page == null"> - <div class="group accounts"> - <MkAvatar :user="$i" class="avatar"/> - <XLink :active="page === 'accounts'" replace to="/settings/accounts"><template #icon><i class="fas fa-users"></i></template>{{ $ts.accounts }}</XLink> - </div> + <div class="title">{{ $ts.settings }}</div> <MkInfo v-if="emailNotConfigured" warn class="info">{{ $ts.emailNotConfiguredWarning }} <MkA to="/settings/email" class="_link">{{ $ts.configure }}</MkA></MkInfo> - <div class="group"> - <div class="label">{{ $ts.basicSettings }}</div> - <XLink :active="page === 'profile'" replace to="/settings/profile"><template #icon><i class="fas fa-user"></i></template>{{ $ts.profile }}</XLink> - <XLink :active="page === 'privacy'" replace to="/settings/privacy"><template #icon><i class="fas fa-lock-open"></i></template>{{ $ts.privacy }}</XLink> - <XLink :active="page === 'reaction'" replace to="/settings/reaction"><template #icon><i class="fas fa-laugh"></i></template>{{ $ts.reaction }}</XLink> - <XLink :active="page === 'drive'" replace to="/settings/drive"><template #icon><i class="fas fa-cloud"></i></template>{{ $ts.drive }}</XLink> - <XLink :active="page === 'notifications'" replace to="/settings/notifications"><template #icon><i class="fas fa-bell"></i></template>{{ $ts.notifications }}</XLink> - <XLink :active="page === 'email'" replace to="/settings/email"><template #icon><i class="fas fa-envelope"></i></template>{{ $ts.email }}</XLink> - <XLink :active="page === 'integration'" replace to="/settings/integration"><template #icon><i class="fas fa-share-alt"></i></template>{{ $ts.integration }}</XLink> - <XLink :active="page === 'security'" replace to="/settings/security"><template #icon><i class="fas fa-lock"></i></template>{{ $ts.security }}</XLink> - </div> - <div class="group"> - <div class="label">{{ $ts.clientSettings }}</div> - <XLink :active="page === 'general'" replace to="/settings/general"><template #icon><i class="fas fa-cogs"></i></template>{{ $ts.general }}</XLink> - <XLink :active="page === 'theme'" replace to="/settings/theme"><template #icon><i class="fas fa-palette"></i></template>{{ $ts.theme }}</XLink> - <XLink :active="page === 'menu'" replace to="/settings/menu"><template #icon><i class="fas fa-list-ul"></i></template>{{ $ts.menu }}</XLink> - <XLink :active="page === 'sounds'" replace to="/settings/sounds"><template #icon><i class="fas fa-music"></i></template>{{ $ts.sounds }}</XLink> - <XLink :active="page === 'plugin'" replace to="/settings/plugin"><template #icon><i class="fas fa-plug"></i></template>{{ $ts.plugins }}</XLink> - </div> - <div class="group"> - <div class="label">{{ $ts.otherSettings }}</div> - <XLink :active="page === 'import-export'" replace to="/settings/import-export"><template #icon><i class="fas fa-boxes"></i></template>{{ $ts.importAndExport }}</XLink> - <XLink :active="page === 'mute-block'" replace to="/settings/mute-block"><template #icon><i class="fas fa-ban"></i></template>{{ $ts.muteAndBlock }}</XLink> - <XLink :active="page === 'word-mute'" replace to="/settings/word-mute"><template #icon><i class="fas fa-comment-slash"></i></template>{{ $ts.wordMute }}</XLink> - <XLink :active="page === 'api'" replace to="/settings/api"><template #icon><i class="fas fa-key"></i></template>API</XLink> - <XLink :active="page === 'other'" replace to="/settings/other"><template #icon><i class="fas fa-ellipsis-h"></i></template>{{ $ts.other }}</XLink> - </div> - <div class="group"> - <XLink @click="clear"><template #icon><i class="fas fa-trash"></i></template>{{ $ts.clearCache }}</XLink> - <XLink @click="logout" danger><template #icon><i class="fas fa-sign-in-alt fa-flip-horizontal"></i></template>{{ $ts.logout }}</XLink> - </div> + <MkSuperMenu :def="menuDef" :grid="page == null"></MkSuperMenu> </div> <div class="main"> <component :is="component" :key="page" v-bind="pageProps"/> @@ -47,8 +14,8 @@ <script lang="ts"> import { computed, defineAsyncComponent, defineComponent, nextTick, onMounted, reactive, ref, watch } from 'vue'; import { i18n } from '@client/i18n'; -import XLink from './index.link.vue'; import MkInfo from '@client/components/ui/info.vue'; +import MkSuperMenu from '@client/components/ui/super-menu.vue'; import { scroll } from '@client/scripts/scroll'; import { signout } from '@client/account'; import { unisonReload } from '@client/scripts/unison-reload'; @@ -58,8 +25,8 @@ import { $i } from '@client/account'; export default defineComponent({ components: { - XLink, MkInfo, + MkSuperMenu, }, props: { @@ -71,7 +38,7 @@ export default defineComponent({ setup(props, context) { const indexInfo = { - title: i18n.locale.settings, + title: i18n.locale.controllPanel, icon: 'fas fa-cog', bg: 'var(--bg)', }; @@ -80,6 +47,125 @@ export default defineComponent({ const narrow = ref(false); const view = ref(null); const el = ref(null); + const menuDef = computed(() => [{ + title: i18n.locale.basicSettings, + items: [{ + icon: 'fas fa-user', + text: i18n.locale.profile, + to: '/settings/profile', + active: page.value === 'profile', + }, { + icon: 'fas fa-lock-open', + text: i18n.locale.privacy, + to: '/settings/privacy', + active: page.value === 'privacy', + }, { + icon: 'fas fa-laugh', + text: i18n.locale.reaction, + to: '/settings/reaction', + active: page.value === 'reaction', + }, { + icon: 'fas fa-cloud', + text: i18n.locale.drive, + to: '/settings/drive', + active: page.value === 'drive', + }, { + icon: 'fas fa-bell', + text: i18n.locale.notifications, + to: '/settings/notifications', + active: page.value === 'notifications', + }, { + icon: 'fas fa-envelope', + text: i18n.locale.email, + to: '/settings/email', + active: page.value === 'email', + }, { + icon: 'fas fa-share-alt', + text: i18n.locale.integration, + to: '/settings/integration', + active: page.value === 'integration', + }, { + icon: 'fas fa-lock', + text: i18n.locale.security, + to: '/settings/security', + active: page.value === 'security', + }], + }, { + title: i18n.locale.clientSettings, + items: [{ + icon: 'fas fa-cogs', + text: i18n.locale.general, + to: '/settings/general', + active: page.value === 'general', + }, { + icon: 'fas fa-palette', + text: i18n.locale.theme, + to: '/settings/theme', + active: page.value === 'theme', + }, { + icon: 'fas fa-list-ul', + text: i18n.locale.menu, + to: '/settings/menu', + active: page.value === 'menu', + }, { + icon: 'fas fa-music', + text: i18n.locale.sounds, + to: '/settings/sounds', + active: page.value === 'sounds', + }, { + icon: 'fas fa-plug', + text: i18n.locale.plugins, + to: '/settings/plugin', + active: page.value === 'plugin', + }], + }, { + title: i18n.locale.otherSettings, + items: [{ + icon: 'fas fa-boxes', + text: i18n.locale.importAndExport, + to: '/settings/import-export', + active: page.value === 'import-export', + }, { + icon: 'fas fa-ban', + text: i18n.locale.muteAndBlock, + to: '/settings/mute-block', + active: page.value === 'mute-block', + }, { + icon: 'fas fa-comment-slash', + text: i18n.locale.wordMute, + to: '/settings/word-mute', + active: page.value === 'word-mute', + }, { + icon: 'fas fa-key', + text: 'API', + to: '/settings/api', + active: page.value === 'api', + }, { + icon: 'fas fa-ellipsis-h', + text: i18n.locale.other, + to: '/settings/other', + active: page.value === 'other', + }], + }, { + items: [{ + type: 'button', + icon: 'fas fa-trash', + text: i18n.locale.clearCache, + action: () => { + localStorage.removeItem('locale'); + localStorage.removeItem('theme'); + unisonReload(); + }, + }, { + type: 'button', + icon: 'fas fa-sign-in-alt fa-flip-horizontal', + text: i18n.locale.logout, + action: () => { + signout(); + }, + danger: true, + },], + }]); const pageProps = ref({}); const component = computed(() => { @@ -170,20 +256,13 @@ export default defineComponent({ return { [symbols.PAGE_INFO]: INFO, page, + menuDef, narrow, view, el, pageProps, component, emailNotConfigured, - logout: () => { - signout(); - }, - clear: () => { - localStorage.removeItem('locale'); - localStorage.removeItem('theme'); - unisonReload(); - }, }; }, }); @@ -192,16 +271,6 @@ export default defineComponent({ <style lang="scss" scoped> .vvcocwet { > .nav { - > .group { - padding: 16px; - - > .label { - font-size: 0.9em; - opacity: 0.7; - margin: 0 0 8px 12px; - } - } - > .info { margin: 0 16px; } diff --git a/src/client/ui/_common_/sidebar.vue b/src/client/ui/_common_/sidebar.vue index 4bb7bbd985..33aea31c39 100644 --- a/src/client/ui/_common_/sidebar.vue +++ b/src/client/ui/_common_/sidebar.vue @@ -50,7 +50,7 @@ import { host } from '@client/config'; import { search } from '@client/scripts/search'; import * as os from '@client/os'; import { menuDef } from '@client/menu'; -import { getAccounts, addAccount, login } from '@client/account'; +import { openAccountMenu } from '@client/account'; export default defineComponent({ props: { @@ -134,76 +134,12 @@ export default defineComponent({ search(); }, - async openAccountMenu(ev) { - const storedAccounts = await getAccounts().then(accounts => accounts.filter(x => x.id !== this.$i.id)); - const accountsPromise = os.api('users/show', { userIds: storedAccounts.map(x => x.id) }); - - const accountItemPromises = storedAccounts.map(a => new Promise(res => { - accountsPromise.then(accounts => { - const account = accounts.find(x => x.id === a.id); - if (account == null) return res(null); - res({ - type: 'user', - user: account, - action: () => { this.switchAccount(account); } - }); - }); - })); - - os.popupMenu([...[{ - type: 'link', - text: this.$ts.profile, - to: `/@${ this.$i.username }`, - avatar: this.$i, - }, null, ...accountItemPromises, { - icon: 'fas fa-plus', - text: this.$ts.addAccount, - action: () => { - os.popupMenu([{ - text: this.$ts.existingAccount, - action: () => { this.addAccount(); }, - }, { - text: this.$ts.createAccount, - action: () => { this.createAccount(); }, - }], ev.currentTarget || ev.target); - }, - }]], ev.currentTarget || ev.target, { - align: 'left' - }); - }, - more(ev) { os.popup(import('@client/components/launch-pad.vue'), {}, { }, 'closed'); }, - addAccount() { - os.popup(import('@client/components/signin-dialog.vue'), {}, { - done: res => { - addAccount(res.id, res.i); - os.success(); - }, - }, 'closed'); - }, - - createAccount() { - os.popup(import('@client/components/signup-dialog.vue'), {}, { - done: res => { - addAccount(res.id, res.i); - this.switchAccountWithToken(res.i); - }, - }, 'closed'); - }, - - async switchAccount(account: any) { - const storedAccounts = await getAccounts(); - const token = storedAccounts.find(x => x.id === account.id).token; - this.switchAccountWithToken(token); - }, - - switchAccountWithToken(token: string) { - login(token); - }, + openAccountMenu, } }); </script> diff --git a/src/client/ui/chat/index.vue b/src/client/ui/chat/index.vue index 4194a9919f..4c068b0d94 100644 --- a/src/client/ui/chat/index.vue +++ b/src/client/ui/chat/index.vue @@ -109,6 +109,7 @@ import { search } from '@client/scripts/search'; import copyToClipboard from '@client/scripts/copy-to-clipboard'; import { store } from './store'; import * as symbols from '@client/symbols'; +import { openAccountMenu } from '@client/account'; export default defineComponent({ components: { @@ -253,6 +254,8 @@ export default defineComponent({ } }], e); }, + + openAccountMenu, } }); </script> diff --git a/src/client/ui/default.header.vue b/src/client/ui/default.header.vue index 75c5c0c051..c64b6cf695 100644 --- a/src/client/ui/default.header.vue +++ b/src/client/ui/default.header.vue @@ -44,7 +44,7 @@ import { host } from '@client/config'; import { search } from '@client/scripts/search'; import * as os from '@client/os'; import { menuDef } from '@client/menu'; -import { getAccounts, addAccount, login } from '@client/account'; +import { openAccountMenu } from '@client/account'; import MkButton from '@client/components/ui/button.vue'; export default defineComponent({ @@ -100,76 +100,12 @@ export default defineComponent({ search(); }, - async openAccountMenu(ev) { - const storedAccounts = await getAccounts().then(accounts => accounts.filter(x => x.id !== this.$i.id)); - const accountsPromise = os.api('users/show', { userIds: storedAccounts.map(x => x.id) }); - - const accountItemPromises = storedAccounts.map(a => new Promise(res => { - accountsPromise.then(accounts => { - const account = accounts.find(x => x.id === a.id); - if (account == null) return res(null); - res({ - type: 'user', - user: account, - action: () => { this.switchAccount(account); } - }); - }); - })); - - os.popupMenu([...[{ - type: 'link', - text: this.$ts.profile, - to: `/@${ this.$i.username }`, - avatar: this.$i, - }, null, ...accountItemPromises, { - icon: 'fas fa-plus', - text: this.$ts.addAccount, - action: () => { - os.popupMenu([{ - text: this.$ts.existingAccount, - action: () => { this.addAccount(); }, - }, { - text: this.$ts.createAccount, - action: () => { this.createAccount(); }, - }], ev.currentTarget || ev.target); - }, - }]], ev.currentTarget || ev.target, { - align: 'left' - }); - }, - more(ev) { os.popup(import('@client/components/launch-pad.vue'), {}, { }, 'closed'); }, - addAccount() { - os.popup(import('@client/components/signin-dialog.vue'), {}, { - done: res => { - addAccount(res.id, res.i); - os.success(); - }, - }, 'closed'); - }, - - createAccount() { - os.popup(import('@client/components/signup-dialog.vue'), {}, { - done: res => { - addAccount(res.id, res.i); - this.switchAccountWithToken(res.i); - }, - }, 'closed'); - }, - - async switchAccount(account: any) { - const storedAccounts = await getAccounts(); - const token = storedAccounts.find(x => x.id === account.id).token; - this.switchAccountWithToken(token); - }, - - switchAccountWithToken(token: string) { - login(token); - }, + openAccountMenu, } }); </script> diff --git a/src/client/ui/default.sidebar.vue b/src/client/ui/default.sidebar.vue index be907aa2a4..745ad2d602 100644 --- a/src/client/ui/default.sidebar.vue +++ b/src/client/ui/default.sidebar.vue @@ -46,7 +46,7 @@ import { host } from '@client/config'; import { search } from '@client/scripts/search'; import * as os from '@client/os'; import { menuDef } from '@client/menu'; -import { getAccounts, addAccount, login } from '@client/account'; +import { openAccountMenu } from '@client/account'; import MkButton from '@client/components/ui/button.vue'; import { StickySidebar } from '@client/scripts/sticky-sidebar'; import MisskeyLogo from '@/../assets/client/misskey.svg'; @@ -120,76 +120,12 @@ export default defineComponent({ search(); }, - async openAccountMenu(ev) { - const storedAccounts = await getAccounts().then(accounts => accounts.filter(x => x.id !== this.$i.id)); - const accountsPromise = os.api('users/show', { userIds: storedAccounts.map(x => x.id) }); - - const accountItemPromises = storedAccounts.map(a => new Promise(res => { - accountsPromise.then(accounts => { - const account = accounts.find(x => x.id === a.id); - if (account == null) return res(null); - res({ - type: 'user', - user: account, - action: () => { this.switchAccount(account); } - }); - }); - })); - - os.popupMenu([...[{ - type: 'link', - text: this.$ts.profile, - to: `/@${ this.$i.username }`, - avatar: this.$i, - }, null, ...accountItemPromises, { - icon: 'fas fa-plus', - text: this.$ts.addAccount, - action: () => { - os.popupMenu([{ - text: this.$ts.existingAccount, - action: () => { this.addAccount(); }, - }, { - text: this.$ts.createAccount, - action: () => { this.createAccount(); }, - }], ev.currentTarget || ev.target); - }, - }]], ev.currentTarget || ev.target, { - align: 'left' - }); - }, - more(ev) { os.popup(import('@client/components/launch-pad.vue'), {}, { }, 'closed'); }, - addAccount() { - os.popup(import('@client/components/signin-dialog.vue'), {}, { - done: res => { - addAccount(res.id, res.i); - os.success(); - }, - }, 'closed'); - }, - - createAccount() { - os.popup(import('@client/components/signup-dialog.vue'), {}, { - done: res => { - addAccount(res.id, res.i); - this.switchAccountWithToken(res.i); - }, - }, 'closed'); - }, - - async switchAccount(account: any) { - const storedAccounts = await getAccounts(); - const token = storedAccounts.find(x => x.id === account.id).token; - this.switchAccountWithToken(token); - }, - - switchAccountWithToken(token: string) { - login(token); - }, + openAccountMenu, } }); </script> From 46424f63f21b264745a07fdd3d6481a83f99ab87 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 10 Oct 2021 17:47:57 +0900 Subject: [PATCH 059/107] =?UTF-8?q?fix(client):=20=E3=83=94=E3=83=B3?= =?UTF-8?q?=E7=95=99=E3=82=81=E3=83=A6=E3=83=BC=E3=82=B6=E3=83=BC=E3=81=AE?= =?UTF-8?q?=E8=A8=AD=E5=AE=9A=E9=A0=85=E7=9B=AE=E3=81=8C=E3=81=AA=E3=81=84?= =?UTF-8?q?=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + src/client/pages/instance/settings.vue | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 36cbcb01de..d0158910c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ - クライアント: MFM関数構文のサジェストを実装 - クライアント: 未読の通知のみ表示する機能 - クライアント: 通知ページで通知の種類によるフィルタ +- クライアント: ピン留めユーザーの設定項目がない問題を修正 - ActivityPub: HTML -> MFMの変換を強化 - API: i/notifications に unreadOnly オプションを追加 - API: ap系のエンドポイントをログイン必須化+レートリミット追加 diff --git a/src/client/pages/instance/settings.vue b/src/client/pages/instance/settings.vue index c9a948da7d..7bd363e5f3 100644 --- a/src/client/pages/instance/settings.vue +++ b/src/client/pages/instance/settings.vue @@ -38,6 +38,11 @@ <span>{{ $ts.maintainerEmail }}</span> </FormInput> + <FormTextarea v-model="pinnedUsers"> + <span>{{ $ts.pinnedUsers }}</span> + <template #desc>{{ $ts.pinnedUsersDescription }}</template> + </FormTextarea> + <FormInput v-model="maxNoteTextLength" type="number"> <template #prefix><i class="fas fa-pencil-alt"></i></template> <span>{{ $ts.maxNoteTextLength }}</span> @@ -98,6 +103,7 @@ export default defineComponent({ maxNoteTextLength: 0, enableLocalTimeline: false, enableGlobalTimeline: false, + pinnedUsers: '', } }, @@ -119,6 +125,7 @@ export default defineComponent({ this.maxNoteTextLength = meta.maxNoteTextLength; this.enableLocalTimeline = !meta.disableLocalTimeline; this.enableGlobalTimeline = !meta.disableGlobalTimeline; + this.pinnedUsers = meta.pinnedUsers.join('\n'); }, save() { @@ -134,6 +141,7 @@ export default defineComponent({ maxNoteTextLength: this.maxNoteTextLength, disableLocalTimeline: !this.enableLocalTimeline, disableGlobalTimeline: !this.enableGlobalTimeline, + pinnedUsers: this.pinnedUsers.split('\n'), }).then(() => { fetchInstance(); }); From 94bf7101f87591d7a961570c02638b550f89d49d Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 10 Oct 2021 17:48:07 +0900 Subject: [PATCH 060/107] :art: --- src/client/pages/instance/index.vue | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/client/pages/instance/index.vue b/src/client/pages/instance/index.vue index e2cb5b8f58..7b07bf2dde 100644 --- a/src/client/pages/instance/index.vue +++ b/src/client/pages/instance/index.vue @@ -2,13 +2,14 @@ <div class="hiyeyicy" :class="{ wide: !narrow }" ref="el"> <div class="nav" v-if="!narrow || page == null"> <MkHeader :info="header"></MkHeader> - - <MkInfo v-if="noMaintainerInformation" warn class="info">{{ $ts.noMaintainerInformationWarning }} <MkA to="/instance/settings" class="_link">{{ $ts.configure }}</MkA></MkInfo> - <MkInfo v-if="noBotProtection" warn class="info">{{ $ts.noBotProtectionWarning }} <MkA to="/instance/bot-protection" class="_link">{{ $ts.configure }}</MkA></MkInfo> <div class="lxpfedzu"> <img :src="$instance.iconUrl || '/favicon.ico'" alt="" class="icon"/> </div> + + <MkInfo v-if="noMaintainerInformation" warn class="info">{{ $ts.noMaintainerInformationWarning }} <MkA to="/instance/settings" class="_link">{{ $ts.configure }}</MkA></MkInfo> + <MkInfo v-if="noBotProtection" warn class="info">{{ $ts.noBotProtectionWarning }} <MkA to="/instance/bot-protection" class="_link">{{ $ts.configure }}</MkA></MkInfo> + <MkSuperMenu :def="menuDef" :grid="page == null"></MkSuperMenu> </div> <div class="main"> @@ -360,7 +361,7 @@ export default defineComponent({ } .lxpfedzu { - padding: 16px; + margin: 16px; > .icon { display: block; From a0c9fd75d77b56d0e39549ca912df8e3480f0ea7 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 10 Oct 2021 19:54:15 +0900 Subject: [PATCH 061/107] fix ui --- src/client/components/global/header.vue | 146 +++++++++++++----------- 1 file changed, 80 insertions(+), 66 deletions(-) diff --git a/src/client/components/global/header.vue b/src/client/components/global/header.vue index 7953d8a6d6..32a332cf69 100644 --- a/src/client/components/global/header.vue +++ b/src/client/components/global/header.vue @@ -1,5 +1,5 @@ <template> -<div class="fdidabkb" :class="{ slim: narrow, thin }" :style="{ background: bg }" @click="onClick"> +<div class="fdidabkb" :class="{ slim: narrow, thin }" :style="{ background: bg }" @click="onClick" ref="el"> <template v-if="info"> <div class="titleContainer" @click="showTabsPopup"> <i v-if="info.icon" class="icon" :class="info.icon"></i> @@ -37,12 +37,13 @@ </template> <script lang="ts"> -import { defineComponent } from 'vue'; +import { computed, defineComponent, onMounted, PropType, ref } from 'vue'; import * as tinycolor from 'tinycolor2'; import { popupMenu } from '@client/os'; import { url } from '@client/config'; import { scrollToTop } from '@client/scripts/scroll'; import MkButton from '@client/components/ui/button.vue'; +import { i18n } from '../../i18n'; export default defineComponent({ components: { @@ -51,6 +52,10 @@ export default defineComponent({ props: { info: { + type: Object as PropType<{ + actions?: {}[]; + tabs?: {}[]; + }>, required: true }, menu: { @@ -62,99 +67,108 @@ export default defineComponent({ }, }, - data() { - return { - bg: null, - narrow: false, - height: 0, - }; - }, - - computed: { - hasTabs(): boolean { - return this.info.tabs && this.info.tabs.length > 0; - }, - - shouldShowMenu() { - if (this.info == null) return false; - if (this.info.actions != null && this.narrow) return true; - if (this.info.menu != null) return true; - if (this.info.share != null) return true; - if (this.menu != null) return true; + setup(props) { + const el = ref<HTMLElement>(null); + const bg = ref(null); + const narrow = ref(false); + const height = ref(0); + const hasTabs = computed(() => { + return props.info.tabs && props.info.tabs.length > 0; + }); + const shouldShowMenu = computed(() => { + if (props.info == null) return false; + if (props.info.actions != null && narrow.value) return true; + if (props.info.menu != null) return true; + if (props.info.share != null) return true; + if (props.menu != null) return true; return false; - } - }, + }); - mounted() { - const rawBg = this.info?.bg || 'var(--bg)'; - const bg = tinycolor(rawBg.startsWith('var(') ? getComputedStyle(document.documentElement).getPropertyValue(rawBg.slice(4, -1)) : rawBg); - bg.setAlpha(0.85); - this.bg = bg.toRgbString(); - - if (this.$el.parentElement) { - this.narrow = this.$el.parentElement.offsetWidth < 500; - new ResizeObserver((entries, observer) => { - this.narrow = this.$el.parentElement.offsetWidth < 500; - }).observe(this.$el.parentElement); - const currentStickyTop = getComputedStyle(this.$el).getPropertyValue('--stickyTop') || '0px'; - this.$el.style.setProperty('--stickyTop', currentStickyTop); - this.$el.parentElement.style.setProperty('--stickyTop', `calc(${currentStickyTop} + ${this.$el.offsetHeight}px)`); - } - }, - - methods: { - share() { + const share = () => { navigator.share({ - url: url + this.info.path, - ...this.info.share, + url: url + props.info.path, + ...props.info.share, }); - }, + }; - showMenu(ev) { - let menu = this.info.menu ? this.info.menu() : []; - if (this.narrow && this.info.actions) { - menu = [...this.info.actions.map(x => ({ + const showMenu = (ev: MouseEvent) => { + let menu = props.info.menu ? props.info.menu() : []; + if (narrow.value && props.info.actions) { + menu = [...props.info.actions.map(x => ({ text: x.text, icon: x.icon, action: x.handler })), menu.length > 0 ? null : undefined, ...menu]; } - if (this.info.share) { + if (props.info.share) { if (menu.length > 0) menu.push(null); menu.push({ - text: this.$ts.share, + text: i18n.locale.share, icon: 'fas fa-share-alt', - action: this.share + action: share }); } - if (this.menu) { + if (props.menu) { if (menu.length > 0) menu.push(null); - menu = menu.concat(this.menu); + menu = menu.concat(props.menu); } popupMenu(menu, ev.currentTarget || ev.target); - }, + }; - showTabsPopup(ev) { - if (!this.hasTabs) return; - if (!this.narrow) return; + const showTabsPopup = (ev: MouseEvent) => { + if (!hasTabs.value) return; + if (!narrow.value) return; ev.preventDefault(); ev.stopPropagation(); - const menu = this.info.tabs.map(tab => ({ + const menu = props.info.tabs.map(tab => ({ text: tab.title, icon: tab.icon, action: tab.onClick, })); popupMenu(menu, ev.currentTarget || ev.target); - }, + }; - preventDrag(ev) { + const preventDrag = (ev: TouchEvent) => { ev.stopPropagation(); - }, + }; - onClick(ev) { - scrollToTop(this.$el, { behavior: 'smooth' }); - } - } + const onClick = () => { + scrollToTop(el.value, { behavior: 'smooth' }); + }; + + onMounted(() => { + const rawBg = props.info?.bg || 'var(--bg)'; + const tinyBg = tinycolor(rawBg.startsWith('var(') ? getComputedStyle(document.documentElement).getPropertyValue(rawBg.slice(4, -1)) : rawBg); + tinyBg.setAlpha(0.85); + bg.value = tinyBg.toRgbString(); + + if (el.value.parentElement) { + narrow.value = el.value.parentElement.offsetWidth < 500; + new ResizeObserver((entries, observer) => { + narrow.value = el.value.parentElement.offsetWidth < 500; + }).observe(el.value.parentElement); + setTimeout(() => { + const currentStickyTop = getComputedStyle(el.value.parentElement).getPropertyValue('--stickyTop') || '0px'; + el.value.style.setProperty('--stickyTop', currentStickyTop); + el.value.parentElement.style.setProperty('--stickyTop', `calc(${currentStickyTop} + ${el.value.offsetHeight}px)`); + }, 100); // レンダリング順序の関係で親のstickyTopの設定が少し遅れることがあるため + } + }); + + return { + el, + bg, + narrow, + height, + hasTabs, + shouldShowMenu, + share, + showMenu, + showTabsPopup, + preventDrag, + onClick, + }; + }, }); </script> From 816493e01ffe613ba4eca1d56a71a8cbf1099bf6 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Mon, 11 Oct 2021 00:36:47 +0900 Subject: [PATCH 062/107] :art: --- src/client/components/global/header.vue | 15 ++++++++++++--- src/client/events.ts | 4 ++++ src/client/pages/settings/import-export.vue | 2 +- src/client/scripts/theme.ts | 4 ++++ 4 files changed, 21 insertions(+), 4 deletions(-) create mode 100644 src/client/events.ts diff --git a/src/client/components/global/header.vue b/src/client/components/global/header.vue index 32a332cf69..346bf42611 100644 --- a/src/client/components/global/header.vue +++ b/src/client/components/global/header.vue @@ -37,13 +37,14 @@ </template> <script lang="ts"> -import { computed, defineComponent, onMounted, PropType, ref } from 'vue'; +import { computed, defineComponent, onMounted, onUnmounted, PropType, ref } from 'vue'; import * as tinycolor from 'tinycolor2'; import { popupMenu } from '@client/os'; import { url } from '@client/config'; import { scrollToTop } from '@client/scripts/scroll'; import MkButton from '@client/components/ui/button.vue'; -import { i18n } from '../../i18n'; +import { i18n } from '@client/i18n'; +import { globalEvents } from '@client/events'; export default defineComponent({ components: { @@ -136,11 +137,19 @@ export default defineComponent({ scrollToTop(el.value, { behavior: 'smooth' }); }; - onMounted(() => { + const calcBg = () => { const rawBg = props.info?.bg || 'var(--bg)'; const tinyBg = tinycolor(rawBg.startsWith('var(') ? getComputedStyle(document.documentElement).getPropertyValue(rawBg.slice(4, -1)) : rawBg); tinyBg.setAlpha(0.85); bg.value = tinyBg.toRgbString(); + }; + + onMounted(() => { + calcBg(); + globalEvents.on('themeChanged', calcBg); + onUnmounted(() => { + globalEvents.off('themeChanged', calcBg); + }); if (el.value.parentElement) { narrow.value = el.value.parentElement.offsetWidth < 500; diff --git a/src/client/events.ts b/src/client/events.ts new file mode 100644 index 0000000000..dbbd908b8f --- /dev/null +++ b/src/client/events.ts @@ -0,0 +1,4 @@ +import { EventEmitter } from 'eventemitter3'; + +// TODO: 型付け +export const globalEvents = new EventEmitter(); diff --git a/src/client/pages/settings/import-export.vue b/src/client/pages/settings/import-export.vue index 64def6fdf0..2b49996dda 100644 --- a/src/client/pages/settings/import-export.vue +++ b/src/client/pages/settings/import-export.vue @@ -1,5 +1,5 @@ <template> -<div> +<div style="margin: 16px;"> <FormSection> <template #label>{{ $ts._exportOrImport.allNotes }}</template> <MkButton :class="$style.button" inline @click="doExport('notes')"><i class="fas fa-download"></i> {{ $ts.export }}</MkButton> diff --git a/src/client/scripts/theme.ts b/src/client/scripts/theme.ts index 3fb5666a72..10842b8943 100644 --- a/src/client/scripts/theme.ts +++ b/src/client/scripts/theme.ts @@ -1,3 +1,4 @@ +import { globalEvents } from '@client/events'; import * as tinycolor from 'tinycolor2'; export type Theme = { @@ -62,6 +63,9 @@ export function applyTheme(theme: Theme, persist = true) { if (persist) { localStorage.setItem('theme', JSON.stringify(props)); } + + // 色計算など再度行えるようにクライアント全体に通知 + globalEvents.emit('themeChanged'); } function compile(theme: Theme): Record<string, string> { From abd3efa318ccb8f370e8f3df83a28735c1e4f45d Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Mon, 11 Oct 2021 02:47:23 +0900 Subject: [PATCH 063/107] chore: fix error --- src/client/components/global/header.vue | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/client/components/global/header.vue b/src/client/components/global/header.vue index 346bf42611..176a1b47ea 100644 --- a/src/client/components/global/header.vue +++ b/src/client/components/global/header.vue @@ -153,9 +153,15 @@ export default defineComponent({ if (el.value.parentElement) { narrow.value = el.value.parentElement.offsetWidth < 500; - new ResizeObserver((entries, observer) => { - narrow.value = el.value.parentElement.offsetWidth < 500; - }).observe(el.value.parentElement); + const ro = new ResizeObserver((entries, observer) => { + if (el.value) { + narrow.value = el.value.parentElement.offsetWidth < 500; + } + }); + ro.observe(el.value.parentElement); + onUnmounted(() => { + ro.disconnect(); + }); setTimeout(() => { const currentStickyTop = getComputedStyle(el.value.parentElement).getPropertyValue('--stickyTop') || '0px'; el.value.style.setProperty('--stickyTop', currentStickyTop); @@ -191,6 +197,7 @@ export default defineComponent({ width: 100%; -webkit-backdrop-filter: var(--blur, blur(15px)); backdrop-filter: var(--blur, blur(15px)); + border-bottom: solid 0.5px var(--divider); &.thin { --height: 50px; From 50bad84747da99ff0e1592d0efc23797b5f6ba2b Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Tue, 12 Oct 2021 00:44:52 +0900 Subject: [PATCH 064/107] chore: fix bug --- src/client/pages/page-editor/page-editor.vue | 134 +++++++++++-------- 1 file changed, 76 insertions(+), 58 deletions(-) diff --git a/src/client/pages/page-editor/page-editor.vue b/src/client/pages/page-editor/page-editor.vue index 0daefc6518..fc05bb27b3 100644 --- a/src/client/pages/page-editor/page-editor.vue +++ b/src/client/pages/page-editor/page-editor.vue @@ -1,79 +1,83 @@ <template> -<div class="_root"> - <MkA class="view" v-if="pageId" :to="`/@${ author.username }/pages/${ currentName }`"><i class="fas fa-external-link-square-alt"></i> {{ $ts._pages.viewPage }}</MkA> +<div> + <MkHeader :info="header"/> - <div class="buttons" style="margin: 16px;"> - <MkButton inline @click="save" primary class="save" v-if="!readonly"><i class="fas fa-save"></i> {{ $ts.save }}</MkButton> - <MkButton inline @click="duplicate" class="duplicate" v-if="pageId"><i class="fas fa-copy"></i> {{ $ts.duplicate }}</MkButton> - <MkButton inline @click="del" class="delete" v-if="pageId && !readonly"><i class="fas fa-trash-alt"></i> {{ $ts.delete }}</MkButton> - </div> + <div class="_root"> + <MkA class="view" v-if="pageId" :to="`/@${ author.username }/pages/${ currentName }`"><i class="fas fa-external-link-square-alt"></i> {{ $ts._pages.viewPage }}</MkA> - <div v-if="tab === 'settings'"> - <div style="padding: 16px;" class="_formRoot"> - <MkInput v-model="title" class="_formBlock"> - <template #label>{{ $ts._pages.title }}</template> - </MkInput> + <div class="buttons" style="margin: 16px;"> + <MkButton inline @click="save" primary class="save" v-if="!readonly"><i class="fas fa-save"></i> {{ $ts.save }}</MkButton> + <MkButton inline @click="duplicate" class="duplicate" v-if="pageId"><i class="fas fa-copy"></i> {{ $ts.duplicate }}</MkButton> + <MkButton inline @click="del" class="delete" v-if="pageId && !readonly"><i class="fas fa-trash-alt"></i> {{ $ts.delete }}</MkButton> + </div> - <MkInput v-model="summary" class="_formBlock"> - <template #label>{{ $ts._pages.summary }}</template> - </MkInput> + <div v-if="tab === 'settings'"> + <div style="padding: 16px;" class="_formRoot"> + <MkInput v-model="title" class="_formBlock"> + <template #label>{{ $ts._pages.title }}</template> + </MkInput> - <MkInput v-model="name" class="_formBlock"> - <template #prefix>{{ url }}/@{{ author.username }}/pages/</template> - <template #label>{{ $ts._pages.url }}</template> - </MkInput> + <MkInput v-model="summary" class="_formBlock"> + <template #label>{{ $ts._pages.summary }}</template> + </MkInput> - <MkSwitch v-model="alignCenter" class="_formBlock">{{ $ts._pages.alignCenter }}</MkSwitch> + <MkInput v-model="name" class="_formBlock"> + <template #prefix>{{ url }}/@{{ author.username }}/pages/</template> + <template #label>{{ $ts._pages.url }}</template> + </MkInput> - <MkSelect v-model="font" class="_formBlock"> - <template #label>{{ $ts._pages.font }}</template> - <option value="serif">{{ $ts._pages.fontSerif }}</option> - <option value="sans-serif">{{ $ts._pages.fontSansSerif }}</option> - </MkSelect> + <MkSwitch v-model="alignCenter" class="_formBlock">{{ $ts._pages.alignCenter }}</MkSwitch> - <MkSwitch v-model="hideTitleWhenPinned" class="_formBlock">{{ $ts._pages.hideTitleWhenPinned }}</MkSwitch> + <MkSelect v-model="font" class="_formBlock"> + <template #label>{{ $ts._pages.font }}</template> + <option value="serif">{{ $ts._pages.fontSerif }}</option> + <option value="sans-serif">{{ $ts._pages.fontSansSerif }}</option> + </MkSelect> - <div class="eyeCatch"> - <MkButton v-if="eyeCatchingImageId == null && !readonly" @click="setEyeCatchingImage"><i class="fas fa-plus"></i> {{ $ts._pages.eyeCatchingImageSet }}</MkButton> - <div v-else-if="eyeCatchingImage"> - <img :src="eyeCatchingImage.url" :alt="eyeCatchingImage.name" style="max-width: 100%;"/> - <MkButton @click="removeEyeCatchingImage()" v-if="!readonly"><i class="fas fa-trash-alt"></i> {{ $ts._pages.eyeCatchingImageRemove }}</MkButton> + <MkSwitch v-model="hideTitleWhenPinned" class="_formBlock">{{ $ts._pages.hideTitleWhenPinned }}</MkSwitch> + + <div class="eyeCatch"> + <MkButton v-if="eyeCatchingImageId == null && !readonly" @click="setEyeCatchingImage"><i class="fas fa-plus"></i> {{ $ts._pages.eyeCatchingImageSet }}</MkButton> + <div v-else-if="eyeCatchingImage"> + <img :src="eyeCatchingImage.url" :alt="eyeCatchingImage.name" style="max-width: 100%;"/> + <MkButton @click="removeEyeCatchingImage()" v-if="!readonly"><i class="fas fa-trash-alt"></i> {{ $ts._pages.eyeCatchingImageRemove }}</MkButton> + </div> </div> </div> </div> - </div> - <div v-else-if="tab === 'contents'"> - <div style="padding: 16px;"> - <XBlocks class="content" v-model="content" :hpml="hpml"/> + <div v-else-if="tab === 'contents'"> + <div style="padding: 16px;"> + <XBlocks class="content" v-model="content" :hpml="hpml"/> - <MkButton @click="add()" v-if="!readonly"><i class="fas fa-plus"></i></MkButton> + <MkButton @click="add()" v-if="!readonly"><i class="fas fa-plus"></i></MkButton> + </div> </div> - </div> - <div v-else-if="tab === 'variables'"> - <div class="qmuvgica"> - <XDraggable tag="div" class="variables" v-show="variables.length > 0" v-model="variables" item-key="name" handle=".drag-handle" :group="{ name: 'variables' }" animation="150" swap-threshold="0.5"> - <template #item="{element}"> - <XVariable - :value="element" - :removable="true" - @remove="() => removeVariable(element)" - :hpml="hpml" - :name="element.name" - :title="element.name" - :draggable="true" - /> - </template> - </XDraggable> + <div v-else-if="tab === 'variables'"> + <div class="qmuvgica"> + <XDraggable tag="div" class="variables" v-show="variables.length > 0" v-model="variables" item-key="name" handle=".drag-handle" :group="{ name: 'variables' }" animation="150" swap-threshold="0.5"> + <template #item="{element}"> + <XVariable + :modelValue="element" + :removable="true" + @remove="() => removeVariable(element)" + :hpml="hpml" + :name="element.name" + :title="element.name" + :draggable="true" + /> + </template> + </XDraggable> - <MkButton @click="addVariable()" class="add" v-if="!readonly"><i class="fas fa-plus"></i></MkButton> + <MkButton @click="addVariable()" class="add" v-if="!readonly"><i class="fas fa-plus"></i></MkButton> + </div> </div> - </div> - <div v-else-if="tab === 'script'"> - <div> - <MkTextarea class="_code" v-model="script"/> + <div v-else-if="tab === 'script'"> + <div> + <MkTextarea class="_code" v-model="script"/> + </div> </div> </div> </div> @@ -128,6 +132,21 @@ export default defineComponent({ data() { return { [symbols.PAGE_INFO]: computed(() => { + let title = this.$ts._pages.newPage; + if (this.initPageId) { + title = this.$ts._pages.editPage; + } + else if (this.initPageName && this.initUser) { + title = this.$ts._pages.readPage; + } + return { + title: title, + icon: 'fas fa-pencil-alt', + bg: 'var(--bg)', + }; + }), + tab: 'settings', + header: computed(() => { let title = this.$ts._pages.newPage; if (this.initPageId) { title = this.$ts._pages.editPage; @@ -162,7 +181,6 @@ export default defineComponent({ }] }; }), - tab: 'settings', author: this.$i, readonly: false, page: null, From 36170c816acd7cb905c34059f66ea4b3fd2e416b Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Tue, 12 Oct 2021 01:04:50 +0900 Subject: [PATCH 065/107] tweak ui --- src/client/components/ui/button.vue | 19 ++++++++++++--- src/client/pages/page-editor/page-editor.vue | 25 +++++++++++--------- 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/src/client/components/ui/button.vue b/src/client/components/ui/button.vue index 53b973fe55..e74c4f550b 100644 --- a/src/client/components/ui/button.vue +++ b/src/client/components/ui/button.vue @@ -1,6 +1,5 @@ <template> -<component class="bghgjjyj _button" - :is="link ? 'MkA' : 'button'" +<button v-if="!link" class="bghgjjyj _button" :class="{ inline, primary, danger, rounded, full }" :type="type" @click="$emit('click', $event)" @@ -10,7 +9,17 @@ <div class="content"> <slot></slot> </div> -</component> +</button> +<MkA v-else class="bghgjjyj _button" + :class="{ inline, primary, danger, rounded, full }" + :to="to" + @mousedown="onMousedown" +> + <div ref="ripples" class="ripples"></div> + <div class="content"> + <slot></slot> + </div> +</MkA> </template> <script lang="ts"> @@ -42,6 +51,10 @@ export default defineComponent({ required: false, default: false }, + to: { + type: String, + required: false + }, autofocus: { type: Boolean, required: false, diff --git a/src/client/pages/page-editor/page-editor.vue b/src/client/pages/page-editor/page-editor.vue index fc05bb27b3..e04039e634 100644 --- a/src/client/pages/page-editor/page-editor.vue +++ b/src/client/pages/page-editor/page-editor.vue @@ -3,12 +3,11 @@ <MkHeader :info="header"/> <div class="_root"> - <MkA class="view" v-if="pageId" :to="`/@${ author.username }/pages/${ currentName }`"><i class="fas fa-external-link-square-alt"></i> {{ $ts._pages.viewPage }}</MkA> - - <div class="buttons" style="margin: 16px;"> - <MkButton inline @click="save" primary class="save" v-if="!readonly"><i class="fas fa-save"></i> {{ $ts.save }}</MkButton> - <MkButton inline @click="duplicate" class="duplicate" v-if="pageId"><i class="fas fa-copy"></i> {{ $ts.duplicate }}</MkButton> - <MkButton inline @click="del" class="delete" v-if="pageId && !readonly"><i class="fas fa-trash-alt"></i> {{ $ts.delete }}</MkButton> + <div class="jqqmcavi" style="margin: 16px;"> + <MkButton v-if="pageId" class="button" inline link :to="`/@${ author.username }/pages/${ currentName }`"><i class="fas fa-external-link-square-alt"></i> {{ $ts._pages.viewPage }}</MkButton> + <MkButton inline @click="save" primary class="button" v-if="!readonly"><i class="fas fa-save"></i> {{ $ts.save }}</MkButton> + <MkButton inline @click="duplicate" class="button" v-if="pageId"><i class="fas fa-copy"></i> {{ $ts.duplicate }}</MkButton> + <MkButton inline @click="del" class="button" v-if="pageId && !readonly" danger><i class="fas fa-trash-alt"></i> {{ $ts.delete }}</MkButton> </div> <div v-if="tab === 'settings'"> @@ -492,6 +491,14 @@ export default defineComponent({ </script> <style lang="scss" scoped> +.jqqmcavi { + > .button { + & + .button { + margin-left: 8px; + } + } +} + .gwbmwxkm { position: relative; @@ -559,11 +566,7 @@ export default defineComponent({ } .qmuvgica { - padding: 32px; - - @media (max-width: 500px) { - padding: 16px; - } + padding: 16px; > .variables { margin-bottom: 16px; From 12635da4733d8530017977569e6bc07a39bf384e Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Tue, 12 Oct 2021 02:15:31 +0900 Subject: [PATCH 066/107] :art: --- src/client/pages/welcome.entrance.a.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/pages/welcome.entrance.a.vue b/src/client/pages/welcome.entrance.a.vue index 82b439ddd3..467a6fb1ad 100644 --- a/src/client/pages/welcome.entrance.a.vue +++ b/src/client/pages/welcome.entrance.a.vue @@ -27,7 +27,7 @@ <div class="desc" v-html="meta.description || $ts.headlineMisskey"></div> </div> <div class="action"> - <MkButton @click="signup()" inline primary data-cy-signup>{{ $ts.signup }}</MkButton> + <MkButton @click="signup()" inline primary data-cy-signup style="margin-right: 12px;">{{ $ts.signup }}</MkButton> <MkButton @click="signin()" inline data-cy-signin>{{ $ts.login }}</MkButton> </div> <div class="status" v-if="onlineUsersCount && stats"> From a99478e2ea3532ee6ec8bf538b5b30f2b4d79211 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Tue, 12 Oct 2021 21:41:27 +0900 Subject: [PATCH 067/107] fix title --- src/client/pages/settings/index.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/pages/settings/index.vue b/src/client/pages/settings/index.vue index 39b9a85618..cc3fd44f70 100644 --- a/src/client/pages/settings/index.vue +++ b/src/client/pages/settings/index.vue @@ -38,7 +38,7 @@ export default defineComponent({ setup(props, context) { const indexInfo = { - title: i18n.locale.controllPanel, + title: i18n.locale.settings, icon: 'fas fa-cog', bg: 'var(--bg)', }; From 91c9a6390ca56fae57dc3e4cd701dd82c440640e Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Tue, 12 Oct 2021 21:46:18 +0900 Subject: [PATCH 068/107] :art: --- src/client/pages/my-antennas/editor.vue | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/client/pages/my-antennas/editor.vue b/src/client/pages/my-antennas/editor.vue index 972a4e0243..93ab640030 100644 --- a/src/client/pages/my-antennas/editor.vue +++ b/src/client/pages/my-antennas/editor.vue @@ -1,10 +1,10 @@ <template> <div class="shaynizk"> <div class="form"> - <MkInput v-model="name" class=""> + <MkInput v-model="name" class="_formBlock"> <template #label>{{ $ts.name }}</template> </MkInput> - <MkSelect v-model="src"> + <MkSelect v-model="src" class="_formBlock"> <template #label>{{ $ts.antennaSource }}</template> <option value="all">{{ $ts._antennaSources.all }}</option> <option value="home">{{ $ts._antennaSources.homeTimeline }}</option> @@ -12,30 +12,30 @@ <option value="list">{{ $ts._antennaSources.userList }}</option> <option value="group">{{ $ts._antennaSources.userGroup }}</option> </MkSelect> - <MkSelect v-model="userListId" v-if="src === 'list'"> + <MkSelect v-model="userListId" v-if="src === 'list'" class="_formBlock"> <template #label>{{ $ts.userList }}</template> <option v-for="list in userLists" :value="list.id" :key="list.id">{{ list.name }}</option> </MkSelect> - <MkSelect v-model="userGroupId" v-else-if="src === 'group'"> + <MkSelect v-model="userGroupId" v-else-if="src === 'group'" class="_formBlock"> <template #label>{{ $ts.userGroup }}</template> <option v-for="group in userGroups" :value="group.id" :key="group.id">{{ group.name }}</option> </MkSelect> - <MkTextarea v-model="users" v-else-if="src === 'users'"> + <MkTextarea v-model="users" v-else-if="src === 'users'" class="_formBlock"> <template #label>{{ $ts.users }}</template> <template #caption>{{ $ts.antennaUsersDescription }} <button class="_textButton" @click="addUser">{{ $ts.addUser }}</button></template> </MkTextarea> - <MkSwitch v-model="withReplies">{{ $ts.withReplies }}</MkSwitch> - <MkTextarea v-model="keywords"> + <MkSwitch v-model="withReplies" class="_formBlock">{{ $ts.withReplies }}</MkSwitch> + <MkTextarea v-model="keywords" class="_formBlock"> <template #label>{{ $ts.antennaKeywords }}</template> <template #caption>{{ $ts.antennaKeywordsDescription }}</template> </MkTextarea> - <MkTextarea v-model="excludeKeywords"> + <MkTextarea v-model="excludeKeywords" class="_formBlock"> <template #label>{{ $ts.antennaExcludeKeywords }}</template> <template #caption>{{ $ts.antennaKeywordsDescription }}</template> </MkTextarea> - <MkSwitch v-model="caseSensitive">{{ $ts.caseSensitive }}</MkSwitch> - <MkSwitch v-model="withFile">{{ $ts.withFileAntenna }}</MkSwitch> - <MkSwitch v-model="notify">{{ $ts.notifyAntenna }}</MkSwitch> + <MkSwitch v-model="caseSensitive" class="_formBlock">{{ $ts.caseSensitive }}</MkSwitch> + <MkSwitch v-model="withFile" class="_formBlock">{{ $ts.withFileAntenna }}</MkSwitch> + <MkSwitch v-model="notify" class="_formBlock">{{ $ts.notifyAntenna }}</MkSwitch> </div> <div class="actions"> <MkButton inline @click="saveAntenna()" primary><i class="fas fa-save"></i> {{ $ts.save }}</MkButton> From ba6959b8c1c4faafccdeb0f76eb26fc29e02af2d Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Thu, 14 Oct 2021 01:24:54 +0900 Subject: [PATCH 069/107] =?UTF-8?q?fix(api):=20=E7=AE=A1=E7=90=86=E8=80=85?= =?UTF-8?q?=E3=81=8A=E3=82=88=E3=81=B3=E3=83=A2=E3=83=87=E3=83=AC=E3=83=BC?= =?UTF-8?q?=E3=82=BF=E3=83=BC=E3=82=92=E3=83=96=E3=83=AD=E3=83=83=E3=82=AF?= =?UTF-8?q?=E3=81=A7=E3=81=8D=E3=81=A6=E3=81=97=E3=81=BE=E3=81=86=E5=95=8F?= =?UTF-8?q?=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + src/server/api/endpoints/blocking/create.ts | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d0158910c6..099a51a03b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ - クライアント: ヘッダーのタブが折り返される問題を修正 - クライアント: ヘッダーにタブが表示されている状態でタイトルをクリックしたときにタブ選択が表示されるのを修正 - クライアント: ユーザーページのタブが機能していない問題を修正 +- API: 管理者およびモデレーターをブロックできてしまう問題を修正 ## 12.91.0 (2021/09/22) diff --git a/src/server/api/endpoints/blocking/create.ts b/src/server/api/endpoints/blocking/create.ts index 1bf5cf374b..850406908b 100644 --- a/src/server/api/endpoints/blocking/create.ts +++ b/src/server/api/endpoints/blocking/create.ts @@ -43,6 +43,12 @@ export const meta = { code: 'ALREADY_BLOCKING', id: '787fed64-acb9-464a-82eb-afbd745b9614' }, + + cannotBlockModerator: { + message: 'Cannot block a moderator or an admin.', + code: 'CANNOT_BLOCK_MODERATOR', + id: '8544aaef-89fb-e470-9f6c-385d38b474f5' + } }, res: { @@ -60,6 +66,10 @@ export default define(meta, async (ps, user) => { throw new ApiError(meta.errors.blockeeIsYourself); } + if (user.isAdmin || user.isModerator) { + throw new ApiError(meta.errors.cannotBlockModerator); + } + // Get blockee const blockee = await getUser(ps.userId).catch(e => { if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); From 23de45cea5b84b26c352836a12d33c46210dc0fc Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Thu, 14 Oct 2021 01:25:19 +0900 Subject: [PATCH 070/107] feat(client): add new theme --- src/client/scripts/theme.ts | 1 + src/client/themes/d-botanical.json5 | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 src/client/themes/d-botanical.json5 diff --git a/src/client/scripts/theme.ts b/src/client/scripts/theme.ts index 10842b8943..9f165f7998 100644 --- a/src/client/scripts/theme.ts +++ b/src/client/scripts/theme.ts @@ -25,6 +25,7 @@ export const builtinThemes = [ require('@client/themes/d-persimmon.json5'), require('@client/themes/d-astro.json5'), require('@client/themes/d-future.json5'), + require('@client/themes/d-botanical.json5'), require('@client/themes/d-black.json5'), ] as Theme[]; diff --git a/src/client/themes/d-botanical.json5 b/src/client/themes/d-botanical.json5 new file mode 100644 index 0000000000..f3665e22b2 --- /dev/null +++ b/src/client/themes/d-botanical.json5 @@ -0,0 +1,26 @@ +{ + id: '504debaf-4912-6a4c-5059-1db08a76b737', + + name: 'Mi Botanical Dark', + author: 'syuilo', + + base: 'dark', + + props: { + accent: 'rgb(148, 179, 0)', + bg: 'rgb(37, 38, 36)', + fg: 'rgb(216, 212, 199)', + fgHighlighted: '#fff', + divider: 'rgba(255, 255, 255, 0.14)', + panel: 'rgb(47, 47, 44)', + panelHeaderBg: '@panel', + panelHeaderDivider: '@divider', + header: ':alpha<0.7<@panel', + navBg: '#363636', + renote: '@accent', + mention: 'rgb(212, 153, 76)', + mentionMe: 'rgb(212, 210, 76)', + hashtag: 'rgb(76, 212, 180)', + link: '@accent', + }, +} From 3f95bd53cd7a3eedf2c47f53544b268d4c010d69 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Thu, 14 Oct 2021 01:25:50 +0900 Subject: [PATCH 071/107] feat(client): add some theme functions --- src/client/scripts/theme.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/client/scripts/theme.ts b/src/client/scripts/theme.ts index 9f165f7998..e79d54fa6d 100644 --- a/src/client/scripts/theme.ts +++ b/src/client/scripts/theme.ts @@ -92,6 +92,8 @@ function compile(theme: Theme): Record<string, string> { case 'darken': return color.darken(arg); case 'lighten': return color.lighten(arg); case 'alpha': return color.setAlpha(arg); + case 'hue': return color.spin(arg); + case 'saturate': return color.saturate(arg); } } From dc12b189dec47a0103dd4a4986f98ef424d49b40 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Thu, 14 Oct 2021 01:26:44 +0900 Subject: [PATCH 072/107] Update CHANGELOG.md --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 099a51a03b..9e1f33fca7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,8 @@ - クライアント: MFM関数構文のサジェストを実装 - クライアント: 未読の通知のみ表示する機能 - クライアント: 通知ページで通知の種類によるフィルタ -- クライアント: ピン留めユーザーの設定項目がない問題を修正 +- クライアント: 新しいダークテーマを追加 +- クライアント: テーマコンパイラに hue と saturate 関数を追加 - ActivityPub: HTML -> MFMの変換を強化 - API: i/notifications に unreadOnly オプションを追加 - API: ap系のエンドポイントをログイン必須化+レートリミット追加 @@ -29,6 +30,7 @@ - クライアント: ヘッダーのタブが折り返される問題を修正 - クライアント: ヘッダーにタブが表示されている状態でタイトルをクリックしたときにタブ選択が表示されるのを修正 - クライアント: ユーザーページのタブが機能していない問題を修正 +- クライアント: ピン留めユーザーの設定項目がない問題を修正 - API: 管理者およびモデレーターをブロックできてしまう問題を修正 ## 12.91.0 (2021/09/22) From 46f3736f44b20ae2e90b0774ad723db5d648eba9 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Thu, 14 Oct 2021 01:27:45 +0900 Subject: [PATCH 073/107] :art: --- src/client/components/signup.vue | 2 +- src/client/components/ui/button.vue | 27 +++++++++++++++++++++---- src/client/pages/welcome.entrance.a.vue | 2 +- src/client/pages/welcome.entrance.b.vue | 2 +- src/client/pages/welcome.entrance.c.vue | 2 +- src/client/themes/_dark.json5 | 2 ++ src/client/themes/_light.json5 | 2 ++ src/client/themes/d-astro.json5 | 2 ++ src/client/themes/d-future.json5 | 2 ++ src/client/ui/_common_/sidebar.vue | 2 +- 10 files changed, 36 insertions(+), 9 deletions(-) diff --git a/src/client/components/signup.vue b/src/client/components/signup.vue index b420bca5a3..cb25eadf06 100644 --- a/src/client/components/signup.vue +++ b/src/client/components/signup.vue @@ -56,7 +56,7 @@ </label> <captcha v-if="meta.enableHcaptcha" class="_formBlock captcha" provider="hcaptcha" ref="hcaptcha" v-model="hCaptchaResponse" :sitekey="meta.hcaptchaSiteKey"/> <captcha v-if="meta.enableRecaptcha" class="_formBlock captcha" provider="recaptcha" ref="recaptcha" v-model="reCaptchaResponse" :sitekey="meta.recaptchaSiteKey"/> - <MkButton class="_formBlock" type="submit" :disabled="shouldDisableSubmitting" primary data-cy-signup-submit>{{ $ts.start }}</MkButton> + <MkButton class="_formBlock" type="submit" :disabled="shouldDisableSubmitting" gradate data-cy-signup-submit>{{ $ts.start }}</MkButton> </template> </form> </template> diff --git a/src/client/components/ui/button.vue b/src/client/components/ui/button.vue index e74c4f550b..5f36be0d76 100644 --- a/src/client/components/ui/button.vue +++ b/src/client/components/ui/button.vue @@ -1,6 +1,6 @@ <template> <button v-if="!link" class="bghgjjyj _button" - :class="{ inline, primary, danger, rounded, full }" + :class="{ inline, primary, gradate, danger, rounded, full }" :type="type" @click="$emit('click', $event)" @mousedown="onMousedown" @@ -11,7 +11,7 @@ </div> </button> <MkA v-else class="bghgjjyj _button" - :class="{ inline, primary, danger, rounded, full }" + :class="{ inline, primary, gradate, danger, rounded, full }" :to="to" @mousedown="onMousedown" > @@ -36,6 +36,11 @@ export default defineComponent({ required: false, default: false }, + gradate: { + type: Boolean, + required: false, + default: false + }, rounded: { type: Boolean, required: false, @@ -137,8 +142,8 @@ export default defineComponent({ padding: 8px 14px; text-align: center; font-weight: normal; - font-size: 0.9em; - line-height: 24px; + font-size: 0.8em; + line-height: 22px; box-shadow: none; text-decoration: none; background: var(--buttonBg); @@ -177,6 +182,20 @@ export default defineComponent({ } } + &.gradate { + font-weight: bold; + color: var(--fgOnAccent) !important; + background: linear-gradient(90deg, var(--buttonGradateA), var(--buttonGradateB)); + + &:not(:disabled):hover { + background: var(--X8); + } + + &:not(:disabled):active { + background: var(--X8); + } + } + &.danger { color: #ff2a2a; diff --git a/src/client/pages/welcome.entrance.a.vue b/src/client/pages/welcome.entrance.a.vue index 467a6fb1ad..13f0993793 100644 --- a/src/client/pages/welcome.entrance.a.vue +++ b/src/client/pages/welcome.entrance.a.vue @@ -27,7 +27,7 @@ <div class="desc" v-html="meta.description || $ts.headlineMisskey"></div> </div> <div class="action"> - <MkButton @click="signup()" inline primary data-cy-signup style="margin-right: 12px;">{{ $ts.signup }}</MkButton> + <MkButton @click="signup()" inline gradate data-cy-signup style="margin-right: 12px;">{{ $ts.signup }}</MkButton> <MkButton @click="signin()" inline data-cy-signin>{{ $ts.login }}</MkButton> </div> <div class="status" v-if="onlineUsersCount && stats"> diff --git a/src/client/pages/welcome.entrance.b.vue b/src/client/pages/welcome.entrance.b.vue index a5c12f09e2..163fc1e35f 100644 --- a/src/client/pages/welcome.entrance.b.vue +++ b/src/client/pages/welcome.entrance.b.vue @@ -12,7 +12,7 @@ <div class="desc" v-html="meta.description || $ts.headlineMisskey"></div> </div> <div class="action"> - <MkButton class="signup" @click="signup()" inline primary>{{ $ts.signup }}</MkButton> + <MkButton class="signup" @click="signup()" inline gradate>{{ $ts.signup }}</MkButton> <MkButton class="signin" @click="signin()" inline>{{ $ts.login }}</MkButton> </div> <div class="status" v-if="onlineUsersCount && stats"> diff --git a/src/client/pages/welcome.entrance.c.vue b/src/client/pages/welcome.entrance.c.vue index 2c8db6e264..bf1c9b1998 100644 --- a/src/client/pages/welcome.entrance.c.vue +++ b/src/client/pages/welcome.entrance.c.vue @@ -24,7 +24,7 @@ <div class="desc" v-html="meta.description || $ts.headlineMisskey"></div> </div> <div class="action"> - <MkButton @click="signup()" inline primary>{{ $ts.signup }}</MkButton> + <MkButton @click="signup()" inline gradate>{{ $ts.signup }}</MkButton> <MkButton @click="signin()" inline>{{ $ts.login }}</MkButton> </div> <div class="status" v-if="onlineUsersCount && stats"> diff --git a/src/client/themes/_dark.json5 b/src/client/themes/_dark.json5 index 22f4dc13d4..d8be16f60a 100644 --- a/src/client/themes/_dark.json5 +++ b/src/client/themes/_dark.json5 @@ -57,6 +57,8 @@ cwHoverBg: '#707b97', buttonBg: 'rgba(255, 255, 255, 0.05)', buttonHoverBg: 'rgba(255, 255, 255, 0.1)', + buttonGradateA: '@accent', + buttonGradateB: ':hue<20<@accent', inputBorder: 'rgba(255, 255, 255, 0.1)', inputBorderHover: 'rgba(255, 255, 255, 0.2)', listItemHoverBg: 'rgba(255, 255, 255, 0.03)', diff --git a/src/client/themes/_light.json5 b/src/client/themes/_light.json5 index 64b92dba8a..251aa36c7a 100644 --- a/src/client/themes/_light.json5 +++ b/src/client/themes/_light.json5 @@ -57,6 +57,8 @@ cwHoverBg: '#bbc4ce', buttonBg: 'rgba(0, 0, 0, 0.05)', buttonHoverBg: 'rgba(0, 0, 0, 0.1)', + buttonGradateA: '@accent', + buttonGradateB: ':hue<20<@accent', inputBorder: 'rgba(0, 0, 0, 0.1)', inputBorderHover: 'rgba(0, 0, 0, 0.2)', listItemHoverBg: 'rgba(0, 0, 0, 0.03)', diff --git a/src/client/themes/d-astro.json5 b/src/client/themes/d-astro.json5 index 08846dec20..2350e3d46d 100644 --- a/src/client/themes/d-astro.json5 +++ b/src/client/themes/d-astro.json5 @@ -46,6 +46,8 @@ navIndicator: '@accent', accentLighten: ':lighten<10<@accent', buttonHoverBg: 'rgba(255, 255, 255, 0.1)', + buttonGradateA: '@accent', + buttonGradateB: ':hue<-20<@accent', driveFolderBg: ':alpha<0.3<@accent', fgHighlighted: ':lighten<3<@fg', panelHeaderBg: ':lighten<3<@panel', diff --git a/src/client/themes/d-future.json5 b/src/client/themes/d-future.json5 index 05ffe87bf0..1882609121 100644 --- a/src/client/themes/d-future.json5 +++ b/src/client/themes/d-future.json5 @@ -21,5 +21,7 @@ mentionMe: '@accent', hashtag: '#70c0e8', link: '#e88080', + buttonGradateA: '@accent', + buttonGradateB: ':saturate<30<:hue<30<@accent', }, } diff --git a/src/client/ui/_common_/sidebar.vue b/src/client/ui/_common_/sidebar.vue index 33aea31c39..d00327b096 100644 --- a/src/client/ui/_common_/sidebar.vue +++ b/src/client/ui/_common_/sidebar.vue @@ -372,7 +372,7 @@ export default defineComponent({ right: 0; bottom: 0; border-radius: 999px; - background: var(--accent); + background: linear-gradient(90deg, var(--buttonGradateA), var(--buttonGradateB)); } &:hover, &.active { From 8e2be5e9a7c9a851bcdbbd77bf079a40ead598dc Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Thu, 14 Oct 2021 01:55:39 +0900 Subject: [PATCH 074/107] =?UTF-8?q?chore:=20https://github.com/misskey-dev?= =?UTF-8?q?/misskey/commit/ba6959b8c1c4faafccdeb0f76eb26fc29e02af2d=20?= =?UTF-8?q?=E3=81=AE=E3=83=AA=E3=83=A2=E3=83=BC=E3=83=88=E5=AF=BE=E5=BF=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/server/api/endpoints/blocking/create.ts | 12 ++++++------ src/services/blocking/create.ts | 5 +++++ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/server/api/endpoints/blocking/create.ts b/src/server/api/endpoints/blocking/create.ts index 850406908b..4deaa39974 100644 --- a/src/server/api/endpoints/blocking/create.ts +++ b/src/server/api/endpoints/blocking/create.ts @@ -66,10 +66,6 @@ export default define(meta, async (ps, user) => { throw new ApiError(meta.errors.blockeeIsYourself); } - if (user.isAdmin || user.isModerator) { - throw new ApiError(meta.errors.cannotBlockModerator); - } - // Get blockee const blockee = await getUser(ps.userId).catch(e => { if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); @@ -86,8 +82,12 @@ export default define(meta, async (ps, user) => { throw new ApiError(meta.errors.alreadyBlocking); } - // Create blocking - await create(blocker, blockee); + try { + await create(blocker, blockee); + } catch (e) { + if (e.id === 'e42b7890-5e4d-9d9c-d54b-cf4dd30adfb5') throw new ApiError(meta.errors.cannotBlockModerator); + throw e; + } NoteWatchings.delete({ userId: blocker.id, diff --git a/src/services/blocking/create.ts b/src/services/blocking/create.ts index 76c4bda9dc..defe377514 100644 --- a/src/services/blocking/create.ts +++ b/src/services/blocking/create.ts @@ -9,8 +9,13 @@ import { User } from '@/models/entities/user'; import { Blockings, Users, FollowRequests, Followings, UserListJoinings, UserLists } from '@/models/index'; import { perUserFollowingChart } from '@/services/chart/index'; import { genId } from '@/misc/gen-id'; +import { IdentifiableError } from '@/misc/identifiable-error'; export default async function(blocker: User, blockee: User) { + if (blockee.isAdmin || blockee.isModerator) { + throw new IdentifiableError('e42b7890-5e4d-9d9c-d54b-cf4dd30adfb5'); + } + await Promise.all([ cancelRequest(blocker, blockee), cancelRequest(blockee, blocker), From 3dc70f9878385ce34c3d4f8ae35006fbe690f8d2 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Thu, 14 Oct 2021 02:00:34 +0900 Subject: [PATCH 075/107] :art: --- src/client/ui/default.sidebar.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/ui/default.sidebar.vue b/src/client/ui/default.sidebar.vue index 745ad2d602..e36febb7fa 100644 --- a/src/client/ui/default.sidebar.vue +++ b/src/client/ui/default.sidebar.vue @@ -4,7 +4,7 @@ <MkAvatar :user="$i" class="avatar"/><MkAcct class="text" :user="$i"/> </button> <div class="post" @click="post" data-cy-open-post-form> - <MkButton class="button" primary full> + <MkButton class="button" gradate full rounded> <i class="fas fa-pencil-alt fa-fw"></i><span class="text" v-if="!iconOnly">{{ $ts.note }}</span> </MkButton> </div> From 955b3e313bbf311dd565d96bc04545c9be96e6bb Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Thu, 14 Oct 2021 18:51:15 +0900 Subject: [PATCH 076/107] :art: --- src/client/components/global/header.vue | 5 +- src/client/components/modal-page-window.vue | 3 +- src/client/components/page-window.vue | 3 +- src/client/components/post-form.vue | 14 ++- src/client/pages/page.vue | 112 +++++++++++--------- src/client/pages/timeline.vue | 4 + src/client/ui/deck/column.vue | 14 +-- 7 files changed, 89 insertions(+), 66 deletions(-) diff --git a/src/client/components/global/header.vue b/src/client/components/global/header.vue index 176a1b47ea..a283e1656c 100644 --- a/src/client/components/global/header.vue +++ b/src/client/components/global/header.vue @@ -1,5 +1,5 @@ <template> -<div class="fdidabkb" :class="{ slim: narrow, thin }" :style="{ background: bg }" @click="onClick" ref="el"> +<div class="fdidabkb" :class="{ slim: narrow, thin: thin_ }" :style="{ background: bg }" @click="onClick" ref="el"> <template v-if="info"> <div class="titleContainer" @click="showTabsPopup"> <i v-if="info.icon" class="icon" :class="info.icon"></i> @@ -37,7 +37,7 @@ </template> <script lang="ts"> -import { computed, defineComponent, onMounted, onUnmounted, PropType, ref } from 'vue'; +import { computed, defineComponent, onMounted, onUnmounted, PropType, ref, inject } from 'vue'; import * as tinycolor from 'tinycolor2'; import { popupMenu } from '@client/os'; import { url } from '@client/config'; @@ -182,6 +182,7 @@ export default defineComponent({ showTabsPopup, preventDrag, onClick, + thin_: props.thin || inject('shouldHeaderThin', false) }; }, }); diff --git a/src/client/components/modal-page-window.vue b/src/client/components/modal-page-window.vue index c20e2b3087..cea92b9cb3 100644 --- a/src/client/components/modal-page-window.vue +++ b/src/client/components/modal-page-window.vue @@ -44,7 +44,8 @@ export default defineComponent({ return { navHook: (path) => { this.navigate(path); - } + }, + shouldHeaderThin: true, }; }, diff --git a/src/client/components/page-window.vue b/src/client/components/page-window.vue index ec4c03c3d8..620d288b14 100644 --- a/src/client/components/page-window.vue +++ b/src/client/components/page-window.vue @@ -46,7 +46,8 @@ export default defineComponent({ return { navHook: (path) => { this.navigate(path); - } + }, + shouldHeaderThin: true, }; }, diff --git a/src/client/components/post-form.vue b/src/client/components/post-form.vue index 4ec09e76db..3ff1dfe0df 100644 --- a/src/client/components/post-form.vue +++ b/src/client/components/post-form.vue @@ -1,6 +1,6 @@ <template> <div class="gafaadew" :class="{ modal, _popup: modal }" - v-size="{ max: [500] }" + v-size="{ max: [310, 500] }" @dragover.stop="onDragover" @dragenter="onDragenter" @dragleave="onDragleave" @@ -914,5 +914,17 @@ export default defineComponent({ } } } + + &.max-width_310px { + > .form { + > footer { + > button { + font-size: 14px; + width: 44px; + height: 44px; + } + } + } + } } </style> diff --git a/src/client/pages/page.vue b/src/client/pages/page.vue index 47a458df9c..b8d7507363 100644 --- a/src/client/pages/page.vue +++ b/src/client/pages/page.vue @@ -1,61 +1,65 @@ <template> -<div class="_root"> - <transition name="fade" mode="out-in"> - <div v-if="page" class="xcukqgmh" :key="page.id" v-size="{ max: [450] }"> - <div class="_block main"> - <!-- - <div class="header"> - <h1>{{ page.title }}</h1> - </div> - --> - <div class="banner"> - <img :src="page.eyeCatchingImage.url" v-if="page.eyeCatchingImageId"/> - </div> - <div class="content"> - <XPage :page="page"/> - </div> - <div class="actions"> - <div class="like"> - <MkButton class="button" @click="unlike()" v-if="page.isLiked" v-tooltip="$ts._pages.unlike" primary><i class="fas fa-heart"></i><span class="count" v-if="page.likedCount > 0">{{ page.likedCount }}</span></MkButton> - <MkButton class="button" @click="like()" v-else v-tooltip="$ts._pages.like"><i class="far fa-heart"></i><span class="count" v-if="page.likedCount > 0">{{ page.likedCount }}</span></MkButton> +<div> + <MkHeader :info="header"/> + + <div class="_root"> + <transition name="fade" mode="out-in"> + <div v-if="page" class="xcukqgmh" :key="page.id" v-size="{ max: [450] }"> + <div class="_block main"> + <!-- + <div class="header"> + <h1>{{ page.title }}</h1> </div> - <div class="other"> - <button class="_button" @click="shareWithNote" v-tooltip="$ts.shareWithNote" v-click-anime><i class="fas fa-retweet fa-fw"></i></button> - <button class="_button" @click="share" v-tooltip="$ts.share" v-click-anime><i class="fas fa-share-alt fa-fw"></i></button> + --> + <div class="banner"> + <img :src="page.eyeCatchingImage.url" v-if="page.eyeCatchingImageId"/> + </div> + <div class="content"> + <XPage :page="page"/> + </div> + <div class="actions"> + <div class="like"> + <MkButton class="button" @click="unlike()" v-if="page.isLiked" v-tooltip="$ts._pages.unlike" primary><i class="fas fa-heart"></i><span class="count" v-if="page.likedCount > 0">{{ page.likedCount }}</span></MkButton> + <MkButton class="button" @click="like()" v-else v-tooltip="$ts._pages.like"><i class="far fa-heart"></i><span class="count" v-if="page.likedCount > 0">{{ page.likedCount }}</span></MkButton> + </div> + <div class="other"> + <button class="_button" @click="shareWithNote" v-tooltip="$ts.shareWithNote" v-click-anime><i class="fas fa-retweet fa-fw"></i></button> + <button class="_button" @click="share" v-tooltip="$ts.share" v-click-anime><i class="fas fa-share-alt fa-fw"></i></button> + </div> + </div> + <div class="user"> + <MkAvatar :user="page.user" class="avatar"/> + <div class="name"> + <MkUserName :user="page.user" style="display: block;"/> + <MkAcct :user="page.user"/> + </div> + <MkFollowButton v-if="!$i || $i.id != page.user.id" :user="page.user" :inline="true" :transparent="false" :full="true" large class="koudoku"/> + </div> + <div class="links"> + <MkA :to="`/@${username}/pages/${pageName}/view-source`" class="link">{{ $ts._pages.viewSource }}</MkA> + <template v-if="$i && $i.id === page.userId"> + <MkA :to="`/pages/edit/${page.id}`" class="link">{{ $ts._pages.editThisPage }}</MkA> + <button v-if="$i.pinnedPageId === page.id" @click="pin(false)" class="link _textButton">{{ $ts.unpin }}</button> + <button v-else @click="pin(true)" class="link _textButton">{{ $ts.pin }}</button> + </template> </div> </div> - <div class="user"> - <MkAvatar :user="page.user" class="avatar"/> - <div class="name"> - <MkUserName :user="page.user" style="display: block;"/> - <MkAcct :user="page.user"/> - </div> - <MkFollowButton v-if="!$i || $i.id != page.user.id" :user="page.user" :inline="true" :transparent="false" :full="true" large class="koudoku"/> - </div> - <div class="links"> - <MkA :to="`/@${username}/pages/${pageName}/view-source`" class="link">{{ $ts._pages.viewSource }}</MkA> - <template v-if="$i && $i.id === page.userId"> - <MkA :to="`/pages/edit/${page.id}`" class="link">{{ $ts._pages.editThisPage }}</MkA> - <button v-if="$i.pinnedPageId === page.id" @click="pin(false)" class="link _textButton">{{ $ts.unpin }}</button> - <button v-else @click="pin(true)" class="link _textButton">{{ $ts.pin }}</button> - </template> + <div class="footer"> + <div><i class="far fa-clock"></i> {{ $ts.createdAt }}: <MkTime :time="page.createdAt" mode="detail"/></div> + <div v-if="page.createdAt != page.updatedAt"><i class="far fa-clock"></i> {{ $ts.updatedAt }}: <MkTime :time="page.updatedAt" mode="detail"/></div> </div> + <MkAd :prefer="['horizontal', 'horizontal-big']"/> + <MkContainer :max-height="300" :foldable="true" class="other"> + <template #header><i class="fas fa-clock"></i> {{ $ts.recentPosts }}</template> + <MkPagination :pagination="otherPostsPagination" #default="{items}"> + <MkPagePreview v-for="page in items" :page="page" :key="page.id" class="_gap"/> + </MkPagination> + </MkContainer> </div> - <div class="footer"> - <div><i class="far fa-clock"></i> {{ $ts.createdAt }}: <MkTime :time="page.createdAt" mode="detail"/></div> - <div v-if="page.createdAt != page.updatedAt"><i class="far fa-clock"></i> {{ $ts.updatedAt }}: <MkTime :time="page.updatedAt" mode="detail"/></div> - </div> - <MkAd :prefer="['horizontal', 'horizontal-big']"/> - <MkContainer :max-height="300" :foldable="true" class="other"> - <template #header><i class="fas fa-clock"></i> {{ $ts.recentPosts }}</template> - <MkPagination :pagination="otherPostsPagination" #default="{items}"> - <MkPagePreview v-for="page in items" :page="page" :key="page.id" class="_gap"/> - </MkPagination> - </MkContainer> - </div> - <MkError v-else-if="error" @retry="fetch()"/> - <MkLoading v-else/> - </transition> + <MkError v-else-if="error" @retry="fetch()"/> + <MkLoading v-else/> + </transition> + </div> </div> </template> @@ -97,6 +101,10 @@ export default defineComponent({ [symbols.PAGE_INFO]: computed(() => this.page ? { title: computed(() => this.page.title || this.page.name), avatar: this.page.user, + } : null), + header: computed(() => this.page ? { + title: computed(() => this.page.title || this.page.name), + avatar: this.page.user, path: `/@${this.page.user.username}/pages/${this.page.name}`, share: { title: this.page.title || this.page.name, diff --git a/src/client/pages/timeline.vue b/src/client/pages/timeline.vue index abe9ccd7e0..dfabcbf84b 100644 --- a/src/client/pages/timeline.vue +++ b/src/client/pages/timeline.vue @@ -215,6 +215,10 @@ export default defineComponent({ } } + > .post-form { + border-radius: var(--radius); + } + > .tl { background: var(--bg); border-radius: var(--radius); diff --git a/src/client/ui/deck/column.vue b/src/client/ui/deck/column.vue index 842a6ff59f..59729e09f7 100644 --- a/src/client/ui/deck/column.vue +++ b/src/client/ui/deck/column.vue @@ -37,6 +37,10 @@ import { updateColumn, swapLeftColumn, swapRightColumn, swapUpColumn, swapDownCo import { deckStore } from './deck-store'; export default defineComponent({ + provide: { + shouldHeaderThin: true + }, + props: { column: { type: Object, @@ -267,6 +271,7 @@ export default defineComponent({ height: 100%; overflow: hidden; contain: content; + box-shadow: 0 0 8px 0 var(--shadow); &.draghover { box-shadow: 0 0 0 2px var(--focus); @@ -320,15 +325,6 @@ export default defineComponent({ &.paged { background: var(--bg) !important; - - > header { - background: transparent; - box-shadow: none; - - > button { - color: var(--fg); - } - } } > header { From 3ec6101b160d6ebbffda1ff6bf4873c49f3253b5 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Thu, 14 Oct 2021 20:55:59 +0900 Subject: [PATCH 077/107] :art: --- src/client/components/global/spacer.vue | 70 ++++++++++++ src/client/components/index.ts | 2 + src/client/pages/explore.vue | 140 ++++++++++++------------ src/client/pages/notifications.vue | 17 +-- src/client/themes/d-botanical.json5 | 2 +- 5 files changed, 146 insertions(+), 85 deletions(-) create mode 100644 src/client/components/global/spacer.vue diff --git a/src/client/components/global/spacer.vue b/src/client/components/global/spacer.vue new file mode 100644 index 0000000000..9037de466c --- /dev/null +++ b/src/client/components/global/spacer.vue @@ -0,0 +1,70 @@ +<template> +<div ref="root" :class="$style.root" :style="{ padding: margin + 'px' }"> + <div ref="content" :class="$style.content"> + <slot></slot> + </div> +</div> +</template> + +<script lang="ts"> +import { defineComponent, onMounted, onUnmounted, ref } from 'vue'; + +export default defineComponent({ + props: { + contentMax: { + type: Number, + required: false, + default: null, + } + }, + + setup(props, context) { + let ro: ResizeObserver; + const root = ref<HTMLElement>(null); + const content = ref<HTMLElement>(null); + const margin = ref(0); + const adjust = (rect: { width: number; height: number; }) => { + if (rect.width > (props.contentMax || 500)) { + margin.value = 32; + } else { + margin.value = 12; + } + }; + + onMounted(() => { + ro = new ResizeObserver((entries) => { + adjust({ + width: entries[0].borderBoxSize[0].inlineSize, + height: entries[0].borderBoxSize[0].blockSize, + }); + }); + ro.observe(root.value); + + if (props.contentMax) { + content.value.style.maxWidth = `${props.contentMax}px`; + } + }); + + onUnmounted(() => { + ro.disconnect(); + }); + + return { + root, + content, + margin, + }; + }, +}); +</script> + +<style lang="scss" module> +.root { + box-sizing: border-box; + width: 100%; +} + +.content { + margin: 0 auto; +} +</style> diff --git a/src/client/components/index.ts b/src/client/components/index.ts index 8f071dfce1..ecf66ea0e8 100644 --- a/src/client/components/index.ts +++ b/src/client/components/index.ts @@ -14,6 +14,7 @@ import loading from './global/loading.vue'; import error from './global/error.vue'; import ad from './global/ad.vue'; import header from './global/header.vue'; +import spacer from './global/spacer.vue'; export default function(app: App) { app.component('I18n', i18n); @@ -30,4 +31,5 @@ export default function(app: App) { app.component('MkError', error); app.component('MkAd', ad); app.component('MkHeader', header); + app.component('MkSpacer', spacer); } diff --git a/src/client/pages/explore.vue b/src/client/pages/explore.vue index 26ce412612..2ca0668611 100644 --- a/src/client/pages/explore.vue +++ b/src/client/pages/explore.vue @@ -2,77 +2,79 @@ <div> <MkHeader :info="header"/> - <div class="lznhrdub _root"> - <div v-if="tab === 'local'"> - <div class="localfedi7 _block _isolated" v-if="meta && stats && tag == null" :style="{ backgroundImage: meta.bannerUrl ? `url(${meta.bannerUrl})` : null }"> - <header><span>{{ $t('explore', { host: meta.name || 'Misskey' }) }}</span></header> - <div><span>{{ $t('exploreUsersCount', { count: num(stats.originalUsersCount) }) }}</span></div> - </div> - - <template v-if="tag == null"> - <MkFolder class="_gap" persist-key="explore-pinned-users"> - <template #header><i class="fas fa-bookmark fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.pinnedUsers }}</template> - <XUserList :pagination="pinnedUsers"/> - </MkFolder> - <MkFolder class="_gap" persist-key="explore-popular-users"> - <template #header><i class="fas fa-chart-line fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.popularUsers }}</template> - <XUserList :pagination="popularUsers"/> - </MkFolder> - <MkFolder class="_gap" persist-key="explore-recently-updated-users"> - <template #header><i class="fas fa-comment-alt fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.recentlyUpdatedUsers }}</template> - <XUserList :pagination="recentlyUpdatedUsers"/> - </MkFolder> - <MkFolder class="_gap" persist-key="explore-recently-registered-users"> - <template #header><i class="fas fa-plus fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.recentlyRegisteredUsers }}</template> - <XUserList :pagination="recentlyRegisteredUsers"/> - </MkFolder> - </template> - </div> - <div v-else-if="tab === 'remote'"> - <div class="localfedi7 _block _isolated" v-if="tag == null" :style="{ backgroundImage: `url(/static-assets/client/fedi.jpg)` }"> - <header><span>{{ $ts.exploreFediverse }}</span></header> - </div> - - <MkFolder :foldable="true" :expanded="false" ref="tags" class="_gap"> - <template #header><i class="fas fa-hashtag fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.popularTags }}</template> - - <div class="vxjfqztj"> - <MkA v-for="tag in tagsLocal" :to="`/explore/tags/${tag.tag}`" :key="'local:' + tag.tag" class="local">{{ tag.tag }}</MkA> - <MkA v-for="tag in tagsRemote" :to="`/explore/tags/${tag.tag}`" :key="'remote:' + tag.tag">{{ tag.tag }}</MkA> + <MkSpacer :content-max="1200"> + <div class="lznhrdub"> + <div v-if="tab === 'local'"> + <div class="localfedi7 _block _isolated" v-if="meta && stats && tag == null" :style="{ backgroundImage: meta.bannerUrl ? `url(${meta.bannerUrl})` : null }"> + <header><span>{{ $t('explore', { host: meta.name || 'Misskey' }) }}</span></header> + <div><span>{{ $t('exploreUsersCount', { count: num(stats.originalUsersCount) }) }}</span></div> </div> - </MkFolder> - <MkFolder v-if="tag != null" :key="`${tag}`" class="_gap"> - <template #header><i class="fas fa-hashtag fa-fw" style="margin-right: 0.5em;"></i>{{ tag }}</template> - <XUserList :pagination="tagUsers"/> - </MkFolder> - - <template v-if="tag == null"> - <MkFolder class="_gap"> - <template #header><i class="fas fa-chart-line fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.popularUsers }}</template> - <XUserList :pagination="popularUsersF"/> - </MkFolder> - <MkFolder class="_gap"> - <template #header><i class="fas fa-comment-alt fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.recentlyUpdatedUsers }}</template> - <XUserList :pagination="recentlyUpdatedUsersF"/> - </MkFolder> - <MkFolder class="_gap"> - <template #header><i class="fas fa-rocket fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.recentlyDiscoveredUsers }}</template> - <XUserList :pagination="recentlyRegisteredUsersF"/> - </MkFolder> - </template> - </div> - <div v-else-if="tab === 'search'"> - <div class="_isolated"> - <MkInput v-model="query" :debounce="true" type="search"> - <template #prefix><i class="fas fa-search"></i></template> - <template #label>{{ $ts.searchUser }}</template> - </MkInput> + <template v-if="tag == null"> + <MkFolder class="_gap" persist-key="explore-pinned-users"> + <template #header><i class="fas fa-bookmark fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.pinnedUsers }}</template> + <XUserList :pagination="pinnedUsers"/> + </MkFolder> + <MkFolder class="_gap" persist-key="explore-popular-users"> + <template #header><i class="fas fa-chart-line fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.popularUsers }}</template> + <XUserList :pagination="popularUsers"/> + </MkFolder> + <MkFolder class="_gap" persist-key="explore-recently-updated-users"> + <template #header><i class="fas fa-comment-alt fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.recentlyUpdatedUsers }}</template> + <XUserList :pagination="recentlyUpdatedUsers"/> + </MkFolder> + <MkFolder class="_gap" persist-key="explore-recently-registered-users"> + <template #header><i class="fas fa-plus fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.recentlyRegisteredUsers }}</template> + <XUserList :pagination="recentlyRegisteredUsers"/> + </MkFolder> + </template> </div> + <div v-else-if="tab === 'remote'"> + <div class="localfedi7 _block _isolated" v-if="tag == null" :style="{ backgroundImage: `url(/static-assets/client/fedi.jpg)` }"> + <header><span>{{ $ts.exploreFediverse }}</span></header> + </div> - <XUserList v-if="query" class="_gap" :pagination="searchPagination" ref="search"/> + <MkFolder :foldable="true" :expanded="false" ref="tags" class="_gap"> + <template #header><i class="fas fa-hashtag fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.popularTags }}</template> + + <div class="vxjfqztj"> + <MkA v-for="tag in tagsLocal" :to="`/explore/tags/${tag.tag}`" :key="'local:' + tag.tag" class="local">{{ tag.tag }}</MkA> + <MkA v-for="tag in tagsRemote" :to="`/explore/tags/${tag.tag}`" :key="'remote:' + tag.tag">{{ tag.tag }}</MkA> + </div> + </MkFolder> + + <MkFolder v-if="tag != null" :key="`${tag}`" class="_gap"> + <template #header><i class="fas fa-hashtag fa-fw" style="margin-right: 0.5em;"></i>{{ tag }}</template> + <XUserList :pagination="tagUsers"/> + </MkFolder> + + <template v-if="tag == null"> + <MkFolder class="_gap"> + <template #header><i class="fas fa-chart-line fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.popularUsers }}</template> + <XUserList :pagination="popularUsersF"/> + </MkFolder> + <MkFolder class="_gap"> + <template #header><i class="fas fa-comment-alt fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.recentlyUpdatedUsers }}</template> + <XUserList :pagination="recentlyUpdatedUsersF"/> + </MkFolder> + <MkFolder class="_gap"> + <template #header><i class="fas fa-rocket fa-fw" style="margin-right: 0.5em;"></i>{{ $ts.recentlyDiscoveredUsers }}</template> + <XUserList :pagination="recentlyRegisteredUsersF"/> + </MkFolder> + </template> + </div> + <div v-else-if="tab === 'search'"> + <div class="_isolated"> + <MkInput v-model="query" :debounce="true" type="search"> + <template #prefix><i class="fas fa-search"></i></template> + <template #label>{{ $ts.searchUser }}</template> + </MkInput> + </div> + + <XUserList v-if="query" class="_gap" :pagination="searchPagination" ref="search"/> + </div> </div> - </div> + </MkSpacer> </div> </template> @@ -214,12 +216,6 @@ export default defineComponent({ </script> <style lang="scss" scoped> -.lznhrdub { - max-width: 1400px; - margin: 0 auto; - padding: 16px; -} - .localfedi7 { color: #fff; padding: 16px; diff --git a/src/client/pages/notifications.vue b/src/client/pages/notifications.vue index c6d7ea9b31..049d057d02 100644 --- a/src/client/pages/notifications.vue +++ b/src/client/pages/notifications.vue @@ -1,9 +1,11 @@ <template> <div> <MkHeader :info="header"/> - <div class="clupoqwt" v-size="{ min: [800] }"> - <XNotifications class="notifications" @before="before" @after="after" :include-types="includeTypes" :unread-only="tab === 'unread'"/> - </div> + <MkSpacer :content-max="800"> + <div class="clupoqwt"> + <XNotifications class="notifications" @before="before" @after="after" :include-types="includeTypes" :unread-only="tab === 'unread'"/> + </div> + </MkSpacer> </div> </template> @@ -90,14 +92,5 @@ export default defineComponent({ <style lang="scss" scoped> .clupoqwt { - &.min-width_800px { - background: var(--bg); - padding: 32px 0; - - > .notifications { - max-width: 800px; - margin: 0 auto; - } - } } </style> diff --git a/src/client/themes/d-botanical.json5 b/src/client/themes/d-botanical.json5 index f3665e22b2..c03b95e2d7 100644 --- a/src/client/themes/d-botanical.json5 +++ b/src/client/themes/d-botanical.json5 @@ -20,7 +20,7 @@ renote: '@accent', mention: 'rgb(212, 153, 76)', mentionMe: 'rgb(212, 210, 76)', - hashtag: 'rgb(76, 212, 180)', + hashtag: '#5bcbb0', link: '@accent', }, } From 46e4b07a87dc3ab62dc6bcaea9278b8c6226e780 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Thu, 14 Oct 2021 21:21:10 +0900 Subject: [PATCH 078/107] :art: --- src/client/ui/default.vue | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/client/ui/default.vue b/src/client/ui/default.vue index 6fc8cba3c9..69668ff96d 100644 --- a/src/client/ui/default.vue +++ b/src/client/ui/default.vue @@ -80,6 +80,12 @@ export default defineComponent({ XWidgets: defineAsyncComponent(() => import('./default.widgets.vue')), }, + provide() { + return { + shouldHeaderThin: this.showMenuOnTop, + }; + }, + data() { return { pageInfo: null, From 8ee4b180f994a84642995a44e62b405e566b114d Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Thu, 14 Oct 2021 22:40:43 +0900 Subject: [PATCH 079/107] :art: --- src/client/components/global/header.vue | 11 +++-------- src/client/ui/deck/column.vue | 3 ++- src/server/web/manifest.json | 2 +- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/client/components/global/header.vue b/src/client/components/global/header.vue index a283e1656c..492b77b1b4 100644 --- a/src/client/components/global/header.vue +++ b/src/client/components/global/header.vue @@ -1,7 +1,7 @@ <template> <div class="fdidabkb" :class="{ slim: narrow, thin: thin_ }" :style="{ background: bg }" @click="onClick" ref="el"> <template v-if="info"> - <div class="titleContainer" @click="showTabsPopup"> + <div class="titleContainer" @click="showTabsPopup" v-if="!hideTitle"> <i v-if="info.icon" class="icon" :class="info.icon"></i> <MkAvatar v-else-if="info.avatar" class="avatar" :user="info.avatar" :disable-preview="true" :show-indicator="true"/> @@ -17,7 +17,7 @@ </div> </div> </div> - <div class="tabs" v-if="!narrow"> + <div class="tabs" v-if="!narrow || hideTitle"> <button class="tab _button" v-for="tab in info.tabs" :class="{ active: tab.active }" @click="tab.onClick" v-tooltip="tab.title"> <i v-if="tab.icon" class="icon" :class="tab.icon"></i> <span v-if="!tab.iconOnly" class="title">{{ tab.title }}</span> @@ -182,6 +182,7 @@ export default defineComponent({ showTabsPopup, preventDrag, onClick, + hideTitle: inject('shouldOmitHeaderTitle', false), thin_: props.thin || inject('shouldHeaderThin', false) }; }, @@ -210,12 +211,6 @@ export default defineComponent({ > .titleContainer { margin: 0 auto; } - - > .buttons { - &.right { - margin-left: 0; - } - } } > .buttons { diff --git a/src/client/ui/deck/column.vue b/src/client/ui/deck/column.vue index 59729e09f7..4f8d1632d9 100644 --- a/src/client/ui/deck/column.vue +++ b/src/client/ui/deck/column.vue @@ -38,7 +38,8 @@ import { deckStore } from './deck-store'; export default defineComponent({ provide: { - shouldHeaderThin: true + shouldHeaderThin: true, + shouldOmitHeaderTitle: true, }, props: { diff --git a/src/server/web/manifest.json b/src/server/web/manifest.json index 48030a2980..db97531bbf 100644 --- a/src/server/web/manifest.json +++ b/src/server/web/manifest.json @@ -2,7 +2,7 @@ "short_name": "Misskey", "name": "Misskey", "start_url": "/", - "display": "standalone", + "display": "minimal-ui", "background_color": "#313a42", "theme_color": "#86b300", "icons": [ From 72b616a990872a6bc53fb3fb7155dd6ab8d7615d Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Thu, 14 Oct 2021 23:14:14 +0900 Subject: [PATCH 080/107] :art: --- src/client/components/modal-page-window.vue | 2 +- src/client/components/page-window.vue | 2 +- src/client/ui/deck/column.vue | 2 +- src/client/ui/deck/main-column.vue | 1 + 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/client/components/modal-page-window.vue b/src/client/components/modal-page-window.vue index cea92b9cb3..3ca7c31e0e 100644 --- a/src/client/components/modal-page-window.vue +++ b/src/client/components/modal-page-window.vue @@ -2,7 +2,7 @@ <MkModal ref="modal" @click="$emit('click')" @closed="$emit('closed')"> <div class="hrmcaedk _window _narrow_" :style="{ width: `${width}px`, height: (height ? `min(${height}px, 100%)` : '100%') }"> <div class="header" @contextmenu="onContextmenu"> - <button v-if="history.length > 0" class="_button" @click="back()"><i class="fas fa-arrow-left"></i></button> + <button v-if="history.length > 0" class="_button" @click="back()" v-tooltip="$ts.goBack"><i class="fas fa-arrow-left"></i></button> <span v-else style="display: inline-block; width: 20px"></span> <span v-if="pageInfo" class="title"> <i v-if="pageInfo.icon" class="icon" :class="pageInfo.icon"></i> diff --git a/src/client/components/page-window.vue b/src/client/components/page-window.vue index 620d288b14..904d13e0e8 100644 --- a/src/client/components/page-window.vue +++ b/src/client/components/page-window.vue @@ -14,7 +14,7 @@ </template> </template> <template #headerLeft> - <button v-if="history.length > 0" class="_button" @click="back()"><i class="fas fa-arrow-left"></i></button> + <button v-if="history.length > 0" class="_button" @click="back()" v-tooltip="$ts.goBack"><i class="fas fa-arrow-left"></i></button> </template> <div class="yrolvcoq _flat_"> <component :is="component" v-bind="props" :ref="changePage"/> diff --git a/src/client/ui/deck/column.vue b/src/client/ui/deck/column.vue index 4f8d1632d9..c04297e384 100644 --- a/src/client/ui/deck/column.vue +++ b/src/client/ui/deck/column.vue @@ -362,7 +362,7 @@ export default defineComponent({ } > .toggleActive, - > .action > *, + > .action > ::v-deep(*), > .menu { z-index: 1; width: var(--deckColumnHeaderHeight); diff --git a/src/client/ui/deck/main-column.vue b/src/client/ui/deck/main-column.vue index 4c1fa255a6..2127444769 100644 --- a/src/client/ui/deck/main-column.vue +++ b/src/client/ui/deck/main-column.vue @@ -2,6 +2,7 @@ <XColumn v-if="deckStore.state.alwaysShowMainColumn || $route.name !== 'index'" :column="column" :is-stacked="isStacked"> <template #header> <template v-if="pageInfo"> + <i :class="pageInfo.icon"></i> {{ pageInfo.title }} </template> </template> From f00d543447b0d5f3e6a391cd795a20509eb49e13 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 15 Oct 2021 02:42:10 +0900 Subject: [PATCH 081/107] =?UTF-8?q?fix(client):=20Deck=20UI=E3=81=AB?= =?UTF-8?q?=E3=81=8A=E3=81=84=E3=81=A6=E3=80=81=E9=87=8D=E3=81=AD=E3=81=9F?= =?UTF-8?q?=E3=82=AB=E3=83=A9=E3=83=A0=E3=81=AE=E7=89=87=E6=96=B9=E3=82=92?= =?UTF-8?q?=E7=95=B3=E3=82=93=E3=81=A0=E7=8A=B6=E6=85=8B=E3=81=A7=E5=8F=B3?= =?UTF-8?q?=E3=81=AB=E5=87=BA=E3=81=99=E3=81=A8=E8=A1=A8=E7=A4=BA=E3=81=8C?= =?UTF-8?q?=E5=A3=8A=E3=82=8C=E3=82=8B=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix #7867 --- CHANGELOG.md | 1 + src/client/ui/deck/deck-store.ts | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e1f33fca7..040c807677 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ - クライアント: ヘッダーにタブが表示されている状態でタイトルをクリックしたときにタブ選択が表示されるのを修正 - クライアント: ユーザーページのタブが機能していない問題を修正 - クライアント: ピン留めユーザーの設定項目がない問題を修正 +- クライアント: Deck UIにおいて、重ねたカラムの片方を畳んだ状態で右に出すと表示が壊れる問題を修正 - API: 管理者およびモデレーターをブロックできてしまう問題を修正 ## 12.91.0 (2021/09/22) diff --git a/src/client/ui/deck/deck-store.ts b/src/client/ui/deck/deck-store.ts index aa389d7610..6c61bf5539 100644 --- a/src/client/ui/deck/deck-store.ts +++ b/src/client/ui/deck/deck-store.ts @@ -219,10 +219,20 @@ export function stackLeftColumn(id: Column['id']) { export function popRightColumn(id: Column['id']) { let layout = copy(deckStore.state.layout); const i = deckStore.state.layout.findIndex(ids => ids.includes(id)); + const affected = layout[i]; layout = layout.map(ids => ids.filter(_id => _id !== id)); layout.splice(i + 1, 0, [id]); layout = layout.filter(ids => ids.length > 0); deckStore.set('layout', layout); + + const columns = copy(deckStore.state.columns); + for (const column of columns) { + if (affected.includes(column.id)) { + column.active = true; + } + } + deckStore.set('columns', columns); + saveDeck(); } From 056ab675cf4b1c92e728a501ebe13cc5f4086836 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 15 Oct 2021 23:33:27 +0900 Subject: [PATCH 082/107] Revert ":art:" This reverts commit b77167a4a122d8dfce3abdd10e340e56a03ac43c. --- src/client/components/ui/menu.vue | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/client/components/ui/menu.vue b/src/client/components/ui/menu.vue index 88c4ce5b99..d144e65c83 100644 --- a/src/client/components/ui/menu.vue +++ b/src/client/components/ui/menu.vue @@ -170,10 +170,9 @@ export default defineComponent({ left: 0; right: 0; margin: auto; - //width: calc(100% - 16px); - width: 100%; + width: calc(100% - 16px); height: 100%; - //border-radius: 6px; + border-radius: 6px; } > * { From c11c22fc73de4b0a7be6fe3343ef6114baf7e09a Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Fri, 15 Oct 2021 23:35:28 +0900 Subject: [PATCH 083/107] :art: --- src/client/components/ui/menu.vue | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/client/components/ui/menu.vue b/src/client/components/ui/menu.vue index d144e65c83..da24d90170 100644 --- a/src/client/components/ui/menu.vue +++ b/src/client/components/ui/menu.vue @@ -152,7 +152,7 @@ export default defineComponent({ > .item { display: block; position: relative; - padding: 8px 16px; + padding: 8px 18px; width: 100%; box-sizing: border-box; white-space: nowrap; @@ -242,12 +242,12 @@ export default defineComponent({ } > i { - margin-right: 4px; + margin-right: 5px; width: 20px; } > .avatar { - margin-right: 4px; + margin-right: 5px; width: 20px; height: 20px; } From fe62f3508b07cee8332bf52b5acbe5c4a2218490 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 16 Oct 2021 01:15:22 +0900 Subject: [PATCH 084/107] :art: --- src/client/components/ui/super-menu.vue | 4 ++-- src/client/pages/settings/index.vue | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/client/components/ui/super-menu.vue b/src/client/components/ui/super-menu.vue index f43b545fd6..35fc81550d 100644 --- a/src/client/components/ui/super-menu.vue +++ b/src/client/components/ui/super-menu.vue @@ -65,8 +65,8 @@ export default defineComponent({ align-items: center; width: 100%; box-sizing: border-box; - padding: 10px 16px 10px 14px; - border-radius: 999px; + padding: 10px 16px 10px 8px; + border-radius: 9px; font-size: 0.9em; &:hover { diff --git a/src/client/pages/settings/index.vue b/src/client/pages/settings/index.vue index cc3fd44f70..bdcadfb955 100644 --- a/src/client/pages/settings/index.vue +++ b/src/client/pages/settings/index.vue @@ -271,6 +271,12 @@ export default defineComponent({ <style lang="scss" scoped> .vvcocwet { > .nav { + > .title { + margin: 16px; + font-size: 1.5em; + font-weight: bold; + } + > .info { margin: 0 16px; } From e78f16bcc48e2298164a70c56d08bb1c7955f549 Mon Sep 17 00:00:00 2001 From: nullobsi <me@nullob.si> Date: Fri, 15 Oct 2021 09:19:49 -0700 Subject: [PATCH 085/107] =?UTF-8?q?feat:=20=E3=83=8E=E3=83=BC=E3=83=88?= =?UTF-8?q?=E3=83=97=E3=83=AC=E3=83=93=E3=83=A5=E3=83=BC=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0=20(#7596)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add note preview * use if * add draftedNote property * custom emojis work * Only show CW on preview when enabled * move button to top * fix css style --- locales/ja-JP.yml | 1 + src/client/components/post-form.vue | 35 ++++++++++++++++++++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 5798ce7ec2..3111768189 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -766,6 +766,7 @@ middle: "中" low: "低" emailNotConfiguredWarning: "メールアドレスの設定がされていません。" ratio: "比率" +notePreview: "ノートをプレビュー" customCss: "カスタムCSS" customCssWarn: "この設定は必ず知識のある方が行ってください。不適切な設定を行うとクライアントが正常に使用できなくなる恐れがあります。" global: "グローバル" diff --git a/src/client/components/post-form.vue b/src/client/components/post-form.vue index 3ff1dfe0df..85117b074e 100644 --- a/src/client/components/post-form.vue +++ b/src/client/components/post-form.vue @@ -17,6 +17,7 @@ <span v-if="visibility === 'followers'"><i class="fas fa-unlock"></i></span> <span v-if="visibility === 'specified'"><i class="fas fa-envelope"></i></span> </button> + <button class="_button preview" @click="showPreview = !showPreview" :class="{ active: showPreview }" v-tooltip="$ts.notePreview"><i class="fas fa-file-code"></i></button> <button class="submit _buttonPrimary" :disabled="!canPost" @click="post" data-cy-open-post-form-submit>{{ submitText }}<i :class="reply ? 'fas fa-reply' : renote ? 'fas fa-quote-right' : 'fas fa-paper-plane'"></i></button> </div> </header> @@ -40,6 +41,7 @@ <input v-show="withHashtags" ref="hashtags" class="hashtags" v-model="hashtags" :placeholder="$ts.hashtags" list="hashtags"> <XPostFormAttaches class="attaches" :files="files" @updated="updateFiles" @detach="detachFile" @changeSensitive="updateFileSensitive" @changeName="updateFileName"/> <XPollEditor v-if="poll" :poll="poll" @destroyed="poll = null" @updated="onPollUpdate"/> + <XNotePreview class="preview" v-if="showPreview" :note="draftedNote"/> <footer> <button class="_button" @click="chooseFileFrom" v-tooltip="$ts.attachFile"><i class="fas fa-photo-video"></i></button> <button class="_button" @click="togglePoll" :class="{ active: poll }" v-tooltip="$ts.poll"><i class="fas fa-poll-h"></i></button> @@ -143,6 +145,7 @@ export default defineComponent({ files: [], poll: null, useCw: false, + showPreview: false, cw: null, localOnly: this.$store.state.rememberNoteVisibility ? this.$store.state.localOnly : this.$store.state.defaultNoteLocalOnly, visibility: this.$store.state.rememberNoteVisibility ? this.$store.state.visibility : this.$store.state.defaultNoteVisibility, @@ -220,6 +223,18 @@ export default defineComponent({ return this.$instance ? this.$instance.maxNoteTextLength : 1000; }, + draftedNote(): object { + return { + user: this.$i, + createdAt: new Date(), + visibility: this.visibility, + localOnly: this.localOnly, + cw: this.useCw ? this.cw : null, + text: this.text, + files: this.files, + poll: this.poll, + }; + } withHashtags: defaultStore.makeGetterSetter('postFormWithHashtags'), hashtags: defaultStore.makeGetterSetter('postFormHashtags'), }, @@ -717,7 +732,7 @@ export default defineComponent({ > .visibility { height: 34px; width: 34px; - margin: 0 8px; + margin: 0 0 0 8px; & + .localOnly { margin-left: 0 !important; @@ -729,6 +744,24 @@ export default defineComponent({ opacity: 0.7; } + > .preview { + display: inline-block; + padding: 0; + margin: 0 8px 0 0; + font-size: 16px; + width: 34px; + height: 34px; + border-radius: 6px; + + &:hover { + background: var(--X5); + } + + &.active { + color: var(--accent); + } + } + > .submit { margin: 16px 16px 16px 0; padding: 0 12px; From 5fb4538315eca7b22ad326ac60c4ebd6a53b20f3 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 16 Oct 2021 01:28:34 +0900 Subject: [PATCH 086/107] =?UTF-8?q?refactor(client):=20=E3=82=B3=E3=83=B3?= =?UTF-8?q?=E3=83=9D=E3=83=BC=E3=83=8D=E3=83=B3=E3=83=88=E5=90=8D=E3=81=8C?= =?UTF-8?q?=E7=B4=9B=E3=82=89=E3=82=8F=E3=81=97=E3=81=8F=E3=81=AA=E3=82=8B?= =?UTF-8?q?=E3=81=AE=E3=81=A7preview->simple=E3=81=AB=E3=83=AA=E3=83=8D?= =?UTF-8?q?=E3=83=BC=E3=83=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/client/components/note-detailed.vue | 6 +++--- .../components/{note-preview.vue => note-simple.vue} | 0 src/client/components/note.vue | 6 +++--- src/client/components/post-form.vue | 10 +++++----- src/client/ui/chat/note.vue | 6 +++--- 5 files changed, 14 insertions(+), 14 deletions(-) rename src/client/components/{note-preview.vue => note-simple.vue} (100%) diff --git a/src/client/components/note-detailed.vue b/src/client/components/note-detailed.vue index 68e7c87f2e..40b0a68c58 100644 --- a/src/client/components/note-detailed.vue +++ b/src/client/components/note-detailed.vue @@ -80,7 +80,7 @@ </div> <XPoll v-if="appearNote.poll" :note="appearNote" ref="pollViewer" class="poll"/> <MkUrlPreview v-for="url in urls" :url="url" :key="url" :compact="true" :detail="true" class="url-preview"/> - <div class="renote" v-if="appearNote.renote"><XNotePreview :note="appearNote.renote"/></div> + <div class="renote" v-if="appearNote.renote"><XNoteSimple :note="appearNote.renote"/></div> </div> <MkA v-if="appearNote.channel && !inChannel" class="channel" :to="`/channels/${appearNote.channel.id}`"><i class="fas fa-satellite-dish"></i> {{ appearNote.channel.name }}</MkA> </div> @@ -132,7 +132,7 @@ import * as mfm from 'mfm-js'; import { sum } from '../../prelude/array'; import XSub from './note.sub.vue'; import XNoteHeader from './note-header.vue'; -import XNotePreview from './note-preview.vue'; +import XNoteSimple from './note-simple.vue'; import XReactionsViewer from './reactions-viewer.vue'; import XMediaList from './media-list.vue'; import XCwButton from './cw-button.vue'; @@ -153,7 +153,7 @@ export default defineComponent({ components: { XSub, XNoteHeader, - XNotePreview, + XNoteSimple, XReactionsViewer, XMediaList, XCwButton, diff --git a/src/client/components/note-preview.vue b/src/client/components/note-simple.vue similarity index 100% rename from src/client/components/note-preview.vue rename to src/client/components/note-simple.vue diff --git a/src/client/components/note.vue b/src/client/components/note.vue index 3b07884cee..91a3e3b87d 100644 --- a/src/client/components/note.vue +++ b/src/client/components/note.vue @@ -64,7 +64,7 @@ </div> <XPoll v-if="appearNote.poll" :note="appearNote" ref="pollViewer" class="poll"/> <MkUrlPreview v-for="url in urls" :url="url" :key="url" :compact="true" :detail="false" class="url-preview"/> - <div class="renote" v-if="appearNote.renote"><XNotePreview :note="appearNote.renote"/></div> + <div class="renote" v-if="appearNote.renote"><XNoteSimple :note="appearNote.renote"/></div> <button v-if="collapsed" class="fade _button" @click="collapsed = false"> <span>{{ $ts.showMore }}</span> </button> @@ -114,7 +114,7 @@ import * as mfm from 'mfm-js'; import { sum } from '../../prelude/array'; import XSub from './note.sub.vue'; import XNoteHeader from './note-header.vue'; -import XNotePreview from './note-preview.vue'; +import XNoteSimple from './note-simple.vue'; import XReactionsViewer from './reactions-viewer.vue'; import XMediaList from './media-list.vue'; import XCwButton from './cw-button.vue'; @@ -134,7 +134,7 @@ export default defineComponent({ components: { XSub, XNoteHeader, - XNotePreview, + XNoteSimple, XReactionsViewer, XMediaList, XCwButton, diff --git a/src/client/components/post-form.vue b/src/client/components/post-form.vue index 85117b074e..3b8db21099 100644 --- a/src/client/components/post-form.vue +++ b/src/client/components/post-form.vue @@ -22,8 +22,8 @@ </div> </header> <div class="form" :class="{ fixed }"> - <XNotePreview class="preview" v-if="reply" :note="reply"/> - <XNotePreview class="preview" v-if="renote" :note="renote"/> + <XNoteSimple class="preview" v-if="reply" :note="reply"/> + <XNoteSimple class="preview" v-if="renote" :note="renote"/> <div class="with-quote" v-if="quoteId"><i class="fas fa-quote-left"></i> {{ $ts.quoteAttached }}<button @click="quoteId = null"><i class="fas fa-times"></i></button></div> <div v-if="visibility === 'specified'" class="to-specified"> <span style="margin-right: 8px;">{{ $ts.recipient }}</span> @@ -41,7 +41,7 @@ <input v-show="withHashtags" ref="hashtags" class="hashtags" v-model="hashtags" :placeholder="$ts.hashtags" list="hashtags"> <XPostFormAttaches class="attaches" :files="files" @updated="updateFiles" @detach="detachFile" @changeSensitive="updateFileSensitive" @changeName="updateFileName"/> <XPollEditor v-if="poll" :poll="poll" @destroyed="poll = null" @updated="onPollUpdate"/> - <XNotePreview class="preview" v-if="showPreview" :note="draftedNote"/> + <XNoteSimple class="preview" v-if="showPreview" :note="draftedNote"/> <footer> <button class="_button" @click="chooseFileFrom" v-tooltip="$ts.attachFile"><i class="fas fa-photo-video"></i></button> <button class="_button" @click="togglePoll" :class="{ active: poll }" v-tooltip="$ts.poll"><i class="fas fa-poll-h"></i></button> @@ -63,7 +63,7 @@ import { defineComponent, defineAsyncComponent } from 'vue'; import insertTextAtCursor from 'insert-text-at-cursor'; import { length } from 'stringz'; import { toASCII } from 'punycode/'; -import XNotePreview from './note-preview.vue'; +import XNoteSimple from './note-simple.vue'; import * as mfm from 'mfm-js'; import { host, url } from '@client/config'; import { erase, unique } from '../../prelude/array'; @@ -82,7 +82,7 @@ import { defaultStore } from '@client/store'; export default defineComponent({ components: { - XNotePreview, + XNoteSimple, XPostFormAttaches: defineAsyncComponent(() => import('./post-form-attaches.vue')), XPollEditor: defineAsyncComponent(() => import('./poll-editor.vue')), MkInfo, diff --git a/src/client/ui/chat/note.vue b/src/client/ui/chat/note.vue index c376887b84..0a054d1057 100644 --- a/src/client/ui/chat/note.vue +++ b/src/client/ui/chat/note.vue @@ -56,7 +56,7 @@ </div> <XPoll v-if="appearNote.poll" :note="appearNote" ref="pollViewer" class="poll"/> <MkUrlPreview v-for="url in urls" :url="url" :key="url" :compact="true" :detail="false" class="url-preview"/> - <div class="renote" v-if="appearNote.renote"><XNotePreview :note="appearNote.renote"/></div> + <div class="renote" v-if="appearNote.renote"><XNoteSimple :note="appearNote.renote"/></div> <button v-if="collapsed" class="fade _button" @click="collapsed = false"> <span>{{ $ts.showMore }}</span> </button> @@ -106,7 +106,7 @@ import * as mfm from 'mfm-js'; import { sum } from '../../../prelude/array'; import XSub from './note.sub.vue'; import XNoteHeader from './note-header.vue'; -import XNotePreview from './note-preview.vue'; +import XNoteSimple from './note-preview.vue'; import XReactionsViewer from '@client/components/reactions-viewer.vue'; import XMediaList from '@client/components/media-list.vue'; import XCwButton from '@client/components/cw-button.vue'; @@ -126,7 +126,7 @@ export default defineComponent({ components: { XSub, XNoteHeader, - XNotePreview, + XNoteSimple, XReactionsViewer, XMediaList, XCwButton, From bd8052fedb59ced318c5d59b1551c287267e1dce Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 16 Oct 2021 01:34:23 +0900 Subject: [PATCH 087/107] =?UTF-8?q?refactor(client):=20=E3=83=80=E3=83=9F?= =?UTF-8?q?=E3=83=BC=E3=82=B3=E3=83=B3=E3=83=9D=E3=83=BC=E3=83=8D=E3=83=B3?= =?UTF-8?q?=E3=83=88=E3=82=92=E4=BD=BF=E3=81=A3=E3=81=A6=E3=83=8E=E3=83=BC?= =?UTF-8?q?=E3=83=88=E3=83=97=E3=83=AC=E3=83=93=E3=83=A5=E3=83=BC=E3=81=99?= =?UTF-8?q?=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/ja-JP.yml | 2 +- src/client/components/note-preview.vue | 98 ++++++++++++++++++++++++++ src/client/components/post-form.vue | 18 ++--- 3 files changed, 103 insertions(+), 15 deletions(-) create mode 100644 src/client/components/note-preview.vue diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 3111768189..358da897d0 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -766,7 +766,7 @@ middle: "中" low: "低" emailNotConfiguredWarning: "メールアドレスの設定がされていません。" ratio: "比率" -notePreview: "ノートをプレビュー" +previewNoteText: "本文をプレビュー" customCss: "カスタムCSS" customCssWarn: "この設定は必ず知識のある方が行ってください。不適切な設定を行うとクライアントが正常に使用できなくなる恐れがあります。" global: "グローバル" diff --git a/src/client/components/note-preview.vue b/src/client/components/note-preview.vue new file mode 100644 index 0000000000..a474a01341 --- /dev/null +++ b/src/client/components/note-preview.vue @@ -0,0 +1,98 @@ +<template> +<div class="fefdfafb" v-size="{ min: [350, 500] }"> + <MkAvatar class="avatar" :user="$i"/> + <div class="main"> + <div class="header"> + <MkUserName :user="$i"/> + </div> + <div class="body"> + <div class="content"> + <Mfm :text="text" :author="$i" :i="$i"/> + </div> + </div> + </div> +</div> +</template> + +<script lang="ts"> +import { defineComponent } from 'vue'; + +export default defineComponent({ + components: { + }, + + props: { + text: { + type: String, + required: true + } + }, +}); +</script> + +<style lang="scss" scoped> +.fefdfafb { + display: flex; + margin: 0; + padding: 0; + overflow: clip; + font-size: 0.95em; + + &.min-width_350px { + > .avatar { + margin: 0 10px 0 0; + width: 44px; + height: 44px; + } + } + + &.min-width_500px { + > .avatar { + margin: 0 12px 0 0; + width: 48px; + height: 48px; + } + } + + > .avatar { + flex-shrink: 0; + display: block; + margin: 0 10px 0 0; + width: 40px; + height: 40px; + border-radius: 8px; + } + + > .main { + flex: 1; + min-width: 0; + + > .header { + margin-bottom: 2px; + } + + > .body { + + > .cw { + cursor: default; + display: block; + margin: 0; + padding: 0; + overflow-wrap: break-word; + + > .text { + margin-right: 8px; + } + } + + > .content { + > .text { + cursor: default; + margin: 0; + padding: 0; + } + } + } + } +} +</style> diff --git a/src/client/components/post-form.vue b/src/client/components/post-form.vue index 3b8db21099..7b4c5db46b 100644 --- a/src/client/components/post-form.vue +++ b/src/client/components/post-form.vue @@ -17,7 +17,7 @@ <span v-if="visibility === 'followers'"><i class="fas fa-unlock"></i></span> <span v-if="visibility === 'specified'"><i class="fas fa-envelope"></i></span> </button> - <button class="_button preview" @click="showPreview = !showPreview" :class="{ active: showPreview }" v-tooltip="$ts.notePreview"><i class="fas fa-file-code"></i></button> + <button class="_button preview" @click="showPreview = !showPreview" :class="{ active: showPreview }" v-tooltip="$ts.previewNoteText"><i class="fas fa-file-code"></i></button> <button class="submit _buttonPrimary" :disabled="!canPost" @click="post" data-cy-open-post-form-submit>{{ submitText }}<i :class="reply ? 'fas fa-reply' : renote ? 'fas fa-quote-right' : 'fas fa-paper-plane'"></i></button> </div> </header> @@ -41,7 +41,7 @@ <input v-show="withHashtags" ref="hashtags" class="hashtags" v-model="hashtags" :placeholder="$ts.hashtags" list="hashtags"> <XPostFormAttaches class="attaches" :files="files" @updated="updateFiles" @detach="detachFile" @changeSensitive="updateFileSensitive" @changeName="updateFileName"/> <XPollEditor v-if="poll" :poll="poll" @destroyed="poll = null" @updated="onPollUpdate"/> - <XNoteSimple class="preview" v-if="showPreview" :note="draftedNote"/> + <XNotePreview class="preview" v-if="showPreview" :text="text"/> <footer> <button class="_button" @click="chooseFileFrom" v-tooltip="$ts.attachFile"><i class="fas fa-photo-video"></i></button> <button class="_button" @click="togglePoll" :class="{ active: poll }" v-tooltip="$ts.poll"><i class="fas fa-poll-h"></i></button> @@ -64,6 +64,7 @@ import insertTextAtCursor from 'insert-text-at-cursor'; import { length } from 'stringz'; import { toASCII } from 'punycode/'; import XNoteSimple from './note-simple.vue'; +import XNotePreview from './note-preview.vue'; import * as mfm from 'mfm-js'; import { host, url } from '@client/config'; import { erase, unique } from '../../prelude/array'; @@ -83,6 +84,7 @@ import { defaultStore } from '@client/store'; export default defineComponent({ components: { XNoteSimple, + XNotePreview, XPostFormAttaches: defineAsyncComponent(() => import('./post-form-attaches.vue')), XPollEditor: defineAsyncComponent(() => import('./poll-editor.vue')), MkInfo, @@ -223,18 +225,6 @@ export default defineComponent({ return this.$instance ? this.$instance.maxNoteTextLength : 1000; }, - draftedNote(): object { - return { - user: this.$i, - createdAt: new Date(), - visibility: this.visibility, - localOnly: this.localOnly, - cw: this.useCw ? this.cw : null, - text: this.text, - files: this.files, - poll: this.poll, - }; - } withHashtags: defaultStore.makeGetterSetter('postFormWithHashtags'), hashtags: defaultStore.makeGetterSetter('postFormHashtags'), }, From 4a766a19cf1948ab84c37e2aa58f47f2e4113b6e Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 16 Oct 2021 01:35:16 +0900 Subject: [PATCH 088/107] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 040c807677..e7392952b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ - アカウント登録にメールアドレスの設定を必須にするオプション - クライアント: アニメーションを減らす設定をメニューのアニメーションにも適用するように - クライアント: MFM関数構文のサジェストを実装 +- クライアント: ノート本文を投稿フォーム内でプレビューできるように - クライアント: 未読の通知のみ表示する機能 - クライアント: 通知ページで通知の種類によるフィルタ - クライアント: 新しいダークテーマを追加 From 8cabc5953ef5e12640598b870ede40c5f24a8d94 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 16 Oct 2021 01:54:28 +0900 Subject: [PATCH 089/107] Update CHANGELOG.md --- CHANGELOG.md | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e7392952b0..c7c01f6099 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,18 +12,18 @@ ### Improvements - アカウント登録にメールアドレスの設定を必須にするオプション -- クライアント: アニメーションを減らす設定をメニューのアニメーションにも適用するように +- クライアント: 全体的なUIのブラッシュアップ - クライアント: MFM関数構文のサジェストを実装 - クライアント: ノート本文を投稿フォーム内でプレビューできるように - クライアント: 未読の通知のみ表示する機能 - クライアント: 通知ページで通知の種類によるフィルタ +- クライアント: アニメーションを減らす設定をメニューのアニメーションにも適用するように - クライアント: 新しいダークテーマを追加 - クライアント: テーマコンパイラに hue と saturate 関数を追加 - ActivityPub: HTML -> MFMの変換を強化 - API: i/notifications に unreadOnly オプションを追加 - API: ap系のエンドポイントをログイン必須化+レートリミット追加 -- Misskeyのコマンドラインオプションを廃止 - - 代わりに環境変数で設定することができます +- MFM: Add tag syntaxes of bold <b></b> and strikethrough <s></s> ### Bugfixes - Fix createDeleteAccountJob @@ -34,6 +34,15 @@ - クライアント: ピン留めユーザーの設定項目がない問題を修正 - クライアント: Deck UIにおいて、重ねたカラムの片方を畳んだ状態で右に出すと表示が壊れる問題を修正 - API: 管理者およびモデレーターをブロックできてしまう問題を修正 +- MFM: Mentions in the link label are parsed as text +- MFM: Add a property to the URL node indicating whether it was enclosed in <> +- MFM: Disallows < and > in hashtags + +### Changes +- 保守性やユーザビリティの観点から、Misskeyのコマンドラインオプションが削除されました。 + - 必要であれば、代わりに環境変数で設定することができます +- MFM: パフォーマンス、保守性、構文誤認識抑制の観点から、旧関数構文のサポートが削除されました。 + - 旧構文(`[foo bar]`)を使用せず、現行の構文(`$[foo bar]`)を使用してください。 ## 12.91.0 (2021/09/22) From ff0521e3aa9b3535e9dcf58e6c1abe6516ed6520 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 16 Oct 2021 13:02:14 +0900 Subject: [PATCH 090/107] =?UTF-8?q?enhance(client):=20=E3=82=A2=E3=83=8B?= =?UTF-8?q?=E3=83=A1=E3=83=BC=E3=82=B7=E3=83=A7=E3=83=B3=E3=82=92=E6=B8=9B?= =?UTF-8?q?=E3=82=89=E3=81=99=E8=A8=AD=E5=AE=9A=E3=81=AE=E9=81=A9=E7=94=A8?= =?UTF-8?q?=E7=AF=84=E5=9B=B2=E3=82=92=E6=8B=A1=E5=85=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 2 +- src/client/pages/emojis.emoji.vue | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c7c01f6099..128c212c0b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,7 @@ - クライアント: ノート本文を投稿フォーム内でプレビューできるように - クライアント: 未読の通知のみ表示する機能 - クライアント: 通知ページで通知の種類によるフィルタ -- クライアント: アニメーションを減らす設定をメニューのアニメーションにも適用するように +- クライアント: アニメーションを減らす設定の適用範囲を拡充 - クライアント: 新しいダークテーマを追加 - クライアント: テーマコンパイラに hue と saturate 関数を追加 - ActivityPub: HTML -> MFMの変換を強化 diff --git a/src/client/pages/emojis.emoji.vue b/src/client/pages/emojis.emoji.vue index 3c9bb4debe..ca0ef2dbb7 100644 --- a/src/client/pages/emojis.emoji.vue +++ b/src/client/pages/emojis.emoji.vue @@ -23,12 +23,14 @@ export default defineComponent({ }, mounted() { - VanillaTilt.init(this.$el, { - reverse: true, - gyroscope: false, - scale: 1.1, - speed: 500, - }); + if (this.$store.animation) { + VanillaTilt.init(this.$el, { + reverse: true, + gyroscope: false, + scale: 1.1, + speed: 500, + }); + } }, methods: { From 1e28081aa3ea3cc01f74392b7a20129eaa0cdead Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 16 Oct 2021 13:02:21 +0900 Subject: [PATCH 091/107] :art: --- src/client/components/global/header.vue | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/client/components/global/header.vue b/src/client/components/global/header.vue index 492b77b1b4..a4466da498 100644 --- a/src/client/components/global/header.vue +++ b/src/client/components/global/header.vue @@ -209,7 +209,17 @@ export default defineComponent({ text-align: center; > .titleContainer { + flex: 1; margin: 0 auto; + margin-left: var(--height); + + > *:first-child { + margin-left: auto; + } + + > *:last-child { + margin-right: auto; + } } } From aee816ced93e412c4ef918e77c928649b2e4dd21 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 16 Oct 2021 15:00:55 +0900 Subject: [PATCH 092/107] :art: --- src/client/components/post-form.vue | 3 ++- src/client/components/ui/button.vue | 4 ++-- src/client/pages/settings/index.vue | 4 ++++ src/client/style.scss | 14 ++++++++++++++ 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/client/components/post-form.vue b/src/client/components/post-form.vue index 7b4c5db46b..a1d89d2a2e 100644 --- a/src/client/components/post-form.vue +++ b/src/client/components/post-form.vue @@ -18,7 +18,7 @@ <span v-if="visibility === 'specified'"><i class="fas fa-envelope"></i></span> </button> <button class="_button preview" @click="showPreview = !showPreview" :class="{ active: showPreview }" v-tooltip="$ts.previewNoteText"><i class="fas fa-file-code"></i></button> - <button class="submit _buttonPrimary" :disabled="!canPost" @click="post" data-cy-open-post-form-submit>{{ submitText }}<i :class="reply ? 'fas fa-reply' : renote ? 'fas fa-quote-right' : 'fas fa-paper-plane'"></i></button> + <button class="submit _buttonGradate" :disabled="!canPost" @click="post" data-cy-open-post-form-submit>{{ submitText }}<i :class="reply ? 'fas fa-reply' : renote ? 'fas fa-quote-right' : 'fas fa-paper-plane'"></i></button> </div> </header> <div class="form" :class="{ fixed }"> @@ -759,6 +759,7 @@ export default defineComponent({ font-weight: bold; vertical-align: bottom; border-radius: 4px; + font-size: 0.9em; &:disabled { opacity: 0.7; diff --git a/src/client/components/ui/button.vue b/src/client/components/ui/button.vue index 5f36be0d76..b5f4547c84 100644 --- a/src/client/components/ui/button.vue +++ b/src/client/components/ui/button.vue @@ -188,11 +188,11 @@ export default defineComponent({ background: linear-gradient(90deg, var(--buttonGradateA), var(--buttonGradateB)); &:not(:disabled):hover { - background: var(--X8); + background: linear-gradient(90deg, var(--X8), var(--X8)); } &:not(:disabled):active { - background: var(--X8); + background: linear-gradient(90deg, var(--X8), var(--X8)); } } diff --git a/src/client/pages/settings/index.vue b/src/client/pages/settings/index.vue index bdcadfb955..9da3031a41 100644 --- a/src/client/pages/settings/index.vue +++ b/src/client/pages/settings/index.vue @@ -301,6 +301,10 @@ export default defineComponent({ width: 32%; box-sizing: border-box; overflow: auto; + + > .title { + margin: 24px; + } } > .main { diff --git a/src/client/style.scss b/src/client/style.scss index 8e1d74bc76..1c8807573b 100644 --- a/src/client/style.scss +++ b/src/client/style.scss @@ -202,6 +202,20 @@ hr { } } +._buttonGradate { + @extend ._buttonPrimary; + color: var(--fgOnAccent); + background: linear-gradient(90deg, var(--buttonGradateA), var(--buttonGradateB)); + + &:not(:disabled):hover { + background: linear-gradient(90deg, var(--X8), var(--X8)); + } + + &:not(:disabled):active { + background: linear-gradient(90deg, var(--X8), var(--X8)); + } +} + ._help { color: var(--accent); cursor: help From 345a9d3525adcc36f828874a32cc54532f06454c Mon Sep 17 00:00:00 2001 From: Johann150 <johann.galle@protonmail.com> Date: Sat, 16 Oct 2021 10:10:19 +0200 Subject: [PATCH 093/107] remove unnecessary imports (#7871) --- src/client/pages/user/clips.vue | 7 ------- src/client/pages/user/follow-list.vue | 7 ------- src/client/pages/user/gallery.vue | 7 ------- src/client/pages/user/pages.vue | 7 ------- 4 files changed, 28 deletions(-) diff --git a/src/client/pages/user/clips.vue b/src/client/pages/user/clips.vue index fc40d583c6..53ee554383 100644 --- a/src/client/pages/user/clips.vue +++ b/src/client/pages/user/clips.vue @@ -12,7 +12,6 @@ <script lang="ts"> import { defineComponent } from 'vue'; import MkPagination from '@client/components/ui/pagination.vue'; -import { userPage, acct } from '@client/filters/user'; export default defineComponent({ components: { @@ -43,12 +42,6 @@ export default defineComponent({ this.$refs.list.reload(); } }, - - methods: { - userPage, - - acct - } }); </script> diff --git a/src/client/pages/user/follow-list.vue b/src/client/pages/user/follow-list.vue index f6df28309f..1f5ab5993c 100644 --- a/src/client/pages/user/follow-list.vue +++ b/src/client/pages/user/follow-list.vue @@ -12,7 +12,6 @@ import { defineComponent } from 'vue'; import MkUserInfo from '@client/components/user-info.vue'; import MkPagination from '@client/components/ui/pagination.vue'; -import { userPage, acct } from '@client/filters/user'; export default defineComponent({ components: { @@ -51,12 +50,6 @@ export default defineComponent({ user() { this.$refs.list.reload(); } - }, - - methods: { - userPage, - - acct } }); </script> diff --git a/src/client/pages/user/gallery.vue b/src/client/pages/user/gallery.vue index 67a5fac109..c21b3e6428 100644 --- a/src/client/pages/user/gallery.vue +++ b/src/client/pages/user/gallery.vue @@ -12,7 +12,6 @@ import { defineComponent } from 'vue'; import MkGalleryPostPreview from '@client/components/gallery-post-preview.vue'; import MkPagination from '@client/components/ui/pagination.vue'; -import { userPage, acct } from '@client/filters/user'; export default defineComponent({ components: { @@ -43,12 +42,6 @@ export default defineComponent({ user() { this.$refs.list.reload(); } - }, - - methods: { - userPage, - - acct } }); </script> diff --git a/src/client/pages/user/pages.vue b/src/client/pages/user/pages.vue index 819bd9f2ef..ece418cf62 100644 --- a/src/client/pages/user/pages.vue +++ b/src/client/pages/user/pages.vue @@ -10,7 +10,6 @@ import { defineComponent } from 'vue'; import MkPagePreview from '@client/components/page-preview.vue'; import MkPagination from '@client/components/ui/pagination.vue'; -import { userPage, acct } from '@client/filters/user'; export default defineComponent({ components: { @@ -41,12 +40,6 @@ export default defineComponent({ user() { this.$refs.list.reload(); } - }, - - methods: { - userPage, - - acct } }); </script> From 03b04acb1628e710dec1d768d74aedfc0a4f1ccd Mon Sep 17 00:00:00 2001 From: MeiMei <30769358+mei23@users.noreply.github.com> Date: Sat, 16 Oct 2021 17:12:20 +0900 Subject: [PATCH 094/107] =?UTF-8?q?=E3=83=86=E3=82=B9=E3=83=88=E7=94=A8?= =?UTF-8?q?=E3=82=B3=E3=83=B3=E3=83=86=E3=83=8A=E3=81=AE=E8=AA=BF=E6=95=B4?= =?UTF-8?q?=20(#7838)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Tune test container * docs * fix cp config * doc * a --- .github/workflows/nodejs.yml | 10 +++++----- CONTRIBUTING.md | 11 +++++++++++ test/docker-compose.yml | 15 +++++++++++++++ test/test.yml | 12 ++++++++++++ 4 files changed, 43 insertions(+), 5 deletions(-) create mode 100644 test/docker-compose.yml create mode 100644 test/test.yml diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index 9a32dac94e..a91572ad78 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -16,16 +16,16 @@ jobs: services: postgres: - image: postgres:10-alpine + image: postgres:12.2-alpine ports: - - 5432:5432 + - 54312:5432 env: POSTGRES_DB: test-misskey POSTGRES_HOST_AUTH_METHOD: trust redis: - image: redis:alpine + image: redis:4.0-alpine ports: - - 6379:6379 + - 56312:6379 steps: - uses: actions/checkout@v2 @@ -40,7 +40,7 @@ jobs: - name: Check yarn.lock run: git diff --exit-code yarn.lock - name: Copy Configure - run: cp .circleci/misskey/*.yml .config + run: cp test/test.yml .config - name: Build run: yarn build - name: Test diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 06154f1f44..f5e0eece1f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -57,6 +57,17 @@ If your language is not listed in Crowdin, please open an issue. - Test codes are located in [`/test`](/test). ### Run test +Create a config file. +``` +cp test/test.yml .config/ +``` +Prepare DB/Redis for testing. +``` +docker-compose -f test/docker-compose.yml up +``` +Alternatively, prepare an empty (data can be erased) DB and edit `.config/test.yml`. + +Run all test. ``` npm run test ``` diff --git a/test/docker-compose.yml b/test/docker-compose.yml new file mode 100644 index 0000000000..c045e7c6c4 --- /dev/null +++ b/test/docker-compose.yml @@ -0,0 +1,15 @@ +version: "3" + +services: + redistest: + image: redis:4.0-alpine + ports: + - "127.0.0.1:56312:6379" + + dbtest: + image: postgres:12.2-alpine + ports: + - "127.0.0.1:54312:5432" + environment: + POSTGRES_DB: "test-misskey" + POSTGRES_HOST_AUTH_METHOD: trust diff --git a/test/test.yml b/test/test.yml new file mode 100644 index 0000000000..2d3094653e --- /dev/null +++ b/test/test.yml @@ -0,0 +1,12 @@ +url: 'http://misskey.local' +port: 61812 +db: + host: localhost + port: 54312 + db: test-misskey + user: postgres + pass: '' +redis: + host: localhost + port: 56312 +id: aid From 482081c41b45ab3798e73c4d11e8a7c1c1f5e8c9 Mon Sep 17 00:00:00 2001 From: MeiMei <30769358+mei23@users.noreply.github.com> Date: Sat, 16 Oct 2021 17:16:24 +0900 Subject: [PATCH 095/107] Refactor request (#7814) * status code * Test ap-request.ts https://github.com/mei23/crytest/blob/4397fc5e70536e4175fe56e974ca83b8047bef3a/test/ap-request.ts * tune --- src/misc/download-url.ts | 21 +-- src/misc/fetch.ts | 67 ++++--- src/queue/processors/deliver.ts | 7 +- src/queue/processors/inbox.ts | 3 +- src/remote/activitypub/ap-request.ts | 104 +++++++++++ .../activitypub/kernel/announce/note.ts | 3 +- src/remote/activitypub/kernel/create/note.ts | 3 +- src/remote/activitypub/models/note.ts | 3 +- src/remote/activitypub/request.ts | 166 ++++-------------- src/server/file/send-drive-file.ts | 5 +- src/server/proxy/proxy-media.ts | 5 +- test/ap-request.ts | 55 ++++++ 12 files changed, 268 insertions(+), 174 deletions(-) create mode 100644 src/remote/activitypub/ap-request.ts create mode 100644 test/ap-request.ts diff --git a/src/misc/download-url.ts b/src/misc/download-url.ts index 8a8640a8cd..c96b4fd1d6 100644 --- a/src/misc/download-url.ts +++ b/src/misc/download-url.ts @@ -2,7 +2,7 @@ import * as fs from 'fs'; import * as stream from 'stream'; import * as util from 'util'; import got, * as Got from 'got'; -import { httpAgent, httpsAgent } from './fetch'; +import { httpAgent, httpsAgent, StatusError } from './fetch'; import config from '@/config/index'; import * as chalk from 'chalk'; import Logger from '@/services/logger'; @@ -37,6 +37,7 @@ export async function downloadUrl(url: string, path: string) { http: httpAgent, https: httpsAgent, }, + http2: false, // default retry: 0, }).on('response', (res: Got.Response) => { if ((process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'test') && !config.proxy && res.ip) { @@ -59,17 +60,17 @@ export async function downloadUrl(url: string, path: string) { logger.warn(`maxSize exceeded (${progress.transferred} > ${maxSize}) on downloadProgress`); req.destroy(); } - }).on('error', (e: any) => { - if (e.name === 'HTTPError') { - const statusCode = e.response?.statusCode; - const statusMessage = e.response?.statusMessage; - e.name = `StatusError`; - e.statusCode = statusCode; - e.message = `${statusCode} ${statusMessage}`; - } }); - await pipeline(req, fs.createWriteStream(path)); + try { + await pipeline(req, fs.createWriteStream(path)); + } catch (e) { + if (e instanceof Got.HTTPError) { + throw new StatusError(`${e.response.statusCode} ${e.response.statusMessage}`, e.response.statusCode, e.response.statusMessage); + } else { + throw e; + } + } logger.succ(`Download finished: ${chalk.cyan(url)}`); } diff --git a/src/misc/fetch.ts b/src/misc/fetch.ts index 82db2f2f8c..f4f16a27e2 100644 --- a/src/misc/fetch.ts +++ b/src/misc/fetch.ts @@ -1,51 +1,62 @@ import * as http from 'http'; import * as https from 'https'; import CacheableLookup from 'cacheable-lookup'; -import fetch, { HeadersInit } from 'node-fetch'; +import fetch from 'node-fetch'; import { HttpProxyAgent, HttpsProxyAgent } from 'hpagent'; import config from '@/config/index'; import { URL } from 'url'; -export async function getJson(url: string, accept = 'application/json, */*', timeout = 10000, headers?: HeadersInit) { - const res = await fetch(url, { +export async function getJson(url: string, accept = 'application/json, */*', timeout = 10000, headers?: Record<string, string>) { + const res = await getResponse({ + url, + method: 'GET', headers: Object.assign({ 'User-Agent': config.userAgent, Accept: accept }, headers || {}), - timeout, - agent: getAgentByUrl, + timeout }); - if (!res.ok) { - throw { - name: `StatusError`, - statusCode: res.status, - message: `${res.status} ${res.statusText}`, - }; - } - return await res.json(); } -export async function getHtml(url: string, accept = 'text/html, */*', timeout = 10000, headers?: HeadersInit) { - const res = await fetch(url, { +export async function getHtml(url: string, accept = 'text/html, */*', timeout = 10000, headers?: Record<string, string>) { + const res = await getResponse({ + url, + method: 'GET', headers: Object.assign({ 'User-Agent': config.userAgent, Accept: accept }, headers || {}), + timeout + }); + + return await res.text(); +} + +export async function getResponse(args: { url: string, method: string, body?: string, headers: Record<string, string>, timeout?: number, size?: number }) { + const timeout = args?.timeout || 10 * 1000; + + const controller = new AbortController(); + setTimeout(() => { + controller.abort(); + }, timeout * 6); + + const res = await fetch(args.url, { + method: args.method, + headers: args.headers, + body: args.body, timeout, + size: args?.size || 10 * 1024 * 1024, agent: getAgentByUrl, + signal: controller.signal, }); if (!res.ok) { - throw { - name: `StatusError`, - statusCode: res.status, - message: `${res.status} ${res.statusText}`, - }; + throw new StatusError(`${res.status} ${res.statusText}`, res.status, res.statusText); } - return await res.text(); + return res; } const cache = new CacheableLookup({ @@ -114,3 +125,17 @@ export function getAgentByUrl(url: URL, bypassProxy = false) { return url.protocol == 'http:' ? httpAgent : httpsAgent; } } + +export class StatusError extends Error { + public statusCode: number; + public statusMessage?: string; + public isClientError: boolean; + + constructor(message: string, statusCode: number, statusMessage?: string) { + super(message); + this.name = 'StatusError'; + this.statusCode = statusCode; + this.statusMessage = statusMessage; + this.isClientError = typeof this.statusCode === 'number' && this.statusCode >= 400 && this.statusCode < 500; + } +} diff --git a/src/queue/processors/deliver.ts b/src/queue/processors/deliver.ts index 373e57cbd5..3c61896a2f 100644 --- a/src/queue/processors/deliver.ts +++ b/src/queue/processors/deliver.ts @@ -11,6 +11,7 @@ import { toPuny } from '@/misc/convert-host'; import { Cache } from '@/misc/cache'; import { Instance } from '@/models/entities/instance'; import { DeliverJobData } from '../types'; +import { StatusError } from '@/misc/fetch'; const logger = new Logger('deliver'); @@ -68,16 +69,16 @@ export default async (job: Bull.Job<DeliverJobData>) => { registerOrFetchInstanceDoc(host).then(i => { Instances.update(i.id, { latestRequestSentAt: new Date(), - latestStatus: res != null && res.hasOwnProperty('statusCode') ? res.statusCode : null, + latestStatus: res instanceof StatusError ? res.statusCode : null, isNotResponding: true }); instanceChart.requestSent(i.host, false); }); - if (res != null && res.hasOwnProperty('statusCode')) { + if (res instanceof StatusError) { // 4xx - if (res.statusCode >= 400 && res.statusCode < 500) { + if (res.isClientError) { // HTTPステータスコード4xxはクライアントエラーであり、それはつまり // 何回再送しても成功することはないということなのでエラーにはしないでおく return `${res.statusCode} ${res.statusMessage}`; diff --git a/src/queue/processors/inbox.ts b/src/queue/processors/inbox.ts index e2c271cdf8..4032ce8653 100644 --- a/src/queue/processors/inbox.ts +++ b/src/queue/processors/inbox.ts @@ -14,6 +14,7 @@ import { InboxJobData } from '../types'; import DbResolver from '@/remote/activitypub/db-resolver'; import { resolvePerson } from '@/remote/activitypub/models/person'; import { LdSignature } from '@/remote/activitypub/misc/ld-signature'; +import { StatusError } from '@/misc/fetch'; const logger = new Logger('inbox'); @@ -53,7 +54,7 @@ export default async (job: Bull.Job<InboxJobData>): Promise<string> => { authUser = await dbResolver.getAuthUserFromApId(getApId(activity.actor)); } catch (e) { // 対象が4xxならスキップ - if (e.statusCode >= 400 && e.statusCode < 500) { + if (e instanceof StatusError && e.isClientError) { return `skip: Ignored deleted actors on both ends ${activity.actor} - ${e.statusCode}`; } throw `Error in actor ${activity.actor} - ${e.statusCode || e}`; diff --git a/src/remote/activitypub/ap-request.ts b/src/remote/activitypub/ap-request.ts new file mode 100644 index 0000000000..76a3857140 --- /dev/null +++ b/src/remote/activitypub/ap-request.ts @@ -0,0 +1,104 @@ +import * as crypto from 'crypto'; +import { URL } from 'url'; + +type Request = { + url: string; + method: string; + headers: Record<string, string>; +}; + +type PrivateKey = { + privateKeyPem: string; + keyId: string; +}; + +export function createSignedPost(args: { key: PrivateKey, url: string, body: string, additionalHeaders: Record<string, string> }) { + const u = new URL(args.url); + const digestHeader = `SHA-256=${crypto.createHash('sha256').update(args.body).digest('base64')}`; + + const request: Request = { + url: u.href, + method: 'POST', + headers: objectAssignWithLcKey({ + 'Date': new Date().toUTCString(), + 'Host': u.hostname, + 'Content-Type': 'application/activity+json', + 'Digest': digestHeader, + }, args.additionalHeaders), + }; + + const result = signToRequest(request, args.key, ['(request-target)', 'date', 'host', 'digest']); + + return { + request, + signingString: result.signingString, + signature: result.signature, + signatureHeader: result.signatureHeader, + }; +} + +export function createSignedGet(args: { key: PrivateKey, url: string, additionalHeaders: Record<string, string> }) { + const u = new URL(args.url); + + const request: Request = { + url: u.href, + method: 'GET', + headers: objectAssignWithLcKey({ + 'Accept': 'application/activity+json, application/ld+json', + 'Date': new Date().toUTCString(), + 'Host': new URL(args.url).hostname, + }, args.additionalHeaders), + }; + + const result = signToRequest(request, args.key, ['(request-target)', 'date', 'host', 'accept']); + + return { + request, + signingString: result.signingString, + signature: result.signature, + signatureHeader: result.signatureHeader, + }; +} + +function signToRequest(request: Request, key: PrivateKey, includeHeaders: string[]) { + const signingString = genSigningString(request, includeHeaders); + const signature = crypto.sign('sha256', Buffer.from(signingString), key.privateKeyPem).toString('base64'); + const signatureHeader = `keyId="${key.keyId}",algorithm="rsa-sha256",headers="${includeHeaders.join(' ')}",signature="${signature}"`; + + request.headers = objectAssignWithLcKey(request.headers, { + Signature: signatureHeader + }); + + return { + request, + signingString, + signature, + signatureHeader, + }; +} + +function genSigningString(request: Request, includeHeaders: string[]) { + request.headers = lcObjectKey(request.headers); + + const results: string[] = []; + + for (const key of includeHeaders.map(x => x.toLowerCase())) { + if (key === '(request-target)') { + results.push(`(request-target): ${request.method.toLowerCase()} ${new URL(request.url).pathname}`); + } else { + results.push(`${key}: ${request.headers[key]}`); + } + } + + return results.join('\n'); +} + +function lcObjectKey(src: Record<string, string>) { + const dst: Record<string, string> = {}; + for (const key of Object.keys(src).filter(x => x != '__proto__' && typeof src[x] === 'string')) dst[key.toLowerCase()] = src[key]; + return dst; +} + +function objectAssignWithLcKey(a: Record<string, string>, b: Record<string, string>) { + return Object.assign(lcObjectKey(a), lcObjectKey(b)); +} diff --git a/src/remote/activitypub/kernel/announce/note.ts b/src/remote/activitypub/kernel/announce/note.ts index b6ec090b99..5230867f24 100644 --- a/src/remote/activitypub/kernel/announce/note.ts +++ b/src/remote/activitypub/kernel/announce/note.ts @@ -8,6 +8,7 @@ import { extractDbHost } from '@/misc/convert-host'; import { fetchMeta } from '@/misc/fetch-meta'; import { getApLock } from '@/misc/app-lock'; import { parseAudience } from '../../audience'; +import { StatusError } from '@/misc/fetch'; const logger = apLogger; @@ -41,7 +42,7 @@ export default async function(resolver: Resolver, actor: IRemoteUser, activity: renote = await resolveNote(targetUri); } catch (e) { // 対象が4xxならスキップ - if (e.statusCode >= 400 && e.statusCode < 500) { + if (e instanceof StatusError && e.isClientError) { logger.warn(`Ignored announce target ${targetUri} - ${e.statusCode}`); return; } diff --git a/src/remote/activitypub/kernel/create/note.ts b/src/remote/activitypub/kernel/create/note.ts index 5dda85d0f5..14e311e4cd 100644 --- a/src/remote/activitypub/kernel/create/note.ts +++ b/src/remote/activitypub/kernel/create/note.ts @@ -4,6 +4,7 @@ import { createNote, fetchNote } from '../../models/note'; import { getApId, IObject, ICreate } from '../../type'; import { getApLock } from '@/misc/app-lock'; import { extractDbHost } from '@/misc/convert-host'; +import { StatusError } from '@/misc/fetch'; /** * 投稿作成アクティビティを捌きます @@ -32,7 +33,7 @@ export default async function(resolver: Resolver, actor: IRemoteUser, note: IObj await createNote(note, resolver, silent); return 'ok'; } catch (e) { - if (e.statusCode >= 400 && e.statusCode < 500) { + if (e instanceof StatusError && e.isClientError) { return `skip ${e.statusCode}`; } else { throw e; diff --git a/src/remote/activitypub/models/note.ts b/src/remote/activitypub/models/note.ts index 25004cb4d2..cf68f3005d 100644 --- a/src/remote/activitypub/models/note.ts +++ b/src/remote/activitypub/models/note.ts @@ -26,6 +26,7 @@ import { createMessage } from '@/services/messages/create'; import { parseAudience } from '../audience'; import { extractApMentions } from './mention'; import DbResolver from '../db-resolver'; +import { StatusError } from '@/misc/fetch'; const logger = apLogger; @@ -177,7 +178,7 @@ export async function createNote(value: string | IObject, resolver?: Resolver, s } } catch (e) { return { - status: e.statusCode >= 400 && e.statusCode < 500 ? 'permerror' : 'temperror' + status: (e instanceof StatusError && e.isClientError) ? 'permerror' : 'temperror' }; } }; diff --git a/src/remote/activitypub/request.ts b/src/remote/activitypub/request.ts index fe1009243c..d6ced630c1 100644 --- a/src/remote/activitypub/request.ts +++ b/src/remote/activitypub/request.ts @@ -1,66 +1,31 @@ -import * as http from 'http'; -import * as https from 'https'; -import { sign } from 'http-signature'; -import * as crypto from 'crypto'; - import config from '@/config/index'; -import { User } from '@/models/entities/user'; -import { getAgentByUrl } from '@/misc/fetch'; -import { URL } from 'url'; -import got from 'got'; -import * as Got from 'got'; import { getUserKeypair } from '@/misc/keypair-store'; +import { User } from '@/models/entities/user'; +import { getResponse } from '../../misc/fetch'; +import { createSignedPost, createSignedGet } from './ap-request'; export default async (user: { id: User['id'] }, url: string, object: any) => { - const timeout = 10 * 1000; - - const { protocol, hostname, port, pathname, search } = new URL(url); - - const data = JSON.stringify(object); - - const sha256 = crypto.createHash('sha256'); - sha256.update(data); - const hash = sha256.digest('base64'); + const body = JSON.stringify(object); const keypair = await getUserKeypair(user.id); - await new Promise<void>((resolve, reject) => { - const req = https.request({ - agent: getAgentByUrl(new URL(`https://example.net`)), - protocol, - hostname, - port, - method: 'POST', - path: pathname + search, - timeout, - headers: { - 'User-Agent': config.userAgent, - 'Content-Type': 'application/activity+json', - 'Digest': `SHA-256=${hash}` - } - }, res => { - if (res.statusCode! >= 400) { - reject(res); - } else { - resolve(); - } - }); + const req = createSignedPost({ + key: { + privateKeyPem: keypair.privateKey, + keyId: `${config.url}/users/${user.id}#main-key` + }, + url, + body, + additionalHeaders: { + 'User-Agent': config.userAgent, + } + }); - sign(req, { - authorizationHeaderName: 'Signature', - key: keypair.privateKey, - keyId: `${config.url}/users/${user.id}#main-key`, - headers: ['(request-target)', 'date', 'host', 'digest'] - }); - - req.on('timeout', () => req.abort()); - - req.on('error', e => { - if (req.aborted) reject('timeout'); - reject(e); - }); - - req.end(data); + await getResponse({ + url, + method: req.request.method, + headers: req.request.headers, + body, }); }; @@ -70,87 +35,24 @@ export default async (user: { id: User['id'] }, url: string, object: any) => { * @param url URL to fetch */ export async function signedGet(url: string, user: { id: User['id'] }) { - const timeout = 10 * 1000; - const keypair = await getUserKeypair(user.id); - const req = got.get<any>(url, { - headers: { - 'Accept': 'application/activity+json, application/ld+json', + const req = createSignedGet({ + key: { + privateKeyPem: keypair.privateKey, + keyId: `${config.url}/users/${user.id}#main-key` + }, + url, + additionalHeaders: { 'User-Agent': config.userAgent, - }, - responseType: 'json', - timeout, - hooks: { - beforeRequest: [ - options => { - options.request = (url: URL, opt: http.RequestOptions, callback?: (response: any) => void) => { - // Select custom agent by URL - opt.agent = getAgentByUrl(url, false); - - // Wrap original https?.request - const requestFunc = url.protocol === 'http:' ? http.request : https.request; - const clientRequest = requestFunc(url, opt, callback) as http.ClientRequest; - - // HTTP-Signature - sign(clientRequest, { - authorizationHeaderName: 'Signature', - key: keypair.privateKey, - keyId: `${config.url}/users/${user.id}#main-key`, - headers: ['(request-target)', 'host', 'date', 'accept'] - }); - - return clientRequest; - }; - }, - ], - }, - retry: 0, + } }); - const res = await receiveResponce(req, 10 * 1024 * 1024); + const res = await getResponse({ + url, + method: req.request.method, + headers: req.request.headers + }); - return res.body; -} - -/** - * Receive response (with size limit) - * @param req Request - * @param maxSize size limit - */ -export async function receiveResponce<T>(req: Got.CancelableRequest<Got.Response<T>>, maxSize: number) { - // 応答ヘッダでサイズチェック - req.on('response', (res: Got.Response) => { - const contentLength = res.headers['content-length']; - if (contentLength != null) { - const size = Number(contentLength); - if (size > maxSize) { - req.cancel(); - } - } - }); - - // 受信中のデータでサイズチェック - req.on('downloadProgress', (progress: Got.Progress) => { - if (progress.transferred > maxSize) { - req.cancel(); - } - }); - - // 応答取得 with ステータスコードエラーの整形 - const res = await req.catch(e => { - if (e.name === 'HTTPError') { - const statusCode = (e as Got.HTTPError).response.statusCode; - const statusMessage = (e as Got.HTTPError).response.statusMessage; - throw { - name: `StatusError`, - statusCode, - message: `${statusCode} ${statusMessage}`, - }; - } else { - throw e; - } - }); - - return res; + return await res.json(); } diff --git a/src/server/file/send-drive-file.ts b/src/server/file/send-drive-file.ts index a73164ed21..1908c969a5 100644 --- a/src/server/file/send-drive-file.ts +++ b/src/server/file/send-drive-file.ts @@ -13,6 +13,7 @@ import { downloadUrl } from '@/misc/download-url'; import { detectType } from '@/misc/get-file-info'; import { convertToJpeg, convertToPngOrJpeg } from '@/services/drive/image-processor'; import { GenerateVideoThumbnail } from '@/services/drive/generate-video-thumbnail'; +import { StatusError } from '@/misc/fetch'; //const _filename = fileURLToPath(import.meta.url); const _filename = __filename; @@ -83,9 +84,9 @@ export default async function(ctx: Koa.Context) { ctx.set('Content-Type', image.type); ctx.set('Cache-Control', 'max-age=31536000, immutable'); } catch (e) { - serverLogger.error(e.statusCode); + serverLogger.error(`${e}`); - if (typeof e.statusCode === 'number' && e.statusCode >= 400 && e.statusCode < 500) { + if (e instanceof StatusError && e.isClientError) { ctx.status = e.statusCode; ctx.set('Cache-Control', 'max-age=86400'); } else { diff --git a/src/server/proxy/proxy-media.ts b/src/server/proxy/proxy-media.ts index 3bd65dfe67..9e13c0877f 100644 --- a/src/server/proxy/proxy-media.ts +++ b/src/server/proxy/proxy-media.ts @@ -5,6 +5,7 @@ import { IImage, convertToPng, convertToJpeg } from '@/services/drive/image-proc import { createTemp } from '@/misc/create-temp'; import { downloadUrl } from '@/misc/download-url'; import { detectType } from '@/misc/get-file-info'; +import { StatusError } from '@/misc/fetch'; export async function proxyMedia(ctx: Koa.Context) { const url = 'url' in ctx.query ? ctx.query.url : 'https://' + ctx.params.url; @@ -37,9 +38,9 @@ export async function proxyMedia(ctx: Koa.Context) { ctx.set('Cache-Control', 'max-age=31536000, immutable'); ctx.body = image.data; } catch (e) { - serverLogger.error(e); + serverLogger.error(`${e}`); - if (typeof e.statusCode === 'number' && e.statusCode >= 400 && e.statusCode < 500) { + if (e instanceof StatusError && e.isClientError) { ctx.status = e.statusCode; } else { ctx.status = 500; diff --git a/test/ap-request.ts b/test/ap-request.ts new file mode 100644 index 0000000000..4a9799eb99 --- /dev/null +++ b/test/ap-request.ts @@ -0,0 +1,55 @@ +import * as assert from 'assert'; +import { genRsaKeyPair } from '../src/misc/gen-key-pair'; +import { createSignedPost, createSignedGet } from '../src/remote/activitypub/ap-request'; +const httpSignature = require('http-signature'); + +export const buildParsedSignature = (signingString: string, signature: string, algorithm: string) => { + return { + scheme: 'Signature', + params: { + keyId: 'KeyID', // dummy, not used for verify + algorithm: algorithm, + headers: [ '(request-target)', 'date', 'host', 'digest' ], // dummy, not used for verify + signature: signature, + }, + signingString: signingString, + algorithm: algorithm?.toUpperCase(), + keyId: 'KeyID', // dummy, not used for verify + }; +}; + +describe('ap-request', () => { + it('createSignedPost with verify', async () => { + const keypair = await genRsaKeyPair(); + const key = { keyId: 'x', 'privateKeyPem': keypair.privateKey }; + const url = 'https://example.com/inbox'; + const activity = { a: 1 }; + const body = JSON.stringify(activity); + const headers = { + 'User-Agent': 'UA' + }; + + const req = createSignedPost({ key, url, body, additionalHeaders: headers }); + + const parsed = buildParsedSignature(req.signingString, req.signature, 'rsa-sha256'); + + const result = httpSignature.verifySignature(parsed, keypair.publicKey); + assert.deepStrictEqual(result, true); + }); + + it('createSignedGet with verify', async () => { + const keypair = await genRsaKeyPair(); + const key = { keyId: 'x', 'privateKeyPem': keypair.privateKey }; + const url = 'https://example.com/outbox'; + const headers = { + 'User-Agent': 'UA' + }; + + const req = createSignedGet({ key, url, additionalHeaders: headers }); + + const parsed = buildParsedSignature(req.signingString, req.signature, 'rsa-sha256'); + + const result = httpSignature.verifySignature(parsed, keypair.publicKey); + assert.deepStrictEqual(result, true); + }); +}); From d184f73160765b8bcefbd934dc2448c644fc2fa7 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 16 Oct 2021 17:42:17 +0900 Subject: [PATCH 096/107] feat(api): add users/groups/leave Resolve #7775 --- CHANGELOG.md | 1 + .../api/endpoints/users/groups/leave.ts | 50 +++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 src/server/api/endpoints/users/groups/leave.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 128c212c0b..dbe98f4ae5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ - クライアント: 新しいダークテーマを追加 - クライアント: テーマコンパイラに hue と saturate 関数を追加 - ActivityPub: HTML -> MFMの変換を強化 +- API: グループから抜ける users/groups/leave エンドポイントを実装 - API: i/notifications に unreadOnly オプションを追加 - API: ap系のエンドポイントをログイン必須化+レートリミット追加 - MFM: Add tag syntaxes of bold <b></b> and strikethrough <s></s> diff --git a/src/server/api/endpoints/users/groups/leave.ts b/src/server/api/endpoints/users/groups/leave.ts new file mode 100644 index 0000000000..0e52f2abdf --- /dev/null +++ b/src/server/api/endpoints/users/groups/leave.ts @@ -0,0 +1,50 @@ +import $ from 'cafy'; +import { ID } from '@/misc/cafy-id'; +import define from '../../../define'; +import { ApiError } from '../../../error'; +import { UserGroups, UserGroupJoinings } from '@/models/index'; + +export const meta = { + tags: ['groups', 'users'], + + requireCredential: true as const, + + kind: 'write:user-groups', + + params: { + groupId: { + validator: $.type(ID), + }, + }, + + errors: { + noSuchGroup: { + message: 'No such group.', + code: 'NO_SUCH_GROUP', + id: '62780270-1f67-5dc0-daca-3eb510612e31' + }, + + youAreOwner: { + message: 'Your are the owner.', + code: 'YOU_ARE_OWNER', + id: 'b6d6e0c2-ef8a-9bb8-653d-79f4a3107c69' + }, + } +}; + +export default define(meta, async (ps, me) => { + // Fetch the group + const userGroup = await UserGroups.findOne({ + id: ps.groupId, + }); + + if (userGroup == null) { + throw new ApiError(meta.errors.noSuchGroup); + } + + if (me.id === userGroup.userId) { + throw new ApiError(meta.errors.youAreOwner); + } + + await UserGroupJoinings.delete({ userGroupId: userGroup.id, userId: me.id }); +}); From 4ba4062519df5121fee8cdb3ddb262d2c99b0dce Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 16 Oct 2021 18:18:41 +0900 Subject: [PATCH 097/107] :art: --- src/client/components/tab.vue | 13 +++++-- src/client/pages/pages.vue | 48 ++++++++++++------------ src/client/pages/user/index.timeline.vue | 4 +- 3 files changed, 37 insertions(+), 28 deletions(-) diff --git a/src/client/components/tab.vue b/src/client/components/tab.vue index 58538c2ca1..ce86af8f95 100644 --- a/src/client/components/tab.vue +++ b/src/client/components/tab.vue @@ -35,8 +35,8 @@ export default defineComponent({ > button { flex: 1; - padding: 15px 12px 12px 12px; - border-bottom: solid 3px transparent; + padding: 10px 8px; + border-radius: 6px; &:disabled { opacity: 1 !important; @@ -45,11 +45,16 @@ export default defineComponent({ &.active { color: var(--accent); - border-bottom-color: var(--accent); + background: var(--accentedBg); } &:not(.active):hover { color: var(--fgHighlighted); + background: var(--panelHighlight); + } + + &:not(:first-child) { + margin-left: 8px; } > .icon { @@ -61,7 +66,7 @@ export default defineComponent({ font-size: 80%; > button { - padding: 11px 8px 8px 8px; + padding: 11px 8px; } } } diff --git a/src/client/pages/pages.vue b/src/client/pages/pages.vue index 988a759b91..8300e8a6e4 100644 --- a/src/client/pages/pages.vue +++ b/src/client/pages/pages.vue @@ -2,33 +2,35 @@ <div> <MkHeader :info="header"/> - <!-- TODO: MkHeaderに統合 --> - <MkTab v-model="tab" v-if="$i"> - <option value="featured"><i class="fas fa-fire-alt"></i> {{ $ts._pages.featured }}</option> - <option value="my"><i class="fas fa-edit"></i> {{ $ts._pages.my }}</option> - <option value="liked"><i class="fas fa-heart"></i> {{ $ts._pages.liked }}</option> - </MkTab> + <MkSpacer> + <!-- TODO: MkHeaderに統合 --> + <MkTab v-model="tab" v-if="$i"> + <option value="featured"><i class="fas fa-fire-alt"></i> {{ $ts._pages.featured }}</option> + <option value="my"><i class="fas fa-edit"></i> {{ $ts._pages.my }}</option> + <option value="liked"><i class="fas fa-heart"></i> {{ $ts._pages.liked }}</option> + </MkTab> - <div class="_section"> - <div class="rknalgpo _content" v-if="tab === 'featured'"> - <MkPagination :pagination="featuredPagesPagination" #default="{items}"> - <MkPagePreview v-for="page in items" class="ckltabjg" :page="page" :key="page.id"/> - </MkPagination> - </div> + <div class="_section"> + <div class="rknalgpo _content" v-if="tab === 'featured'"> + <MkPagination :pagination="featuredPagesPagination" #default="{items}"> + <MkPagePreview v-for="page in items" class="ckltabjg" :page="page" :key="page.id"/> + </MkPagination> + </div> - <div class="rknalgpo _content my" v-if="tab === 'my'"> - <MkButton class="new" @click="create()"><i class="fas fa-plus"></i></MkButton> - <MkPagination :pagination="myPagesPagination" #default="{items}"> - <MkPagePreview v-for="page in items" class="ckltabjg" :page="page" :key="page.id"/> - </MkPagination> - </div> + <div class="rknalgpo _content my" v-if="tab === 'my'"> + <MkButton class="new" @click="create()"><i class="fas fa-plus"></i></MkButton> + <MkPagination :pagination="myPagesPagination" #default="{items}"> + <MkPagePreview v-for="page in items" class="ckltabjg" :page="page" :key="page.id"/> + </MkPagination> + </div> - <div class="rknalgpo _content" v-if="tab === 'liked'"> - <MkPagination :pagination="likedPagesPagination" #default="{items}"> - <MkPagePreview v-for="like in items" class="ckltabjg" :page="like.page" :key="like.page.id"/> - </MkPagination> + <div class="rknalgpo _content" v-if="tab === 'liked'"> + <MkPagination :pagination="likedPagesPagination" #default="{items}"> + <MkPagePreview v-for="like in items" class="ckltabjg" :page="like.page" :key="like.page.id"/> + </MkPagination> + </div> </div> - </div> + </MkSpacer> </div> </template> diff --git a/src/client/pages/user/index.timeline.vue b/src/client/pages/user/index.timeline.vue index 8796ded469..c3444f26f6 100644 --- a/src/client/pages/user/index.timeline.vue +++ b/src/client/pages/user/index.timeline.vue @@ -1,6 +1,6 @@ <template> <div class="yrzkoczt" v-sticky-container> - <MkTab v-model="with_" class="_gap tab"> + <MkTab v-model="with_" class="tab"> <option :value="null">{{ $ts.notes }}</option> <option value="replies">{{ $ts.notesAndReplies }}</option> <option value="files">{{ $ts.withFiles }}</option> @@ -60,6 +60,8 @@ export default defineComponent({ <style lang="scss" scoped> .yrzkoczt { > .tab { + margin: calc(var(--margin) / 2) 0; + padding: calc(var(--margin) / 2) 0; background: var(--bg); } } From c194eddb1b2b9a38f918588819b480a5e80d7b97 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 16 Oct 2021 19:18:46 +0900 Subject: [PATCH 098/107] chore: fix spacer component --- src/client/components/global/spacer.vue | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/client/components/global/spacer.vue b/src/client/components/global/spacer.vue index 9037de466c..1129d54c71 100644 --- a/src/client/components/global/spacer.vue +++ b/src/client/components/global/spacer.vue @@ -33,10 +33,16 @@ export default defineComponent({ onMounted(() => { ro = new ResizeObserver((entries) => { + /* iOSが対応していない adjust({ width: entries[0].borderBoxSize[0].inlineSize, height: entries[0].borderBoxSize[0].blockSize, }); + */ + adjust({ + width: root.value.offsetWidth, + height: root.value.offsetHeight, + }); }); ro.observe(root.value); From d672fccef49835c7616638da19506426c7a979ed Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 16 Oct 2021 19:20:49 +0900 Subject: [PATCH 099/107] refactor --- src/client/components/modal-page-window.vue | 2 +- src/client/components/page-window.vue | 2 +- src/client/components/ui/folder.vue | 2 +- src/client/components/ui/info.vue | 2 +- src/client/pages/user/index.vue | 2 +- src/client/style.scss | 2 +- src/client/ui/chat/side.vue | 2 +- src/client/ui/deck/main-column.vue | 2 +- src/client/ui/default.vue | 2 +- src/client/widgets/notifications.vue | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/client/components/modal-page-window.vue b/src/client/components/modal-page-window.vue index 3ca7c31e0e..cb81a974f5 100644 --- a/src/client/components/modal-page-window.vue +++ b/src/client/components/modal-page-window.vue @@ -10,7 +10,7 @@ </span> <button class="_button" @click="$refs.modal.close()"><i class="fas fa-times"></i></button> </div> - <div class="body _flat_"> + <div class="body _fitSide_"> <keep-alive> <component :is="component" v-bind="props" :ref="changePage"/> </keep-alive> diff --git a/src/client/components/page-window.vue b/src/client/components/page-window.vue index 904d13e0e8..7d15c75d62 100644 --- a/src/client/components/page-window.vue +++ b/src/client/components/page-window.vue @@ -16,7 +16,7 @@ <template #headerLeft> <button v-if="history.length > 0" class="_button" @click="back()" v-tooltip="$ts.goBack"><i class="fas fa-arrow-left"></i></button> </template> - <div class="yrolvcoq _flat_"> + <div class="yrolvcoq _fitSide_"> <component :is="component" v-bind="props" :ref="changePage"/> </div> </XWindow> diff --git a/src/client/components/ui/folder.vue b/src/client/components/ui/folder.vue index cc3da083c5..d0616a57c1 100644 --- a/src/client/components/ui/folder.vue +++ b/src/client/components/ui/folder.vue @@ -154,7 +154,7 @@ export default defineComponent({ } } -._flat_ .ssazuxis { +._fitSide_ .ssazuxis { > header { padding: 0 16px; } diff --git a/src/client/components/ui/info.vue b/src/client/components/ui/info.vue index 4dc6b58373..e16f2736f1 100644 --- a/src/client/components/ui/info.vue +++ b/src/client/components/ui/info.vue @@ -43,7 +43,7 @@ export default defineComponent({ } } -._flat_ .fpezltsf { +._fitSide_ .fpezltsf { border-radius: 0; } </style> diff --git a/src/client/pages/user/index.vue b/src/client/pages/user/index.vue index f34c42797a..0ddf73d572 100644 --- a/src/client/pages/user/index.vue +++ b/src/client/pages/user/index.vue @@ -827,7 +827,7 @@ export default defineComponent({ } } -._flat_ .ftskorzw.narrow { +._fitSide_ .ftskorzw.narrow { > .profile { > .warn { margin: 0; diff --git a/src/client/style.scss b/src/client/style.scss index 1c8807573b..d6bad5a24d 100644 --- a/src/client/style.scss +++ b/src/client/style.scss @@ -380,7 +380,7 @@ hr { } } -._flat_ { +._fitSide_ { --root-margin: 0px; --baseContentWidth: 100%; --panelBorder: none; diff --git a/src/client/ui/chat/side.vue b/src/client/ui/chat/side.vue index d920e5b77c..3fd0a0e77b 100644 --- a/src/client/ui/chat/side.vue +++ b/src/client/ui/chat/side.vue @@ -3,7 +3,7 @@ <header class="header" @contextmenu.prevent.stop="onContextmenu"> <MkHeader class="title" :info="pageInfo" :center="false"/> </header> - <component :is="component" v-bind="props" :ref="changePage" class="body _flat_"/> + <component :is="component" v-bind="props" :ref="changePage" class="body _fitSide_"/> </div> </template> diff --git a/src/client/ui/deck/main-column.vue b/src/client/ui/deck/main-column.vue index 2127444769..baf88a9721 100644 --- a/src/client/ui/deck/main-column.vue +++ b/src/client/ui/deck/main-column.vue @@ -7,7 +7,7 @@ </template> </template> - <router-view v-slot="{ Component }" class="_flat_"> + <router-view v-slot="{ Component }" class="_fitSide_"> <transition> <keep-alive :include="['timeline']"> <component :is="Component" :ref="changePage" @contextmenu.stop="onContextmenu"/> diff --git a/src/client/ui/default.vue b/src/client/ui/default.vue index 69668ff96d..3518b1a91a 100644 --- a/src/client/ui/default.vue +++ b/src/client/ui/default.vue @@ -13,7 +13,7 @@ </template> <main class="main" @contextmenu.stop="onContextmenu" :style="{ background: pageInfo?.bg }"> - <div class="content" :class="{ _flat_: !fullView }"> + <div class="content" :class="{ _fitSide_: !fullView }"> <router-view v-slot="{ Component }"> <transition :name="$store.state.animation ? 'page' : ''" mode="out-in" @enter="onTransition"> <keep-alive :include="['timeline']"> diff --git a/src/client/widgets/notifications.vue b/src/client/widgets/notifications.vue index 01c76850d8..b0245eed6a 100644 --- a/src/client/widgets/notifications.vue +++ b/src/client/widgets/notifications.vue @@ -3,7 +3,7 @@ <template #header><i class="fas fa-bell"></i>{{ $ts.notifications }}</template> <template #func><button @click="configure()" class="_button"><i class="fas fa-cog"></i></button></template> - <div class="_flat_"> + <div class="_fitSide_"> <XNotifications :include-types="props.includingTypes"/> </div> </MkContainer> From 1e8132e610a5cc2f28060de1a4e6f72eca97736f Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 16 Oct 2021 19:25:40 +0900 Subject: [PATCH 100/107] :art: --- src/client/pages/messaging/messaging-room.message.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/pages/messaging/messaging-room.message.vue b/src/client/pages/messaging/messaging-room.message.vue index dfac83ad6a..a2740c0bdc 100644 --- a/src/client/pages/messaging/messaging-room.message.vue +++ b/src/client/pages/messaging/messaging-room.message.vue @@ -302,7 +302,7 @@ export default defineComponent({ > .text { &, ::v-deep(*) { - color: #fff !important; + color: var(--fgOnAccent) !important; } } } From 0d306e9d410d87148a58b6aad4302da18e0c1506 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 16 Oct 2021 19:27:06 +0900 Subject: [PATCH 101/107] New Crowdin updates (#7824) * New translations ja-JP.yml (German) * New translations ja-JP.yml (Esperanto) * New translations ja-JP.yml (Esperanto) * New translations ja-JP.yml (Korean) * New translations ja-JP.yml (Chinese Simplified) * New translations ja-JP.yml (Chinese Simplified) * New translations create-plugin.md (Esperanto) * New translations keyboard-shortcut.md (Esperanto) * New translations faq.md (Esperanto) * New translations misskey.md (Esperanto) * New translations stream.md (Esperanto) * New translations favorite.md (Esperanto) * New translations faq.md (Esperanto) * New translations ja-JP.yml (Esperanto) * New translations keyboard-shortcut.md (Esperanto) * New translations ja-JP.yml (Esperanto) * New translations ja-JP.yml (Esperanto) * New translations ja-JP.yml (Esperanto) * New translations keyboard-shortcut.md (Esperanto) * New translations ja-JP.yml (Esperanto) * New translations note.md (Esperanto) * New translations ja-JP.yml (Esperanto) * New translations ja-JP.yml (French) * New translations ja-JP.yml (French) * New translations ja-JP.yml (Esperanto) * New translations ja-JP.yml (Esperanto) * New translations ja-JP.yml (Esperanto) * New translations ja-JP.yml (English) * New translations api.md (English) * New translations aiscript.md (English) * New translations create-plugin.md (English) * New translations stream.md (English) * New translations create-plugin.md (English) * New translations develop-bot.md (English) * New translations ja-JP.yml (Spanish) * New translations ja-JP.yml (Spanish) * New translations ja-JP.yml (Spanish) * New translations ja-JP.yml (Spanish) * New translations ja-JP.yml (Spanish) * New translations ja-JP.yml (Spanish) * New translations troubleshooting.md (Spanish) * New translations glossary.md (Esperanto) * New translations glossary.md (Esperanto) * New translations glossary.md (Esperanto) * New translations glossary.md (Esperanto) * New translations note.md (Esperanto) * New translations note.md (Esperanto) * New translations note.md (Esperanto) * New translations note.md (Esperanto) * New translations ja-JP.yml (German) * New translations ja-JP.yml (English) * New translations ja-JP.yml (Chinese Simplified) * New translations ja-JP.yml (German) * New translations ja-JP.yml (German) * New translations ja-JP.yml (English) * New translations ja-JP.yml (English) * New translations ja-JP.yml (English) * New translations ja-JP.yml (German) * New translations ja-JP.yml (English) * New translations ja-JP.yml (Esperanto) * New translations note.md (Esperanto) * New translations note.md (Esperanto) * New translations note.md (Esperanto) * New translations note.md (Esperanto) * New translations ja-JP.yml (Esperanto) * New translations note.md (Esperanto) * New translations ja-JP.yml (Esperanto) * New translations ja-JP.yml (Esperanto) * New translations note.md (Esperanto) * New translations note.md (Esperanto) * New translations note.md (Esperanto) * New translations ja-JP.yml (Russian) * New translations ja-JP.yml (Esperanto) * New translations ja-JP.yml (Esperanto) * New translations mfm.md (Esperanto) * New translations mfm.md (Esperanto) * New translations ja-JP.yml (Esperanto) * New translations ja-JP.yml (Esperanto) * New translations ja-JP.yml (Esperanto) * New translations ja-JP.yml (Esperanto) * New translations ja-JP.yml (Esperanto) * New translations ja-JP.yml (Esperanto) * New translations note.md (Esperanto) * New translations ja-JP.yml (Esperanto) * New translations note.md (Esperanto) * New translations note.md (Esperanto) * New translations note.md (Esperanto) * New translations ja-JP.yml (Russian) * New translations note.md (Esperanto) * New translations ja-JP.yml (Russian) * New translations note.md (Esperanto) * New translations note.md (Esperanto) * New translations ja-JP.yml (Arabic) * New translations ja-JP.yml (German) * New translations ja-JP.yml (English) * New translations ja-JP.yml (German) * New translations ja-JP.yml (Chinese Simplified) * New translations ja-JP.yml (German) * New translations ja-JP.yml (English) * New translations ja-JP.yml (Chinese Simplified) * New translations ja-JP.yml (French) * New translations ja-JP.yml (French) * New translations ja-JP.yml (French) * New translations ja-JP.yml (French) * New translations ja-JP.yml (Russian) * New translations ja-JP.yml (Korean) * New translations ja-JP.yml (English) * New translations ja-JP.yml (Indonesian) * New translations ja-JP.yml (Esperanto) * New translations ja-JP.yml (Russian) * New translations ja-JP.yml (German) * New translations ja-JP.yml (English) * New translations ja-JP.yml (German) * New translations ja-JP.yml (French) * New translations ja-JP.yml (French) * New translations glossary.md (French) * New translations troubleshooting.md (French) * New translations troubleshooting.md (French) * New translations troubleshooting.md (French) * New translations troubleshooting.md (French) * New translations troubleshooting.md (French) * New translations troubleshooting.md (French) * New translations troubleshooting.md (French) * New translations ja-JP.yml (French) * New translations troubleshooting.md (French) * New translations troubleshooting.md (English) * New translations ja-JP.yml (Italian) * New translations note.md (Esperanto) * New translations glossary.md (French) * New translations apps.md (French) * New translations ja-JP.yml (French) * New translations note.md (Esperanto) * New translations glossary.md (French) * New translations glossary.md (French) * New translations glossary.md (French) * New translations glossary.md (French) * New translations glossary.md (French) * New translations glossary.md (French) * New translations glossary.md (French) * New translations glossary.md (French) * New translations glossary.md (French) * New translations glossary.md (French) * New translations glossary.md (French) * New translations glossary.md (French) * New translations glossary.md (French) * New translations glossary.md (French) * New translations ja-JP.yml (French) * New translations glossary.md (French) * New translations glossary.md (French) * New translations glossary.md (English) * New translations glossary.md (French) * New translations glossary.md (French) * New translations glossary.md (French) * New translations glossary.md (French) * New translations ja-JP.yml (French) * New translations glossary.md (French) * New translations ja-JP.yml (Esperanto) * New translations ja-JP.yml (Esperanto) * New translations ja-JP.yml (Esperanto) * New translations glossary.md (Esperanto) * New translations api.md (English) * New translations create-plugin.md (English) * New translations ja-JP.yml (German) * New translations ja-JP.yml (English) * New translations ja-JP.yml (English) * New translations ja-JP.yml (German) * New translations ja-JP.yml (German) * New translations ja-JP.yml (English) * New translations ja-JP.yml (Chinese Simplified) * New translations stream.md (Chinese Simplified) * New translations follow.md (Chinese Simplified) * New translations mfm.md (Chinese Simplified) * New translations mfm.md (Chinese Simplified) --- locales/ar-SA.yml | 1 + locales/de-DE.yml | 28 +++++-- locales/en-US.yml | 18 ++++- locales/eo-UY.yml | 84 +++++++++++--------- locales/es-ES.yml | 71 +++++++++++++++++ locales/fr-FR.yml | 22 ++++- locales/id-ID.yml | 1 + locales/it-IT.yml | 2 +- locales/ko-KR.yml | 3 + locales/ru-RU.yml | 11 +++ locales/zh-CN.yml | 16 ++++ src/docs/en-US/advanced/aiscript.md | 2 +- src/docs/en-US/advanced/api.md | 22 ++--- src/docs/en-US/advanced/create-plugin.md | 12 +-- src/docs/en-US/advanced/develop-bot.md | 2 +- src/docs/en-US/advanced/stream.md | 2 +- src/docs/en-US/general/glossary.md | 2 +- src/docs/en-US/general/troubleshooting.md | 2 +- src/docs/eo-UY/admin/faq.md | 2 +- src/docs/eo-UY/advanced/create-plugin.md | 2 +- src/docs/eo-UY/advanced/stream.md | 2 +- src/docs/eo-UY/features/favorite.md | 2 +- src/docs/eo-UY/features/keyboard-shortcut.md | 12 +-- src/docs/eo-UY/features/mfm.md | 6 +- src/docs/eo-UY/features/note.md | 20 ++--- src/docs/eo-UY/general/faq.md | 2 +- src/docs/eo-UY/general/glossary.md | 10 +-- src/docs/eo-UY/general/misskey.md | 2 +- src/docs/es-ES/general/troubleshooting.md | 2 +- src/docs/fr-FR/general/apps.md | 4 +- src/docs/fr-FR/general/glossary.md | 66 +++++++-------- src/docs/fr-FR/general/troubleshooting.md | 14 ++-- src/docs/zh-CN/advanced/stream.md | 18 ++--- src/docs/zh-CN/features/follow.md | 4 +- src/docs/zh-CN/features/mfm.md | 16 ++-- 35 files changed, 320 insertions(+), 165 deletions(-) diff --git a/locales/ar-SA.yml b/locales/ar-SA.yml index 3de8ac764e..3685eda5f8 100644 --- a/locales/ar-SA.yml +++ b/locales/ar-SA.yml @@ -6,6 +6,7 @@ search: "البحث" notifications: "الإشعارات" username: "اسم المستخدم" password: "الكلمة السرية" +forgotPassword: "نسيتَ كلمة السر" fetchingAsApObject: "جارٍ جلبه مِن الفديفرس…" ok: " حسناً" gotIt: "فهِمت" diff --git a/locales/de-DE.yml b/locales/de-DE.yml index 8f7a4c24e9..cebb95109c 100644 --- a/locales/de-DE.yml +++ b/locales/de-DE.yml @@ -81,6 +81,8 @@ somethingHappened: "Ein Fehler ist aufgetreten" retry: "Wiederholen" pageLoadError: "Laden der Seite fehlgeschlagen." pageLoadErrorDescription: "Dieser Fehler wird meist durch Netzwerkfehler oder den Browser-Cache verursacht. Bitte leere den Cache oder versuche es nach einiger Zeit erneut." +serverIsDead: "Dieser Server antwortet nicht. Bitte warte einen Moment und versuche es dann erneut." +youShouldUpgradeClient: "Bitte aktualisiere diese Seite, um eine neuere Version deines Clients zu verwenden." enterListName: "Name der Liste eingeben" privacy: "Privatsphäre" makeFollowManuallyApprove: "Follow-Anfragen benötigen Bestätigung" @@ -545,7 +547,7 @@ invisibleNote: "Private Notiz" enableInfiniteScroll: "Automatisch mehr Notizen laden" visibility: "Sichtbarkeit" poll: "Umfrage" -useCw: "Inhalt verbergen" +useCw: "Inhaltswarnung verwenden" enablePlayer: "Video-Player öffnen" disablePlayer: "Video-Player schließen" expandTweet: "Tweet ausklappen" @@ -764,6 +766,7 @@ middle: "Mittel" low: "Niedrig" emailNotConfiguredWarning: "Keine Email-Adresse hinterlegt" ratio: "Verhältnis" +previewNoteText: "Vorschau anzeigen" customCss: "Benutzerdefiniertes CSS" customCssWarn: "Verwende diese Einstellung nur, wenn du weißt, was sie tut. Ungültige Eingaben können dazu führen, dass der Client nicht mehr normal funktioniert." global: "Global" @@ -782,11 +785,22 @@ translatedFrom: "Aus {x} übersetzt" accountDeletionInProgress: "Löschung des Benutzerkontos momentan in Bearbeitung" usernameInfo: "Ein Name, durch den dein Benutzerkonto auf diesem Server identifiziert werden kann. Du kannst das Alphabet (a~z, A~Z), Ziffern (0~9) oder Unterstriche (_) verwenden. Benutzernamen können später nicht geändert werden." aiChanMode: "Ai Modus" -keepCw: "Inhaltswarnung beibehalten" +keepCw: "Inhaltswarnungen beibehalten" pubSub: "Pub/Sub Benutzerkonten" lastCommunication: "Letzte Kommunikation" resolved: "Gelöst" unresolved: "Ungelöst" +itsOn: "Eingeschaltet" +itsOff: "Ausgeschaltet" +emailRequiredForSignup: "Angaben einer Email-Adresse als benötigt markieren" +unread: "Ungelesen" +filter: "Filter" +controllPanel: "Systemsteuerung" +manageAccounts: "Benutzerkonten verwalten" +_signup: + almostThere: "Fast geschafft" + emailAddressInfo: "Bitte gib deine Email-Adresse ein." + emailSent: "An deine Email-Adresse ({email}) wurde soeben eine Bestätigungsmail geschickt. Bitte klicke auf den enthaltenen Link, um die Erstellung deines Benutzerkontos abzuschließen." _accountDelete: accountDelete: "Benutzerkonto löschen" mayTakeTime: "Da die Löschung eines Benutzerkontos ein aufwendiger Prozess ist, kann dessen Dauer davon abhängen, wie viel Inhalt in diesem erstellt wurde oder wie viele Dateien hochgeladen wurden." @@ -901,6 +915,8 @@ _mfm: fontDescription: "Setzt die Schriftart des Inhaltes fest." rainbow: "Regenbogen" rainbowDescription: "Lässt den Inhalt in Regenbogenfarben erscheinen." + sparkle: "Glitzer" + sparkleDescription: "Verleiht Inhalt einen glitzernden Partikeleffekt." _reversi: reversi: "Reversi" gameSettings: "Spieleinstellungen" @@ -1024,9 +1040,9 @@ _theme: infoFg: "Text von Informationen" infoWarnBg: "Hintergrund von Warnungen" infoWarnFg: "Text von Warnungen" - cwBg: "Hintergrund von verborgenen Inhalten" - cwFg: "Text von verborgenen Inhalten" - cwHoverBg: "Hintergrund von verborgenen Inhalten (Mouseover)" + cwBg: "Hintergrund des Inhaltswarnungsknopfs" + cwFg: "Text des Inhaltswarnungsknopfs" + cwHoverBg: "Hintergrund des Inhaltswarnungsknopfs (Mouseover)" toastBg: "Hintergrund von Benachrichtigungen" toastFg: "Text von Benachrichtigungen" buttonBg: "Hintergrund von Schaltflächen" @@ -1173,7 +1189,7 @@ _widgets: aiscript: "AiScript-Konsole" aichan: "Ai" _cw: - hide: "Verbergen" + hide: "Inhalt verbergen" show: "Inhalt anzeigen" chars: "{count} Zeichen" files: "{count} Datei(en)" diff --git a/locales/en-US.yml b/locales/en-US.yml index 4e843ce06d..aeb0aa1eac 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -1,7 +1,7 @@ --- _lang_: "English" headlineMisskey: "A network connected by notes" -introMisskey: "Welcome! Misskey is an open source, decentralized microblogging service.\nCreate \"notes\" to share what your thoughts with everyone around you. 📡\nWith \"reactions\", you can also quickly express your feelings about everyone's notes. 👍\nLet's explore a new world! 🚀" +introMisskey: "Welcome! Misskey is an open source, decentralized microblogging service.\nCreate \"notes\" to share your thoughts with everyone around you. 📡\nWith \"reactions\", you can also quickly express your feelings about everyone's notes. 👍\nLet's explore a new world! 🚀" monthAndDay: "{month}/{day}" search: "Search" notifications: "Notifications" @@ -81,6 +81,8 @@ somethingHappened: "An error occurred" retry: "Retry" pageLoadError: "Failed to load page." pageLoadErrorDescription: "This is normally caused by network errors or the browser's cache. Try clearing the cache and then try again after waiting a little while." +serverIsDead: "This server is not responding. Please wait for a while and try again." +youShouldUpgradeClient: "To view this page, please refresh to update your client." enterListName: "Enter a list name" privacy: "Privacy" makeFollowManuallyApprove: "Follow requests require approval" @@ -764,6 +766,7 @@ middle: "Medium" low: "Low" emailNotConfiguredWarning: "Email address not set." ratio: "Ratio" +previewNoteText: "Show preview" customCss: "Custom CSS" customCssWarn: "This setting should only be used if you know what it does. Entering improper values may cause the client to stop functioning normally." global: "Global" @@ -782,11 +785,22 @@ translatedFrom: "Translated from {x}" accountDeletionInProgress: "Account deletion is currently in progress" usernameInfo: "A name that identifies your account from others on this server. You can use the alphabet (a~z, A~Z), digits (0~9) or underscores (_). Usernames cannot be changed later." aiChanMode: "Ai Mode" -keepCw: "Keep Content Warning" +keepCw: "Keep Content Warnings" pubSub: "Pub/Sub Accounts" lastCommunication: "Last communication" resolved: "Resolved" unresolved: "Unresolved" +itsOn: "Enabled" +itsOff: "Disabled" +emailRequiredForSignup: "Require email address for sign-up" +unread: "Unread" +filter: "Filter" +controllPanel: "Control Panel" +manageAccounts: "Manage Accounts" +_signup: + almostThere: "Almost there" + emailAddressInfo: "Please enter your email address." + emailSent: "A confirmation email has been sent to your email address ({email}). Please click the included link to complete account creation." _accountDelete: accountDelete: "Delete Account" mayTakeTime: "As account deletion is a resource-heavy process, it may take some time to complete depending on how much content you have created and how many files you have uploaded." diff --git a/locales/eo-UY.yml b/locales/eo-UY.yml index fbc1aa31ec..a76d5b5fd9 100644 --- a/locales/eo-UY.yml +++ b/locales/eo-UY.yml @@ -1,8 +1,8 @@ --- _lang_: "Esperanto" -headlineMisskey: "Jen la reto konektita de notoj" -introMisskey: "Bonvenon! Misskey estas malfermitkoda malcentraliza mikrobloga servo.\nKreu \"noto\"n por diskonigu tion kio nun okazas, aŭ por parolu pri vi. 📡\nUzu la funkcion \"reago\" por esprimu rapide vian senton pri ies noto. 👍\nBonvole esploru novan mondon. 🚀" -monthAndDay: "{day}a/{month}" +headlineMisskey: "Jen la reto konektata de notoj" +introMisskey: "Bonvenon! Misskey estas malfermitkoda malcentraliza etbloga servo.\nKreu \"noto\"n por paroli vian penson al iuj ĉirkaŭ vi. 📡\nLa funkcion \"reago\" ebligas esprimi rapide vian senton pri ies noto en Fediverso. 👍\nBonvole esploru novan mondon. 🚀" +monthAndDay: "La {day}-a de la {month}-a monato" search: "Serĉi" notifications: "Sciigoj" username: "Uzantnomo" @@ -33,18 +33,18 @@ save: "Konservi" users: "Uzantoj" addUser: "Aldoni uzanton" favorite: "Preferi" -favorites: "Preferataĵoj" +favorites: "Preferaĵoj" unfavorite: "Malpreferi" -favorited: "Aldonita al preferataĵoj" -alreadyFavorited: "Ankoraŭ aldonita al via listo de preferaĵoj." -cantFavorite: "Ne aldonita al preferataĵoj" -pin: "Alpingli al la profilo" +favorited: "Aldonita al via listo de preferaĵoj." +alreadyFavorited: "Ĝi jam estis aldonita al via listo de preferaĵoj." +cantFavorite: "Ne aldonita al via listo de preferaĵoj." +pin: "Alpingli" unpin: "Depingli" copyContent: "Kopii enhavon" copyLink: "Kopii ligilon" delete: "Forviŝi" -deleteAndEdit: "Forviŝi kaj redakti" -deleteAndEditConfirm: "Ĉu vi certas, ke vi volas forigi kaj redakti la noton? Kun tio foriĝos reagoj, plusendaĵoj, kaj respondoj ĉiuj de ĝi." +deleteAndEdit: "Redakti foriginte" +deleteAndEditConfirm: "Ĉu vi certas, ke vi volas forigi kaj redakti la noton? Tio forviŝos reagojn, notojn plusendintajn, kaj respondojn ĉiujn de ĝi." addToList: "Aldoni al listo" sendMessage: "Sendi mesaĝon" copyUsername: "Kopii uzantnomon" @@ -57,17 +57,17 @@ receiveFollowRequest: "Peto de sekvado estas ricevita" followRequestAccepted: "La peto de sekvado akceptita" mention: "Mencioj" mentions: "Al vi" -directNotes: "Notoj rektaj" +directNotes: "Rekte senditaj" importAndExport: "Importi/eksporti" import: "Importi" export: "Eksporti" files: "Dosieroj" download: "Elŝuti" driveFileDeleteConfirm: "Ĉu vi certas, ke vi volas forviŝi la dosieron \"{name}\"? Pro tio forviŝiĝos ankaŭ la notoj kiuj enhavas ĝin." -unfollowConfirm: "Ĉu vi certas, ke vi volas ne plu sekvi {name}'(o)n?" +unfollowConfirm: "Ĉu vi certas, ke vi volas ĉesi sekvi {name}'(o)n?" lists: "Listoj" noLists: "Neniu listo" -note: "Notoj" +note: "Sendi" notes: "Notoj" following: "Sekvatoj" followers: "Sekvantoj" @@ -92,13 +92,13 @@ cantRenote: "Oni ne povas plusendi la noton." cantReRenote: "Plusendado ne estas plusendebla." quote: "Citi" pinnedNote: "Alpinglita noto" -pinned: "Alpingli al la profilo" +pinned: "Alpingli" you: "Vi" clickToShow: "Klaku por malkaŝu" sensitive: "Enhavo ne estas deca por laborejo (NSFW)" add: "Aldoni" reaction: "Reagoj" -rememberNoteVisibility: "Rememoru la videblecon de la noto laste sendita" +rememberNoteVisibility: "Rememoru videblecon de la noto laste sendita " attachCancel: "Deigi aldonaĵon" markAsSensitive: "Troviĝi NSFW" unmarkAsSensitive: "Ne troviĝi NSFW" @@ -137,7 +137,7 @@ removeWallpaper: "Forviŝi ekranfonon. " searchWith: "Serĉi: {q}" youHaveNoLists: "Vi ne havas listojn." followConfirm: "Ĉu vi certas ke vi volas sekvi {name}'(o)n?" -host: "Gastiganto" +host: "Gastigo" selectUser: "Elekti uzanton" recipient: "Ricevonto" annotation: "Komentarioj" @@ -254,7 +254,7 @@ unwatch: "Malobservi" accept: "Permesi" normal: "Normala" instanceName: "Nomo de la nodo" -instanceDescription: "Mempriskribo de la nodo " +instanceDescription: "Priskribo de la nodo " maintainerName: "Nomo de la administranto" maintainerEmail: "Retpoŝto de la administranto" tosUrl: "URL de kondiĉoj de uzado" @@ -323,6 +323,7 @@ newPasswordIs: "La nova pasvorto estas {password}." share: "Diskonigi" notFound: "Ne trovita" cacheClear: "Malplenigi staplon" +markAsReadAllNotifications: "Marki ĉiujn sciigojn kiel legito" help: "Manlibro de uzado" inputMessageHere: "Entajpu masaĝo tie ĉi" close: "Fermi" @@ -344,10 +345,10 @@ quoteAttached: "Kun citaĵo" quoteQuestion: "Ĉu vi aldonas citaĵon?" noMessagesYet: "Ankoraŭ neniu mesaĝo" newMessageExists: "Vi ricevis novan mesaĝon." -onlyOneFileCanBeAttached: "Vi povas aldoni nur unu dosieron po unu mesaĝo." +onlyOneFileCanBeAttached: "Oni povas aldoni nur unu dosieron po mesaĝo." signinRequired: "Bonvolu ensaluti" invitations: "Inviti" -invitationCode: "Kodo de invito" +invitationCode: "Invita kodo" unavailable: "Ne disponebla" passwordMatched: "Konforma" passwordNotMatched: "Nekonforma" @@ -417,7 +418,7 @@ enablePlayer: "Vidi videon" disablePlayer: "Fermi videon" expandTweet: "Disvolvi pepon" themeEditor: "Redaktilo de koloraroj" -description: "Priskribe" +description: "Priskribo" describeFile: "Priskribi la bildon" enterFileDescription: "Priskribu" author: "Aŭtoro" @@ -433,7 +434,7 @@ emailServer: "Retpoŝta servilo" email: "Retpoŝto" emailAddress: "Retpoŝta adreso" smtpConfig: "Agordoj de SMTP servilo" -smtpHost: "Gastiganto" +smtpHost: "Gastigo" smtpPort: "Pordo" smtpUser: "Uzantnomo" smtpPass: "Pasvorto" @@ -481,7 +482,7 @@ notSet: "Ne elektita" emailVerified: "Via retpoŝto estis kontrolita." noteFavoritesCount: "La nombro de notoj preferataj" pageLikesCount: "La nombro de paĝoj kiun la uzanto preferas" -pageLikedCount: "La nombro de uzantoj kiuj preferas la paĝon" +pageLikedCount: "La nombro de uzantoj, kiuj preferas paĝon de ĉi tiu uzanto" contact: "Kontakto" makeExplorable: "Videbligi konton sur la paĝo \"Esplori\"" duplicate: "Duobligi" @@ -509,7 +510,7 @@ inUse: "Uzata" editCode: "Redakti kodon" emailNotification: "Sciigoj per retpoŝto" inChannelSearch: "Serĉi en kanalo" -useReactionPickerForContextMenu: "Oni malfermas reago-elektilon per dekstro-kliki" +useReactionPickerForContextMenu: "Malfermi reago-elektilon per dekstro-klaki" typingUsers: "{users} nun entajpas…" clear: "Vakigi" goBack: "Reiri antaŭ" @@ -540,6 +541,7 @@ troubleshooting: "Problemsolvi" learnMore: "Lernu pli" translate: "Traduki" translatedFrom: "Tradukita el {x}" +controllPanel: "Ŝaltpodio" _docs: continueReading: "Legi plu" features: "Funkcioj" @@ -614,8 +616,8 @@ _wordMute: mutedNotes: "Silentigitaj notoj" _theme: manage: "Administri kolorarojn" - code: "Kodo de koloraro" - description: "Priskribe" + code: "Kolorara kodo" + description: "Priskribo" color: "Koloro" darken: "Malbrileco" lighten: "Brileco" @@ -667,8 +669,8 @@ _permissions: "write:blocks": "Redakti vian liston de blokitoj" "read:drive": "Legi vian diskon" "write:drive": "Ĉia operacio por skribi, forviŝi, aŭ alimaniere ŝanĝi la informon de dosiero en via disko de Misskey" - "read:favorites": "Vidi vian liston de preferataĵoj" - "write:favorites": "Redakti vian liston de preferataĵoj." + "read:favorites": "Vidi vian liston de preferaĵoj" + "write:favorites": "Redakti vian liston de preferaĵoj" "read:following": "Vidi la infomaciojn pri tio, kion vi sekvas" "write:following": "Sekvi aŭ malsekvi alian uzanton" "read:messaging": "Vidi vian retbabiladon" @@ -715,23 +717,24 @@ _poll: vote: "Baloti" closed: "Oni jam balotis ĝin" _visibility: + public: "Publika" publicDescription: "Via noto estos videbla de ĉiuj uzantoj" home: "Hejma" homeDescription: "Dissendi nur sur hejma templinio" - followers: "Sekvantoj" - followersDescription: "Nur al sekvantoj al mi" - specified: "Rekta" - specifiedDescription: "Publikigi nur al specifaj uzantoj" + followers: "Nur al sekvantoj" + followersDescription: "Publiki nur al viaj sekvantoj" + specified: "Rekte" + specifiedDescription: "Montri nur al specifaj uzantoj" localOnly: "Nur loka" localOnlyDescription: "Ne montri al transaj uzantoj" _postForm: - replyPlaceholder: "Respondi tiun noton…" - quotePlaceholder: "Citi tiun noton…" + replyPlaceholder: "Respondi la noton…" + quotePlaceholder: "Citi la noton…" channelPlaceholder: "Mencii en kanalo…" _profile: name: "Nomo" username: "Uzantnomo" - description: "Pri mi" + description: "Sinprezento" metadata: "Kromaj informoj" metadataEdit: "Redakti kromaj informoj" changeAvatar: "Ŝanĝi profilbildon" @@ -746,7 +749,7 @@ _charts: federationInstancesTotal: "La totala nombro de nodoj kunfederantaj" usersTotal: "La totala nombro de la uzantoj" activeUsers: "La nombro de la uzantoj aktivaj" - notesTotal: "La totala nombro de la notoj" + notesTotal: "La totala nombro de notoj" filesTotal: "La totala nombro de la dosieroj" _timelines: home: "Hejma" @@ -859,16 +862,21 @@ _pages: argVariables: "Eniga junto" _notification: fileUploaded: "La dosiero sukcese alŝutiĝis." + youGotMention: "{name} mencis" + youGotReply: "{name} respondis" + youGotQuote: "{name} citis" youRenoted: "{name} plusendis" youGotPoll: "{name} balotis" youGotMessagingMessageFromUser: "{name} sentis mesaĝon al vi." youGotMessagingMessageFromGroup: "Retbabilan mesaĝon oni sendis al la grupo {name}" youWereFollowed: "eksekvis vin" youReceivedFollowRequest: "Vi ricevis peton de sekvado" - yourFollowRequestAccepted: "Via peto por sekvado estis akceptita." + yourFollowRequestAccepted: "Via peto de sekvado estis akceptita." _types: - follow: "Sekvatoj" + all: "Ĉio" + follow: "Nova sekvatoj" mention: "Mencioj" + reply: "Respondoj" renote: "Notoj plusenditaj" quote: "Citi" reaction: "Reagoj" @@ -882,4 +890,4 @@ _deck: antenna: "Antenoj" list: "Listoj" mentions: "Al vi" - direct: "Notoj rektaj" + direct: "Rekte" diff --git a/locales/es-ES.yml b/locales/es-ES.yml index 0defcabd0e..c55a00b821 100644 --- a/locales/es-ES.yml +++ b/locales/es-ES.yml @@ -7,6 +7,7 @@ search: "Buscar" notifications: "Notificaciones" username: "Nombre de usuario" password: "Contraseña" +forgotPassword: "Olvidé mi Contraseña" fetchingAsApObject: "Buscando en el fediverso" ok: "OK" gotIt: "Entendido" @@ -279,6 +280,7 @@ emptyDrive: "El drive está vacío" emptyFolder: "La carpeta está vacía" unableToDelete: "No se puede borrar" inputNewFileName: "Ingrese un nuevo nombre de archivo" +inputNewDescription: "Ingrese nueva descripción" inputNewFolderName: "Ingrese un nuevo nombre de la carpeta" circularReferenceFolder: "La carpeta de destino es una sub-carpeta de la carpeta que quieres mover." hasChildFilesOrFolders: "No se puede borrar esta carpeta. No está vacía." @@ -310,6 +312,8 @@ monthX: "Mes {month}" yearX: "Año {year}" pages: "Páginas" integration: "Integración" +connectService: "Conectar" +disconnectService: "Desconectar" enableLocalTimeline: "Habilitar linea de tiempo local" enableGlobalTimeline: "Habilitar linea de tiempo global" disablingTimelinesInfo: "Aunque se desactiven estas lineas de tiempo, por conveniencia el administrador y los moderadores pueden seguir usándolos" @@ -323,6 +327,7 @@ driveCapacityPerRemoteAccount: "Capacidad del drive por usuario remoto" inMb: "En megabytes" iconUrl: "URL de la imagen del avatar" bannerUrl: "URL de la imagen del banner" +backgroundImageUrl: "URL de la imagen de fondo" basicInfo: "Información básica" pinnedUsers: "Usuarios fijados" pinnedUsersDescription: "Describir los usuarios que quiere fijar en la página \"Descubrir\" separados por una linea nueva" @@ -524,6 +529,9 @@ removeAllFollowing: "Retener todos los siguientes" removeAllFollowingDescription: "Cancelar todos los siguientes del servidor {host}. Ejecutar en caso de que esta instancia haya dejado de existir" userSuspended: "Este usuario ha sido suspendido." userSilenced: "Este usuario ha sido silenciado." +yourAccountSuspendedTitle: "Esta cuenta ha sido suspendida" +yourAccountSuspendedDescription: "Esta cuenta ha sido suspendida debido a violaciones de los términos de servicio del servidor y otras razones. Para más información, póngase en contacto con el administrador. Por favor, no cree una nueva cuenta." +menu: "Menú" divider: "Divisor" addItem: "Agregar elemento" rooms: "Cuartos" @@ -543,6 +551,8 @@ disablePlayer: "Cerrar reproductor" expandTweet: "Expandir tweet" themeEditor: "Editor de temas" description: "Descripción" +describeFile: "Añade una descripción" +enterFileDescription: "Introducir un título" author: "Autor" leaveConfirm: "Hay modificaciones sin guardar. ¿Desea descartarlas?" manage: "Administrar" @@ -645,29 +655,90 @@ driveFilesCount: "Cantidad de archivos en el drive" driveUsage: "Uso del drive" noCrawle: "Rechazar indexación del crawler" noCrawleDescription: "Pedir a los motores de búsqueda que no indexen tu perfil, notas, páginas, etc." +lockedAccountInfo: "A menos que configures la visibilidad de tus notas como \"Sólo seguidores\", tus notas serán visibles para cualquiera, incluso si requieres que los seguidores sean aprobados manualmente." alwaysMarkSensitive: "Marcar los medios de comunicación como contenido sensible por defecto" +loadRawImages: "Cargar las imágenes originales en lugar de mostrar las miniaturas" +disableShowingAnimatedImages: "No reproducir imágenes animadas" verificationEmailSent: "Se le ha enviado un correo electrónico de confirmación. Por favor, acceda al enlace proporcionado en el correo electrónico para completar la configuración." notSet: "Sin especificar" emailVerified: "Su dirección de correo electrónico ha sido verificada." noteFavoritesCount: "Número de notas favoritas" pageLikesCount: "Número de favoritos en la página" pageLikedCount: "Número de favoritos de su página" +reversiCount: "Numero de partidas Reversi" contact: "Contacto" +useSystemFont: "Utilizar la tipografía por defecto del sistema" clips: "Clip" +experimentalFeatures: "Características experimentales" +developer: "Desarrolladores" +makeExplorable: "Hacer visible la cuenta en \"Explorar\"" +makeExplorableDescription: "Si desactiva esta opción, su cuenta no aparecerá en la sección \"Explorar\"." +showGapBetweenNotesInTimeline: "Mostrar un intervalo entre notas en la línea de tiempo" +duplicate: "Duplicar" +left: "Izquierda" +center: "Centrar" +wide: "Ancho" +narrow: "Estrecho" +reloadToApplySetting: "Esta configuración sólo se aplicará después de recargar la página. ¿Recargar ahora?" +showTitlebar: "Mostrar la barra de título" clearCache: "Limpiar caché" +onlineUsersCount: "{n} usuarios en línea" +nUsers: "{n} Usuarios" +nNotes: "{n} Notas" +sendErrorReports: "Envíar informe de errores" +sendErrorReportsDescription: "Si habilita esta opción, ayudará a mejorar la calidad de Misskey compartiendo información detallada sobre los errores cuando se produzca un problema.\nEsto incluye información como la versión de su sistema operativo, el tipo de navegador que utiliza, su historial de actividad, etc." +myTheme: "Mi Tema" backgroundColor: "Fondo" accentColor: "Acento" textColor: "Texto" +saveAs: "Guardar como…" +advanced: "Avanzado" value: "Valores" +createdAt: "Fecha de creación" +updatedAt: "Actualizado" +saveConfirm: "¿Guardar cambios?" +deleteConfirm: "¿Desea eliminarlo?" +invalidValue: "Este no es un valor válido." +registry: "Registro" +closeAccount: "Cerrar cuenta" +currentVersion: "Versión actual" +latestVersion: "Última versión" +youAreRunningUpToDateClient: "Está utilizando la versión más reciente de su cliente." +newVersionOfClientAvailable: "Hay una versión más nueva de su cliente disponible." +usageAmount: "Uso" +capacity: "Capacidad" +inUse: "Usado" +editCode: "Editar código" goBack: "Deseleccionar" info: "Información" user: "Usuarios" administration: "Administrar" expiration: "Termina el" middle: "Mediano" +customCssWarn: "Este ajuste sólo debe utilizarse si se sabe lo que hace. Introducir valores inadecuados puede hacer que el cliente deje de funcionar con normalidad." global: "Global" +squareAvatars: "Mostrar iconos cuadrados" sent: "Enviar" +received: "Recibido" +searchResult: "Resultados de búsqueda" hashtags: "Hashtag" +troubleshooting: "Solución de problemas" +useBlurEffect: "Utilizar efecto de desenfoque en la interfaz de usuario" +learnMore: "Ver más" +misskeyUpdated: "¡Misskey ha sido actualizado!" +whatIsNew: "Mostrar cambios" +translate: "Traducir" +translatedFrom: "Traducido de {x}" +accountDeletionInProgress: "La eliminación de la cuenta está en curso" +usernameInfo: "Un nombre que identifique su cuenta de otras en este servidor. Puede utilizar el alfabeto (a~z, A~Z), dígitos (0~9) o guiones bajos (_). Los nombres de usuario no se pueden cambiar posteriormente." +aiChanMode: "Modo Ai" +keepCw: "Mantener la advertencia de contenido" +pubSub: "Cuentas Pub/Sub" +lastCommunication: "Última comunicación" +resolved: "Resuelto" +unresolved: "Sin resolver" +_accountDelete: + accountDelete: "Eliminar Cuenta" _docs: admin: "Administrar" _ad: diff --git a/locales/fr-FR.yml b/locales/fr-FR.yml index c4fee05343..99d2b7bc07 100644 --- a/locales/fr-FR.yml +++ b/locales/fr-FR.yml @@ -81,6 +81,8 @@ somethingHappened: "Une erreur est survenue" retry: "Réessayer" pageLoadError: "Le chargement de la page a échoué" pageLoadErrorDescription: "Cela est généralement causé par le cache du navigateur ou par un problème réseau. Veuillez vider votre cache ou attendre un peu et réessayer." +serverIsDead: "Le serveur ne répond pas. Patientez quelques instants puis essayez à nouveau." +youShouldUpgradeClient: "Si la page ne s'affiche pas correctement, rechargez-la pour mettre votre client à jour." enterListName: "Nom de la liste" privacy: "Confidentialité" makeFollowManuallyApprove: "Accepter manuellement les demandes d’abonnement" @@ -136,7 +138,7 @@ settingGuide: "Configuration proposée" cacheRemoteFiles: "Mise en cache des fichiers distants" cacheRemoteFilesDescription: "Lorsque cette option est désactivée, les fichiers distants sont chargés directement depuis l’instance distante. La désactiver diminuera certes l’utilisation de l’espace de stockage local mais augmentera le trafic réseau puisque les miniatures ne seront plus générées." flagAsBot: "Ce compte est un robot" -flagAsBotDescription: "Si ce compte est géré de manière automatisée , définissez cette option. Si elle est activée, elle agira comme un marqueur pour les autres développeurs afin d'éviter des chaînes d'interaction sans fin avec d'autres robots et d'ajuster les systèmes internes de Misskey pour traiter ce compte comme un robot." +flagAsBotDescription: "Si ce compte est géré de manière automatisée, choisissez cette option. Si elle est activée, elle agira comme un marqueur pour les autres développeurs afin d'éviter des chaînes d'interaction sans fin avec d'autres robots et d'ajuster les systèmes internes de Misskey pour traiter ce compte comme un robot." flagAsCat: "Ce compte est un chat" flagAsCatDescription: "Activer l'option \" Je suis un chat \" pour ce compte." autoAcceptFollowed: "Accepter automatiquement les demandes d’abonnement venant d’utilisateur·rice·s que vous suivez" @@ -377,7 +379,7 @@ aboutMisskey: "À propos de Misskey" administrator: "Administrateur" token: "Jeton" twoStepAuthentication: "Authentification à deux facteurs" -moderator: "Modérateurs" +moderator: "Modérateur·rice·s" nUsersMentioned: "{n} utilisateur·rice·s mentionné·e·s" securityKey: "Clé de sécurité" securityKeyName: "Nom de la clé" @@ -495,7 +497,7 @@ objectStorageSetPublicRead: "Régler sur « public » lors de l'envoi" serverLogs: "Journal du serveur" deleteAll: "Supprimer tout" showFixedPostForm: "Afficher le formulaire de publication en haut du fil d'actualité" -newNoteRecived: "Vous avez reçu une nouvelle note" +newNoteRecived: "Voir les nouvelles notes" sounds: "Sons" listen: "Écouter" none: "Rien" @@ -530,6 +532,7 @@ removeAllFollowingDescription: "Se désabonner de tous les comptes de {host}. Ve userSuspended: "Cet·te utilisateur·rice a été suspendu·e." userSilenced: "Cette utilisateur·trice a été mis·e en sourdine." yourAccountSuspendedTitle: "Ce compte est suspendu" +yourAccountSuspendedDescription: "Ce compte est suspendu car vous avez enfreint les conditions d'utilisation de l'instance, ou pour un motif similaire. Si vous souhaitez connaître en détail les raisons de cette suspension, renseignez-vous auprès de l'administrateur·rice de votre instance. Merci de ne pas créer de nouveau compte." menu: "Menu" divider: "Séparateur" addItem: "Ajouter un élément" @@ -786,6 +789,15 @@ pubSub: "Comptes Pub/Sub" lastCommunication: "Dernière communication" resolved: "Résolu" unresolved: "En attente" +emailRequiredForSignup: "Une adresse e-mail est nécessaire pour créer un compte" +unread: "Non lu" +filter: "Filtre" +controllPanel: "Panneau de contrôle" +manageAccounts: "Gérer les comptes" +_signup: + almostThere: "Bientôt fini" + emailAddressInfo: "Insérez votre adresse e-mail." + emailSent: "Un courriel de confirmation vient d'être envoyé à l'adresse que vous avez renseignée ({email}). Cliquez sur le lien contenu dans le message pour terminer la création de votre compte." _accountDelete: accountDelete: "Supprimer le compte" mayTakeTime: "La suppression de compte nécessitant beaucoup de ressources, l'exécution du processus peut prendre du temps, en fonction de la quantité de contenus que vous avez créés et du nombre de fichiers que vous avez téléversés." @@ -900,6 +912,8 @@ _mfm: fontDescription: "Il est possible de choisir la police." rainbow: "Arc-en-ciel" rainbowDescription: "Permet d'afficher le contenu en couleurs arc-en-ciel." + sparkle: "Paillettes" + sparkleDescription: "Ajoute un effet scintillant au contenu." _reversi: reversi: "Reversi" gameSettings: "Réglages de la partie" @@ -1243,7 +1257,7 @@ _charts: federationInstancesTotal: "Nombre total d'instances fédérées" usersIncDec: "Variation du nombre d'utilisateur·rice·s" usersTotal: "Nombre des utilisateur·rice·s au total" - activeUsers: "Utilisateur·rice·s actif·ve·s" + activeUsers: "Nombre d'utilisateurices actif·ve·s" notesIncDec: "Variation du nombre des notes" localNotesIncDec: "Variation du nombre de notes locales" remoteNotesIncDec: "Variation du nombre de notes distantes" diff --git a/locales/id-ID.yml b/locales/id-ID.yml index 06191089c3..44c7f49f1c 100644 --- a/locales/id-ID.yml +++ b/locales/id-ID.yml @@ -780,6 +780,7 @@ translatedFrom: "Terjemahkan dari {x}" accountDeletionInProgress: "Penghapusan akun sedang dalam proses" usernameInfo: "Nama yang mengidentifikasikan akun kamu dari yang lain pada server ini. Kamu dapat menggunakan alfabet (a~z, A~Z), digit (0~9) atau garis bawah (_). Username tidak dapat diubah setelahnya." keepCw: "Biarkan Peringatan Konten" +controllPanel: "Panel kontrol" _accountDelete: accountDelete: "Hapus akun" mayTakeTime: "Karena penghapusan akun merupakan proses yang berat dan intensif, kemungkinan dapat membutuhkan waktu untuk menyelesaikan tergantung daripada berapa banyak konten yang kamu buat dan berapa banyak berkas yang telah kamu unggah." diff --git a/locales/it-IT.yml b/locales/it-IT.yml index f291eaa17f..fc9060852d 100644 --- a/locales/it-IT.yml +++ b/locales/it-IT.yml @@ -482,7 +482,7 @@ objectStorageSetPublicRead: "Imposta \"visibilità pubblica\" al momento di cari serverLogs: "Log del server" deleteAll: "Cancella cronologia" showFixedPostForm: "Visualizzare la finestra di pubblicazione in cima alla timeline" -newNoteRecived: "Nuova nota ricevuta" +newNoteRecived: "Vedi le nuove note" sounds: "Impostazioni suoni" listen: "Ascolta" none: "Niente" diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml index 82c7c1a079..db0cf7f20c 100644 --- a/locales/ko-KR.yml +++ b/locales/ko-KR.yml @@ -787,6 +787,7 @@ pubSub: "Pub/Sub 계정" lastCommunication: "마지막 통신" resolved: "해결됨" unresolved: "해결되지 않음" +controllPanel: "제어판" _accountDelete: accountDelete: "계정 삭제" mayTakeTime: "계정 삭제는 서버에 부하를 가하기 때문에, 작성한 콘텐츠나 업로드한 파일의 수가 많으면 완료까지 시간이 걸릴 수 있습니다." @@ -901,6 +902,8 @@ _mfm: fontDescription: "내용의 글꼴을 지정할 수 있습니다." rainbow: "무지개" rainbowDescription: "내용을 무지개로 표시합니다." + sparkle: "반짝반짝" + sparkleDescription: "반짝이는 파티클 효과를 추가합니다." _reversi: reversi: "리버시" gameSettings: "대국 설정" diff --git a/locales/ru-RU.yml b/locales/ru-RU.yml index b886d4c605..c8aa28df5e 100644 --- a/locales/ru-RU.yml +++ b/locales/ru-RU.yml @@ -81,6 +81,8 @@ somethingHappened: "Что-то пошло не так" retry: "Повторить попытку" pageLoadError: "Не удалось загрузить страницу" pageLoadErrorDescription: "Обычно это случается из-за сбоев в сети или кэша браузера. Попробуйте очистить кэш, или подождать пару минут, а потом попытаться загрузить страницу снова." +serverIsDead: "Ответа от сервера нет. Пожалуйста, подождите немного и повторите попытку." +youShouldUpgradeClient: "Чтобы просмотреть эту страницу, пожалуйста, обновите ее." enterListName: "Название списка" privacy: "Конфиденциальность" makeFollowManuallyApprove: "Принимать подписчиков вручную" @@ -529,6 +531,8 @@ removeAllFollowing: "Удалить всех подписчиков" removeAllFollowingDescription: "Отменить все подписки с домена {host}? Пожалуйста, применяйте это действие, если инстанс больше не существует." userSuspended: "Эта учётная запись заморожена" userSilenced: "Этот пользователь был заглушен" +yourAccountSuspendedTitle: "Эта учетная запись заблокирована" +yourAccountSuspendedDescription: "Эта учетная запись была заблокирована из-за нарушения условий предоставления услуг сервера. Свяжитесь с администратором, если вы хотите узнать более подробную причину. Пожалуйста, не создавайте новую учетную запись." menu: "Меню" divider: "Линия-разделитель" addItem: "Добавить элемент" @@ -775,6 +779,13 @@ useBlurEffect: "Размытие в интерфейсе" learnMore: "Подробнее" misskeyUpdated: "Misskey обновился!" whatIsNew: "Что новенького?" +translate: "Перевод" +accountDeletionInProgress: "В настоящее время выполняется удаление учетной записи" +usernameInfo: "Имя, которое отличает вашу учетную запись от других на этом сервере. Вы можете использовать алфавит (a~z, A~Z), цифры (0~9) или символы подчеркивания (_). Имена пользователей не могут быть изменены позже." +aiChanMode: "ИИ режим" +keepCw: "Сохраняйте Предупреждения о содержимом" +controllPanel: "Панель управления" +manageAccounts: "Управление аккаунтом" _docs: continueReading: "Читать подробнее" features: "Возможности" diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index 00e5ffce46..a8e653f797 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -81,6 +81,8 @@ somethingHappened: "出现了一些问题!" retry: "重试" pageLoadError: "页面加载失败。" pageLoadErrorDescription: "这通常是由于网络或浏览器缓存的原因。请清除缓存或等待片刻后重试。" +serverIsDead: "服务器没有响应。 请稍等片刻,然后重试。" +youShouldUpgradeClient: "请重新加载并使用新版本的客户端查看此页面。" enterListName: "输入列表名称" privacy: "隐私" makeFollowManuallyApprove: "关注者的关注请求需要批准" @@ -764,6 +766,7 @@ middle: "中" low: "低" emailNotConfiguredWarning: "电子邮件地址未设置。" ratio: "比率" +previewNoteText: "预览文本" customCss: "自定义 CSS" customCssWarn: "这些设置必须有相关的基础知识,不当的配置可能导致客户端无法正常使用!" global: "全局" @@ -787,6 +790,17 @@ pubSub: "Pub/Sub账户" lastCommunication: "最近通信" resolved: "已解决" unresolved: "未解决" +itsOn: "已开启" +itsOff: "已关闭" +emailRequiredForSignup: "注册账户需要电子邮件地址" +unread: "未读" +filter: "筛选" +controllPanel: "控制面板" +manageAccounts: "管理账户" +_signup: + almostThere: "即将完成" + emailAddressInfo: "请输入您所使用的电子邮件地址" + emailSent: "已将确认邮件发送至您输入的电子邮件地址 ({email})。请访问电子邮件中的链接以完成帐户创建。" _accountDelete: accountDelete: "删除帐户" mayTakeTime: "删除账号是一个性能损耗较大的处理,如果账号持有的内容数量和上传的文件数量较多的话,完成需要花费一段时间。" @@ -901,6 +915,8 @@ _mfm: fontDescription: "可以设置内容所使用的字体。" rainbow: "彩虹" rainbowDescription: "用彩虹色来显示内容。" + sparkle: "闪光" + sparkleDescription: "添加发光粒子效果。" _reversi: reversi: "黑白棋" gameSettings: "对局设置" diff --git a/src/docs/en-US/advanced/aiscript.md b/src/docs/en-US/advanced/aiscript.md index fc2802fcd4..cfcdfebf96 100644 --- a/src/docs/en-US/advanced/aiscript.md +++ b/src/docs/en-US/advanced/aiscript.md @@ -3,5 +3,5 @@ AiScript is a scripting language for Misskey. <div class="info">ℹ️ AiScript is open source and hosted in a separate repository from Misskey. </a></div> -## 使い方 +## Usage AiScript documentation such as syntax and built-in functions can be found [here](https://github.com/syuilo/aiscript/tree/master/docs). diff --git a/src/docs/en-US/advanced/api.md b/src/docs/en-US/advanced/api.md index 76019b6145..ef1995b18e 100644 --- a/src/docs/en-US/advanced/api.md +++ b/src/docs/en-US/advanced/api.md @@ -4,7 +4,7 @@ MisskeyAPIを使ってMisskeyクライアント、Misskey連携Webサービス APIを使い始めるには、まずアクセストークンを取得する必要があります。 このドキュメントでは、アクセストークンを取得する手順を説明した後、基本的なAPIの使い方を説明します。 -## アクセストークンの取得 +## Obtain an access token 基本的に、APIはリクエストにはアクセストークンが必要となります。 APIにリクエストするのが自分自身なのか、不特定の利用者に使ってもらうアプリケーションなのかによって取得手順は異なります。 * 前者の場合: [「自分自身のアクセストークンを手動発行する」](#自分自身のアクセストークンを手動発行する)に進む @@ -20,7 +20,7 @@ APIを使い始めるには、まずアクセストークンを取得する必 #### Step 1 -UUIDを生成する。以後これをセッションIDと呼びます。 +Generate UUID.以後これをセッションIDと呼びます。 > このセッションIDは毎回生成し、使いまわさないようにしてください。 @@ -30,14 +30,14 @@ UUIDを生成する。以後これをセッションIDと呼びます。 > 例: `{_URL_}/miauth/c1f6d42b-468b-4fd2-8274-e58abdedef6f` 表示する際、URLにクエリパラメータとしていくつかのオプションを設定できます: -* `name` ... アプリケーション名 +* `name` ... App name * > 例: `MissDeck` -* `icon` ... アプリケーションのアイコン画像URL +* `icon` ... App icon URL * > 例: `https://missdeck.example.com/icon.png` * `callback` ... 認証が終わった後にリダイレクトするURL * > 例: `https://missdeck.example.com/callback` * リダイレクト時には、`session`というクエリパラメータでセッションIDが付きます -* `permission` ... アプリケーションが要求する権限 +* `permission` ... App permissions * > 例: `write:notes,write:following,read:drive` * 要求する権限を`,`で区切って列挙します * どのような権限があるかは[APIリファレンス](/api-doc)で確認できます @@ -46,13 +46,13 @@ UUIDを生成する。以後これをセッションIDと呼びます。 ユーザーが発行を許可した後、`{_URL_}/api/miauth/{session}/check`にPOSTリクエストすると、レスポンスとしてアクセストークンを含むJSONが返ります。 レスポンスに含まれるプロパティ: -* `token` ... ユーザーのアクセストークン -* `user` ... ユーザーの情報 +* `token` ... User access token +* `user` ... User info [「APIの使い方」へ進む](#APIの使い方) -## APIの使い方 -**APIはすべてPOSTで、リクエスト/レスポンスともにJSON形式です。RESTではありません。** アクセストークンは、`i`というパラメータ名でリクエストに含めます。 +## API usage +**APIはすべてPOSTで、リクエスト/レスポンスともにJSON形式です。There is no REST support.** アクセストークンは、`i`というパラメータ名でリクエストに含めます。 -* [APIリファレンス](/api-doc) -* [ストリーミングAPI](./stream) +* [API Reference](/api-doc) +* [Streaming API](./stream) diff --git a/src/docs/en-US/advanced/create-plugin.md b/src/docs/en-US/advanced/create-plugin.md index ec17b95186..3b8763dfd8 100644 --- a/src/docs/en-US/advanced/create-plugin.md +++ b/src/docs/en-US/advanced/create-plugin.md @@ -1,20 +1,20 @@ -# プラグインの作成 +# New Plugin Misskey Webクライアントのプラグイン機能を使うと、クライアントを拡張し、様々な機能を追加できます。 ここではプラグインの作成にあたってのメタデータ定義や、AiScript APIリファレンスを掲載します。 ## Metadata プラグインは、AiScriptのメタデータ埋め込み機能を使って、デフォルトとしてプラグインのメタデータを定義する必要があります。 メタデータは次のプロパティを含むオブジェクトです。 ### name -プラグイン名 +Plugin name ### author -プラグイン作者 +Plugin author ### version -プラグインバージョン。数値を指定してください。 +プラグインバージョン。A number must be specified. ### description -プラグインの説明 +Plugin description ### permissions プラグインが要求する権限。MisskeyAPIにリクエストする際に用いられます。 @@ -34,7 +34,7 @@ Misskey Webクライアントのプラグイン機能を使うと、クライア #### default 設定のデフォルト値 -## APIリファレンス +## API Reference AiScript標準で組み込まれているAPIは掲載しません。 ### Mk:dialog(title text type) diff --git a/src/docs/en-US/advanced/develop-bot.md b/src/docs/en-US/advanced/develop-bot.md index 7f825e9bc4..c6a312e79e 100644 --- a/src/docs/en-US/advanced/develop-bot.md +++ b/src/docs/en-US/advanced/develop-bot.md @@ -1,4 +1,4 @@ -# Botの作成 +# Create a bot [Misskey API](./api)を利用してBotの開発が可能です。 また、いくつかのBot実装が公開されているため、ぜひ参考にしてください。 - [syuilo/ai](https://github.com/syuilo/ai) ... Node.js上で動く、TypeScript製Bot実装 diff --git a/src/docs/en-US/advanced/stream.md b/src/docs/en-US/advanced/stream.md index c0d0efc910..16b15c7619 100644 --- a/src/docs/en-US/advanced/stream.md +++ b/src/docs/en-US/advanced/stream.md @@ -1,4 +1,4 @@ -# ストリーミングAPI +# Streaming API ストリーミングAPIを使うと、リアルタイムで様々な情報(例えばタイムラインに新しい投稿が流れてきた、メッセージが届いた、フォローされた、など)を受け取ったり、様々な操作を行ったりすることができます。 diff --git a/src/docs/en-US/general/glossary.md b/src/docs/en-US/general/glossary.md index 53164a0a59..139c3eab82 100644 --- a/src/docs/en-US/general/glossary.md +++ b/src/docs/en-US/general/glossary.md @@ -73,7 +73,7 @@ A feature allowing users to organize the files they have uploaded to Misskey.For ## Notes Content which may include text, images, surveys and others that has been posted to Misskey.For details, see [here.](../features/note) -## Misskist +## Misskeyist Users of Misskey. ## Moderator diff --git a/src/docs/en-US/general/troubleshooting.md b/src/docs/en-US/general/troubleshooting.md index e3dd5129e0..75051debe6 100644 --- a/src/docs/en-US/general/troubleshooting.md +++ b/src/docs/en-US/general/troubleshooting.md @@ -34,7 +34,7 @@ A blinking light indicates unread content.In cases where this light won't go awa Followers-only notes cannot be renoted. ## Specific parts of the UI are not being displayed -Problems like these can arise if you are using an Adblocker.Please turn these off on Misskey. +Problems like these can arise if you are using an Adblocker. For an optimized experience on Misskey, please turn it off. ## Some parts of the UI are untranslated In most cases, this is simply a matter of the translation not having been done yet instead of being a bug.Please wait until the translation of this area has been completed. You can alternatively also [participate in translation](./misskey) yourself. diff --git a/src/docs/eo-UY/admin/faq.md b/src/docs/eo-UY/admin/faq.md index 317b4e0655..5341c0f16f 100644 --- a/src/docs/eo-UY/admin/faq.md +++ b/src/docs/eo-UY/admin/faq.md @@ -1,4 +1,4 @@ -# よくある質問 +# Oftaj demandoj ここでは、サーバー管理者向けのよくある質問を掲載しています。 ## デフォルトテーマを設定したい diff --git a/src/docs/eo-UY/advanced/create-plugin.md b/src/docs/eo-UY/advanced/create-plugin.md index e7826037b8..7ed29bb824 100644 --- a/src/docs/eo-UY/advanced/create-plugin.md +++ b/src/docs/eo-UY/advanced/create-plugin.md @@ -1,4 +1,4 @@ -# プラグインの作成 +# Krei kromaĵo Misskey Webクライアントのプラグイン機能を使うと、クライアントを拡張し、様々な機能を追加できます。 ここではプラグインの作成にあたってのメタデータ定義や、AiScript APIリファレンスを掲載します。 ## Metadatumoj diff --git a/src/docs/eo-UY/advanced/stream.md b/src/docs/eo-UY/advanced/stream.md index 932da90f25..9f5cdbbcb0 100644 --- a/src/docs/eo-UY/advanced/stream.md +++ b/src/docs/eo-UY/advanced/stream.md @@ -1,4 +1,4 @@ -# ストリーミングAPI +# API de Flui ストリーミングAPIを使うと、リアルタイムで様々な情報(例えばタイムラインに新しい投稿が流れてきた、メッセージが届いた、フォローされた、など)を受け取ったり、様々な操作を行ったりすることができます。 diff --git a/src/docs/eo-UY/features/favorite.md b/src/docs/eo-UY/features/favorite.md index 05c03fa2db..747c871a5d 100644 --- a/src/docs/eo-UY/features/favorite.md +++ b/src/docs/eo-UY/features/favorite.md @@ -1,4 +1,4 @@ -# Preferataĵoj +# Preferaĵoj [ノート](./node)をお気に入りとして登録できる機能です。 お気に入り登録したノートは、[お気に入りページ](./my/favorites)で一覧することができます。 お気に入りに登録したことは相手に通知されず、お気に入りは自分しか見ることができません。 ノートをお気に入り登録するには、ノートメニューの「お気に入り」を押します。お気に入り解除するには、ノートメニューの「お気に入り解除」を押します。 diff --git a/src/docs/eo-UY/features/keyboard-shortcut.md b/src/docs/eo-UY/features/keyboard-shortcut.md index b4bb35b763..413bef16c1 100644 --- a/src/docs/eo-UY/features/keyboard-shortcut.md +++ b/src/docs/eo-UY/features/keyboard-shortcut.md @@ -4,21 +4,21 @@ これらのショートカットは基本的にどこでも使えます。 <table> <thead> - <tr><th>Fulmoklavoj</th><th>効果</th><th>由来</th></tr> + <tr><th>Fulmoklavoj</th><th>Rezultato</th><th>Deveno (angla)</th></tr> </thead> <tbody> <tr><td><kbd class="key">P</kbd>, <kbd class="key">N</kbd></td><td>Skribi novan noton</td><td><b>P</b>ost, <b>N</b>ew, <b>N</b>ote</td></tr> <tr><td><kbd class="key">T</kbd></td><td>タイムラインの最も新しい投稿にフォーカス</td><td><b>T</b>imeline, <b>T</b>op</td></tr> <tr><td><kbd class="group"><kbd class="key">Shift</kbd> + <kbd class="key">N</kbd></kbd></td><td>Malfermi sekcio de sciigoj</td><td><b>N</b>otifications</td></tr> <tr><td><kbd class="key">S</kbd></td><td>Serĉi</td><td><b>S</b>earch</td></tr> - <tr><td><kbd class="key">H</kbd>, <kbd class="key">?</kbd></td><td>ヘルプを表示</td><td><b>H</b>elp</td></tr> + <tr><td><kbd class="key">H</kbd>, <kbd class="key">?</kbd></td><td>Montru helpon</td><td><b>H</b>elp</td></tr> </tbody> </table> ## 投稿にフォーカスされた状態 <table> <thead> - <tr><th>Fulmoklavoj</th><th>効果</th><th>Deveno (angla)</th></tr> + <tr><th>Fulmoklavoj</th><th>Rezultato</th><th>Deveno (angla)</th></tr> </thead> <tbody> <tr><td><kbd class="key">↑</kbd>, <kbd class="key">K</kbd>, <kbd class="group"><kbd class="key">Shift</kbd> + <kbd class="key">Tab</kbd></kbd></td><td>上の投稿にフォーカスを移動</td><td>-</td></tr> @@ -28,7 +28,7 @@ <tr><td><kbd class="group"><kbd class="key">Ctrl</kbd> + <kbd class="key">Q</kbd></kbd></td><td>Tuj plusendos (sen la fasado)</td><td>-</td></tr> <tr><td><kbd class="key">E</kbd>, <kbd class="key">A</kbd>, <kbd class="key">+</kbd></td><td>リアクションフォームを開く</td><td><b>E</b>mote, re<b>A</b>ction</td></tr> <tr><td><kbd class="key">0</kbd>-<kbd class="key">9</kbd></td><td>数字に対応したリアクションをする(対応については後述)</td><td>-</td></tr> - <tr><td><kbd class="key">F</kbd>, <kbd class="key">B</kbd></td><td>Aldoni vian liston de preferaĵoj</td><td><b>F</b>avorite, <b>B</b>ookmark</td></tr> + <tr><td><kbd class="key">F</kbd>, <kbd class="key">B</kbd></td><td>Aldoni al vian liston de preferaĵoj</td><td><b>F</b>avorite, <b>B</b>ookmark</td></tr> <tr><td><kbd class="key">Del</kbd>, <kbd class="group"><kbd class="key">Ctrl</kbd> + <kbd class="key">D</kbd></kbd></td><td>Forviŝi la noton</td><td><b>D</b>elete</tr> <tr><td><kbd class="key">M</kbd>, <kbd class="key">O</kbd></td><td>Malfelmi poŝtaĵan menuon</td><td><b>M</b>ore, <b>O</b>ther</td></tr> <tr><td><kbd class="key">S</kbd></td><td>CWで隠された部分を表示 or 隠す</td><td><b>S</b>how, <b>S</b>ee</td></tr> @@ -48,11 +48,11 @@ </tbody> </table> -## リアクションフォーム +## Elektilo de reago デフォルトで「👍」にフォーカスが当たっている状態です。 <table> <thead> - <tr><th>Fulmoklavoj</th><th>効果</th><th>Deveno (angla)</th></tr> + <tr><th>Fulmoklavoj</th><th>Rezultato</th><th>Deveno (angla)</th></tr> </thead> <tbody> <tr><td><kbd class="key">↑</kbd>, <kbd class="key">K</kbd></td><td>上のリアクションにフォーカスを移動</td><td>-</td></tr> diff --git a/src/docs/eo-UY/features/mfm.md b/src/docs/eo-UY/features/mfm.md index 2970fbd7b2..7257033d8d 100644 --- a/src/docs/eo-UY/features/mfm.md +++ b/src/docs/eo-UY/features/mfm.md @@ -2,10 +2,10 @@ MFMは、Misskey Flavored Markdownの略で、Misskeyの様々な場所で使用できる専用のマークアップ言語です。 MFMで使用可能な構文は[MFMチートシート](/mfm-cheat-sheet)で確認できます。 ## MFMが使用可能な場所の例 -- ノート本文 +- Teksto de notoj - CW注釈 -- Nomo de uzanto -- Profilo de uzanto +- La nomo de uzantoj +- La sinprezento de profiloj ## Informoj por programistoj MFMのパーサー実装はライブラリとして公開されており、簡単にクライアントにMFMを組み込むことが可能です。 diff --git a/src/docs/eo-UY/features/note.md b/src/docs/eo-UY/features/note.md index aa72981e24..2bfe10951c 100644 --- a/src/docs/eo-UY/features/note.md +++ b/src/docs/eo-UY/features/note.md @@ -1,5 +1,5 @@ # Notoj -ノートは、Misskeyに投稿される、文章、ファイル、アンケートなどを含むコンテンツで、Misskeyの中心的概念です。また、そのノートを作成する行為自体もノートと呼ばれます。 +Notoj estas centraj konceptoj en Misskey kaj enhavoj kiuj konsistas el teksto, bildoj, dosieroj, balotujo k.t.p.Ankaŭ krei notojn estas nomata "noto" same kiel ili ノートが作成されると、[タイムライン](./timeline)に追加され、自分の[フォロワー](./follow)やサーバーのユーザーが見れるようになります。 @@ -7,14 +7,14 @@ ノートを[お気に入り](./favorite)登録することで、後で簡単に見返すことができます。 -## ノートを作成する +## Skribi notojn ノートを作成するには、画面上にある鉛筆マークのボタンを押して、作成フォームを開きます。作成フォームに内容を入力し、「ノート」ボタンを押すことでノートが作成されます。 ノートには、画像、動画など任意のファイルや、[アンケート](./poll)を添付することができます。また、本文中には[MFM](./mfm)が使用でき、[メンション](./mention)や[ハッシュタグ](./hashtag)を含めることもできます。 他にも、CWや公開範囲といった設定も行えます(詳細は後述)。 <div class="info">ℹ️ コンピューターのクリップボードに画像データがある状態で、フォーム内のテキストボックスにペーストするとその画像を添付することができます。</div> <div class="info">ℹ️ テキストボックス内で<kbd class="key">Ctrl + Enter</kbd>を押すことでも投稿できます。</div> -## Plusendi la noton +## Plusendi noton 既にあるノートを引用、もしくはそのノートを新しいノートとして共有する行為、またそれによって作成されたノートをRenoteと呼びます。 自分がフォローしているユーザーの、気に入ったノートを自分のフォロワーに共有したい場合や、過去の自分のノートを再度共有したい場合に使います。 同じノートに対して無制限にRenoteを行うことができますが、あまり連続して使用すると迷惑になる場合もあるので、注意しましょう。 -<div class="warn">⚠️ 公開範囲がフォロワーやダイレクトのノートはRenoteできません</div> +<div class="warn">⚠️ Se oni sendas notojn nur al sekvantoj aŭ rekte, iliaj ne estas plusendeblaj.</div> Renoteを削除するには、Renoteの時刻表示の隣にある「...」を押し、「Renote解除」を選択します。 @@ -31,10 +31,10 @@ Contents Warningの略で、ノートの内容を、閲覧者の操作なしに ### Hejma 全ての人に対してノートが公開されますが、フォロワー以外のローカルタイムライン、ソーシャルタイムライン、グローバルタイムラインにはノートは流れません。 -### Sekvantoj -自分のフォロワーに対してのみノートを公開します。フォロワーの全てのタイムラインに流れます。 +### Nur al sekvantoj +Viaj notoj estos senditaj nur al viaj sekvantoj.La noto aperos sur ĉiuj templinioj de viaj sekvantoj. -### Rekta +### Rekte 指定したユーザーに対してのみノートを公開します。指定したユーザーの全てのタイムラインに流れます。 ### 「ローカルのみ」オプション @@ -42,13 +42,13 @@ Contents Warningの略で、ノートの内容を、閲覧者の操作なしに ### 公開範囲の比較 <table> - <tr><th></th><th>Publika</th><th>Hejma</th><th>Sekvantoj</th><th>Rekta</th></tr> + <tr><th></th><th>Publika</th><th>Hejma</th><th>Nur al sekvantoj</th><th>Rekte</th></tr> <tr><th>フォロワーのLTL/STL/GTL</th><td>✔</td><td>✔</td><td>✔</td><td></td></tr> <tr><th>非フォロワーのLTL/STL/GTL</th><td>✔</td><td></td><td></td><td></td></tr> </table> -## Alpingli al la profilo +## Alpingli sur profilo ノートをピン留めすると、ユーザーページに常にそのノートを表示しておくことができます。 ノートのメニューを開き、「ピン留め」を選択してピン留めできます。 複数のノートをピン留めできます。 ## Observi -Vi povas ricevi sciigojn pri reagoj, respondoj, ktp al noto, kiuj ne apartenas al vi. Por observu, malfermu respektivan menuon de noto, kaj elektu la "Observi" el ĝi. +Vi povas ricevi tiuj sciigoj pri reagoj, respondoj, k.t.p al noto kiuj ne apartenas al vi estas ankaŭ ricevebla. Por komenci tion elektu la "Observi" el la menuon kuntekstan de la notoj respektivaj. diff --git a/src/docs/eo-UY/general/faq.md b/src/docs/eo-UY/general/faq.md index c272b2ad42..f7ede4c6cc 100644 --- a/src/docs/eo-UY/general/faq.md +++ b/src/docs/eo-UY/general/faq.md @@ -1,4 +1,4 @@ -# よくある質問 +# Oftaj demandoj ここでは利用上のよくある質問について掲載しています。 Misskeyのプロジェクト自体についてのよくある質問は[こちら](./misskey)に掲載されています。 ## iOS/Androidのアプリはありますか? diff --git a/src/docs/eo-UY/general/glossary.md b/src/docs/eo-UY/general/glossary.md index fe3b034181..25dd5f82c1 100644 --- a/src/docs/eo-UY/general/glossary.md +++ b/src/docs/eo-UY/general/glossary.md @@ -1,4 +1,4 @@ -# 用語集 +# Glosaro Misskeyに関する用語集です。 ## ActivityPub @@ -49,7 +49,7 @@ Ai estas oficiala maskoto de Misskey. ## Nodo todo -## Personecigitaj emoĵioj +## Emoĵioj personecigitaj サーバーで用意された絵文字。カスタム絵文字ではない通常の絵文字は「Unicode絵文字」と区別して呼ばれる。 ## Ŝaltpodio @@ -68,10 +68,10 @@ todo アカウントが使用不可に設定されている状態。 ## Disko -Misskeyにアップロードしたファイルを管理する機能。詳細は[こちら。](../features/drive) +Funkcio ebligas al uzantoj administri dosierojn kiujn ili alŝutis al Misskey.Rigardu por sciu pli tie[.](../features/drive) ## Notoj -Misskeyに投稿される、文章、ファイル、アンケートなどを含めることができるコンテンツ。Rigardu por sciu pli tie[.](../features/note) +Enpoŝtigaĵoj en Misskey kiuj konsistas el teksto, dosiero, balotujo, ktp.Rigardu por sciu pli tie[.](../features/note) ## Miskiisto Uzuloj de Misskey. @@ -82,7 +82,7 @@ Uzuloj de Misskey. ## Transa, Surloka 他サーバーのことを指します。リモートユーザーといったように接頭辞としても使われます。ローカルの逆です。 -## Kunfederado +## Federado サーバー上で作成された情報が他のサーバーに伝わること。 ## Loka diff --git a/src/docs/eo-UY/general/misskey.md b/src/docs/eo-UY/general/misskey.md index 506f7a8f24..ef287e5964 100644 --- a/src/docs/eo-UY/general/misskey.md +++ b/src/docs/eo-UY/general/misskey.md @@ -43,7 +43,7 @@ Misskeyはビジネスではなく、利用は無料であるため、収益は ## クレジット Misskeyの開発者や、Misskeyに寄付をしてくださった方の一覧は[こちら](/about-misskey)で見ることができます。 -## よくある質問 +## Oftaj demandoj ### プロジェクトは何を目指していますか? 強いて言うと、漠然的になりますが広く使われる汎用的なプラットフォームになることを目指しています。 Misskeyは他のプロジェクトとは違い、何らかの思想(例えば、反中央集権)やビジョンに基づいて開発が行われているわけではなく、その点ではフラットです。 それが逆に、特定の方向性に縛られないフレキシブルさを生み出すことに繋がっていると感じています。 <!-- TODO: ここにロードマップへのリンク --> diff --git a/src/docs/es-ES/general/troubleshooting.md b/src/docs/es-ES/general/troubleshooting.md index f895b49847..2bedfc3129 100644 --- a/src/docs/es-ES/general/troubleshooting.md +++ b/src/docs/es-ES/general/troubleshooting.md @@ -1,4 +1,4 @@ -# トラブルシューティング +# Solución de problemas <div class="info">ℹ️ <a href="./faq">よくある質問</a>も合わせてお役立てください。</div> 問題が発生したときは、まずこちらをご確認ください。 該当する項目が無い、もしくは手順を試しても効果がない場合は、サーバーの管理者に連絡するか[不具合を報告](./report-issue)してください。 diff --git a/src/docs/fr-FR/general/apps.md b/src/docs/fr-FR/general/apps.md index 7f9165a306..32a1274a59 100644 --- a/src/docs/fr-FR/general/apps.md +++ b/src/docs/fr-FR/general/apps.md @@ -1,6 +1,6 @@ # Liste des applications tierces -## クライアント +## Client todo -## 連携サービス +## Services connexes todo diff --git a/src/docs/fr-FR/general/glossary.md b/src/docs/fr-FR/general/glossary.md index 441a2b5bc4..d26d2e8ee6 100644 --- a/src/docs/fr-FR/general/glossary.md +++ b/src/docs/fr-FR/general/glossary.md @@ -1,65 +1,65 @@ -# 用語集 -Misskeyに関する用語集です。 +# Glossaire +Glossaire des termes utilisés dans Misskey. ## ActivityPub -(読み: あくてぃびてぃぱぶ) 分散型を実現するために用いられるプロトコル(仕様)。このプロトコルに則ってサーバー同士通信を行うことで、連合が行われ、Fediverseを形成しています。 +Nom du protocole (procédé technique) utilisé par Misskey pour pouvoir fonctionner comme service décentralisé. Ce protocole permet à tous les serveurs l'ayant adopté de communiquer entre eux et de former une sorte de fédération que l'on appelle « Fédiverse ». ## AiScript -(読み: あいすくりぷと) Misskey上で使用できるプログラミング言語です。詳細は[こちら。](../advanced/aiscript) +Langage de programmation qui peut être utilisé sur Misskey. [Voir ici pour plus d'informations.](../advanced/aiscript) ## API -(読み: えーぴーあい) Misskeyのサーバーが公開している、プログラムからMisskeyを扱うためのインターフェース。詳細は[こちら。](../advanced/api) +(読み: えーぴーあい) Misskeyのサーバーが公開している、プログラムからMisskeyを扱うためのインターフェース。[Voir ici pour plus d'informations. ](../advanced/api) ## Bot -(読み: ぼっと) プログラムによって動作しているアカウント。 +Anglicisme désignant un compte géré par un programme informatique (vous le trouverez parfois aussi sous le terme de « robot »). ## CW -(読み: こんてんつわーにんぐ) Contents Warningの略。ノートの内容を、操作なしには表示しないようにできる機能。主に長大な内容を隠すためや、ネタバレ防止などに使われます。 +Abréviation de « Content Warning » (qui signifie littéralement « alerte de contenu »). Fonctionnalité permettant d'assujettir l'affichage du contenu d'une note à une intervention de l'utilisateur·rice par le biais d'un bouton de masquage automatique. Principalement employée pour cacher le contenu des notes très longues, ou pour éviter de dévoiler publiquement des spoils potentiels, etc. -## Fediverse -(読み: ふぇでぃばーす) Misskeyを含む様々な分散型ソフトウェアのサーバーで構成されたネットワーク。 +## Fédiverse +Nom du réseau social fédéré qui rassemble une multitude d'instances appartenant à différents services et dont fait partie Misskey. -## GTL -グローバルタイムライン(Global TimeLine)の略。タイムラインの詳細は[こちら。](../features/timeline) +## FG +Abréviation de « Fil global ». Pour en savoir plus sur les différents fils, [voir ici.](../features/timeline) -## HTL -ホームタイムライン(Home TimeLine)の略。タイムラインの詳細は[こちら。](../features/timeline) +## FP +Abréviation de « Fil principal ». Pour en savoir plus sur les différents fils, [voir ici.](../features/timeline) -## LTL -ローカルタイムライン(Local TimeLine)の略。タイムラインの詳細は[こちら。](../features/timeline) +## FL +Abréviation de « Fil local ». Pour en savoir plus sur les différents fils, [voir ici.](../features/timeline) ## MFM -(読み: えむえふえむ) Misskey Flavored Markdownの略で、Misskey上で使用できるマークアップ言語です。詳細は[こちら。](../features/mfm) +Abréviation de « Misskey Flavored Markdown », un langage Markdown qui peut être utilisé sur Misskey. [Voir ici pour plus d'informations.](../features/mfm) ## NSFW -(読み: のっとせーふふぉーわーく) Not Safe For Workの略。画像を「閲覧注意」扱いにし、操作なしには表示しないようにすることができる機能。 +Abréviation de « Not Safe For Work » (qui signifie littéralement « pas sûr pour le travail »). Fonctionnalité permettant d'avertir que le contenu d'une note n'est pas approprié sur le lieu de travail et d'en assujettir l'affichage à une intervention de l'utilisateur·rice par le biais d'un bouton de masquage automatique. ## Renoter -(読み: りのーと) 既にあるノートを引用、もしくはそのノートを新しいノートとして共有する行為、またそれによって作成されたノート。詳細は[こちら。](../features/note) +Il s'agit du fait de citer une note existante ou de partager une note existante dans une nouvelle note. La note créée par l'un de ces deux biais est alors appelée une « Renote ». [Voir ici pour plus d'informations.](../features/note) -## STL -ソーシャルタイムライン(Social TimeLine)の略。タイムラインの詳細は[こちら。](../features/timeline) +## FS +Abréviation de « Fil social ». Pour en savoir plus sur les différents fils, [voir ici.](../features/timeline) -## 藍 -(読み: あい) Misskeyの看板娘(公式キャラクター)です。 +## Ai +Nom de la mascotte officielle de Misskey. (Le mot japonais, à prononcer « a-i », signifie littéralement « indigo »). -## アクティブユーザー -インスタンスにアカウントを作っているユーザーのうち、現在も実際にサービスを利用しているユーザーのこと。 +## Utilisateurices actif·ve·s +Désigne les utilisateur·rice·s, parmi tou·te·s celleux inscrit·e·s sur l'instance, qui utilisent effectivement leur compte au moment présent. ## Instance todo ## Émojis personnalisés -サーバーで用意された絵文字。カスタム絵文字ではない通常の絵文字は「Unicode絵文字」と区別して呼ばれる。 +Désigne les émojis mis à disposition par votre instance. Par opposition, les émojis disponibles par défaut (donc pas « personnalisés ») sont appelés « émojis unicode ». -## コントロールパネル -インスタンスの設定画面のこと。 +## Panneau de contrôle +Écran de contrôle des paramètres d'instance. ## Serveurs todo ## Mettre en sourdine -ノートをパブリックな公開範囲で投稿できなくされている状態。モデレーターの判断でユーザーごとに設定されます。詳細は[こちら。](../features/silence) +ノートをパブリックな公開範囲で投稿できなくされている状態。モデレーターの判断でユーザーごとに設定されます。[Voir ici pour plus d'informations. ](../features/silence) ## File d’attente アクティビティ配送などを順番に行うためのシステム。 @@ -68,15 +68,15 @@ todo アカウントが使用不可に設定されている状態。 ## Drive -Misskeyにアップロードしたファイルを管理する機能。詳細は[こちら。](../features/drive) +Fonctionnalité vous permettant de gérer les fichiers que vous avez téléversés sur Misskey. [Voir ici pour plus d'informations. ](../features/drive) ## Notes -Misskeyに投稿される、文章、ファイル、アンケートなどを含めることができるコンテンツ。詳細は[こちら。](../features/note) +Nom des publications sur Misskey. Leur contenu peut être du texte, mais aussi des fichiers, des enquêtes, etc. [Voir ici pour plus d'informations.](../features/note) -## ミスキスト -Misskeyを使う人のこと。 +## Misskeynaute +Désigne les utilisateur·rice·s de Misskey. -## Modérateurs +## Modérateur·rice·s スパムの凍結およびサイレンスや不適切な投稿の削除など、コミュニティ運営に関する権限を持つユーザー。 ## Distant diff --git a/src/docs/fr-FR/general/troubleshooting.md b/src/docs/fr-FR/general/troubleshooting.md index f6a6771df2..3abcf34006 100644 --- a/src/docs/fr-FR/general/troubleshooting.md +++ b/src/docs/fr-FR/general/troubleshooting.md @@ -21,20 +21,20 @@ Essayez les solutions proposées ci-dessous : - activer l'option « Réduire les animations dans l'interface » dans les paramètres du client - désactiver l'option « Utiliser un effet de flou pour les modals » dans les paramètres du client - activer l'accélération matérielle dans les paramètres de votre navigateur -- お使いのデバイスのスペックを上げる +- effectuer les mises à jour de votre appareil. -## UIの一部の表示がおかしい(背景が透明になっている等) -アップデートによりUIの改修が行われたときに、テーマのキャッシュシステムの影響でそのような表示になることがあります。 クライアントの設定の「キャッシュをクリア」すると直ります。 -<div class="warn">⚠️ 「クライアントの」キャッシュクリアです。「ブラウザの」キャッシュクリアは行わないでください。</div> +## Certaines parties de l'interface ne s'affichent pas correctement (arrière-plan transparent, etc.) +Cela peut être lié au système de mise en cache du thème lorsqu'une mise à jour visant à améliorer l'interface a eu lieu. Pour résoudre le problème, sélectionnez la touche « Vider le cache » dans les paramètres du client. +<div class="warn">⚠️ Attention de bien vider le cache du -client-... et pas celui du -navigateur- !</div> -## 通知やアンテナ等の点滅が消えない -点滅は、未読のコンテンツがあることを示しています。通常点滅が消えない場合は、コンテンツを遡ると未読なコンテンツが残っています。 すべて既読にしたと思われるのに、それでもなお点滅が続く場合(おそらく不具合と思われます)は設定から強制的にすべて既読扱いにすることができます。 +## Les pastilles de notification clignotantes ne disparaissent pas +Une pastille clignotante indique la présence de nouveau contenu que vous n'avez pas encore lu. Lorsque cette pastille ne disparaît pas, c'est généralement parce que du contenu laissé non lu a été repoussé par la réception de nouveau contenu. S'il s'avère que vous avez déjà lu le contenu dans sa totalité mais que la pastille continue tout de même de clignoter, il s'agit alors vraisemblablement d'un bug et vous pouvez forcer Misskey à tout marquer comme lu depuis vos paramètres généraux. ## La fonction « Renoter » ne fonctionne pas Les notes dont l'audience est limitée aux « Abonné·e·s uniquement » ne peuvent pas être renotées. ## Des éléments spécifiques de l'interface ne s'affichent pas -広告ブロッカーを使用しているとそのような不具合が発生することがあります。Misskeyではオフにしてご利用ください。 +Ce type de dysfonctionnement survient lorsque vous utilisez des bloqueurs de publicité. Désactivez-les pour profiter d'une expérience optimale sur Misskey. ## Certaines parties de l'interface ne sont pas traduites La plupart du temps, cela n'est pas un bug mais simplement un problème de traduction qui n'a pas encore été faite. Merci de patienter jusqu'à ce que la traduction de la portion en question soit achevée. Vous pouvez également [aider à traduire](./misskey) Misskey. diff --git a/src/docs/zh-CN/advanced/stream.md b/src/docs/zh-CN/advanced/stream.md index 090f8475ea..3351f2d839 100644 --- a/src/docs/zh-CN/advanced/stream.md +++ b/src/docs/zh-CN/advanced/stream.md @@ -50,7 +50,7 @@ MisskeyのストリーミングAPIにはチャンネルという概念があり } ``` -ここで、 +其中: * `channel`には接続したいチャンネル名を設定します。チャンネルの種類については後述します。 * `id`にはそのチャンネルとやり取りするための任意のIDを設定します。ストリームでは様々なメッセージが流れるので、そのメッセージがどのチャンネルからのものなのか識別する必要があるからです。このIDは、UUIDや、乱数のようなもので構いません。 * `params`はチャンネルに接続する際のパラメータです。チャンネルによって接続時に必要とされるパラメータは異なります。パラメータ不要のチャンネルに接続する際は、このプロパティは省略可能です。 @@ -74,7 +74,7 @@ MisskeyのストリーミングAPIにはチャンネルという概念があり } ``` -ここで、 +其中: * `id`には前述したそのチャンネルに接続する際に設定したIDが設定されています。これで、このメッセージがどのチャンネルからのものなのか知ることができます。 * `type`にはメッセージの種類が設定されます。チャンネルによって、どのような種類のメッセージが流れてくるかは異なります。 * `body`にはメッセージの内容が設定されます。チャンネルによって、どのような内容のメッセージが流れてくるかは異なります。 @@ -96,7 +96,7 @@ MisskeyのストリーミングAPIにはチャンネルという概念があり } ``` -ここで、 +其中: * `id`には前述したそのチャンネルに接続する際に設定したIDを設定します。これで、このメッセージがどのチャンネルに向けたものなのか識別させることができます。 * `type`にはメッセージの種類を設定します。チャンネルによって、どのような種類のメッセージを受け付けるかは異なります。 * `body`にはメッセージの内容を設定します。チャンネルによって、どのような内容のメッセージを受け付けるかは異なります。 @@ -113,7 +113,7 @@ MisskeyのストリーミングAPIにはチャンネルという概念があり } ``` -ここで、 +其中: * `id`には前述したそのチャンネルに接続する際に設定したIDを設定します。 ## ストリームを経由してAPIリクエストする @@ -134,7 +134,7 @@ MisskeyのストリーミングAPIにはチャンネルという概念があり } ``` -ここで、 +其中: * `id`には、APIのレスポンスを識別するための、APIリクエストごとの一意なIDを設定する必要があります。UUIDや、簡単な乱数のようなもので構いません。 * `endpoint`には、あなたがリクエストしたいAPIのエンドポイントを指定します。 * `data`には、エンドポイントのパラメータを含めます。 @@ -154,7 +154,7 @@ APIへリクエストすると、レスポンスがストリームから次の } ``` -ここで、 +其中: * `xxxxxxxxxxxxxxxx`の部分には、リクエストの際に設定された`id`が含まれています。これにより、どのリクエストに対するレスポンスなのか判別することができます。 * `body`には、レスポンスが含まれています。 @@ -181,7 +181,7 @@ Misskeyは投稿のキャプチャと呼ばれる仕組みを提供していま } ``` -ここで、 +其中: * `id`にキャプチャしたい投稿の`id`を設定します。 このメッセージを送信すると、Misskeyにキャプチャを要請したことになり、以後、その投稿に関するイベントが流れてくるようになります。 @@ -202,7 +202,7 @@ Misskeyは投稿のキャプチャと呼ばれる仕組みを提供していま } ``` -ここで、 +其中: * `body`内の`id`に、イベントを発生させた投稿のIDが設定されます。 * `body`内の`type`に、イベントの種類が設定されます。 * `body`内の`body`に、イベントの詳細が設定されます。 @@ -285,7 +285,7 @@ Misskeyは投稿のキャプチャと呼ばれる仕組みを提供していま } ``` -ここで、 +其中: * `id`にキャプチャを解除したい投稿の`id`を設定します。 このメッセージを送信すると、以後、その投稿に関するイベントは流れてこないようになります。 diff --git a/src/docs/zh-CN/features/follow.md b/src/docs/zh-CN/features/follow.md index 115a2786f6..2a95aa6dc4 100644 --- a/src/docs/zh-CN/features/follow.md +++ b/src/docs/zh-CN/features/follow.md @@ -1,2 +1,2 @@ -# 关注中 -ユーザーをフォローすると、タイムラインにそのユーザーの投稿が表示されるようになります。ただし、他のユーザーに対する返信は含まれません。 ユーザーをフォローするには、ユーザーページの「フォロー」ボタンをクリックします。フォローを解除するには、もう一度クリックします。 +# 关注 +当您关注一名用户时,您可以在您的时间线上看到该用户的发帖。但是不包含该用户对其他用户的回复。 要关注一名用户,请点击该用户页面上的“关注”按钮。如果需要取消关注,请再次点击该按钮。 diff --git a/src/docs/zh-CN/features/mfm.md b/src/docs/zh-CN/features/mfm.md index d42350edf0..8bcaacbed7 100644 --- a/src/docs/zh-CN/features/mfm.md +++ b/src/docs/zh-CN/features/mfm.md @@ -1,12 +1,12 @@ # MFM -MFMは、Misskey Flavored Markdownの略で、Misskeyの様々な場所で使用できる専用のマークアップ言語です。 MFMで使用可能な構文は[MFMチートシート](/mfm-cheat-sheet)で確認できます。 +MFM是Misskey Flavored Markdown的缩写,是一种专用的标记语言,可以用在Misskey的任何地方。 MFM中可用的语法可以在[MFM代码速查表](/mfm-cheat-sheet)中找到。 -## MFMが使用可能な場所の例 -- ノート本文 +## 使用 MFM 的位置示例 +- 帖子正文 - CW注释 -- ユーザーの名前 -- ユーザーの自己紹介 +- 用户姓名 +- 用户自我介绍 -## 開発者向け情報 -MFMのパーサー実装はライブラリとして公開されており、簡単にクライアントにMFMを組み込むことが可能です。 -- [misskey-dev/mfm.js](https://github.com/misskey-dev/mfm.js) - JavaScriptパーサー実装 +## 面向开发者的信息 +MFM 的解析器实现作为库发布,可以轻松地将 MFM 嵌入到客户端中。 +- [misskey-dev/mfm.js](https://github.com/misskey-dev/mfm.js) - JavaScript的解析器实现 From 3f688a728b59514da94e16764cf356c6b81931ad Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 16 Oct 2021 19:30:31 +0900 Subject: [PATCH 102/107] :art: --- src/client/components/ui/window.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/components/ui/window.vue b/src/client/components/ui/window.vue index 6a7c61a7d5..00284b0467 100644 --- a/src/client/components/ui/window.vue +++ b/src/client/components/ui/window.vue @@ -382,7 +382,7 @@ export default defineComponent({ <style lang="scss" scoped> .window-enter-active, .window-leave-active { - transition: opacity 0.3s, transform 0.3s !important; + transition: opacity 0.2s, transform 0.2s !important; } .window-enter-from, .window-leave-to { pointer-events: none; From 66470b49374628a1c16d57ad9a74286d5f836fd6 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 16 Oct 2021 19:38:47 +0900 Subject: [PATCH 103/107] update deps --- package.json | 32 ++-- yarn.lock | 475 ++++++++++++++++++++++++++------------------------- 2 files changed, 256 insertions(+), 251 deletions(-) diff --git a/package.json b/package.json index 308e6242c8..e23b574725 100644 --- a/package.json +++ b/package.json @@ -103,8 +103,8 @@ "@types/webpack-stream": "3.2.12", "@types/websocket": "1.0.4", "@types/ws": "8.2.0", - "@typescript-eslint/parser": "4.33.0", - "@vue/compiler-sfc": "3.2.19", + "@typescript-eslint/parser": "5.0.0", + "@vue/compiler-sfc": "3.2.20", "abort-controller": "3.0.0", "apexcharts": "3.28.3", "autobind-decorator": "2.4.0", @@ -114,8 +114,8 @@ "bcryptjs": "2.4.3", "blurhash": "1.1.4", "broadcast-channel": "4.2.0", - "bull": "3.29.2", - "cacheable-lookup": "6.0.2", + "bull": "3.29.3", + "cacheable-lookup": "6.0.3", "cafy": "15.2.1", "cbor": "8.0.2", "chalk": "4.1.2", @@ -125,11 +125,11 @@ "concurrently": "6.3.0", "content-disposition": "0.5.3", "crc-32": "1.2.0", - "css-loader": "6.3.0", + "css-loader": "6.4.0", "cssnano": "5.0.8", "dateformat": "4.5.1", "escape-regexp": "0.0.1", - "eslint": "7.32.0", + "eslint": "8.0.1", "eslint-plugin-vue": "7.19.1", "eventemitter3": "4.0.7", "feed": "4.2.2", @@ -176,15 +176,15 @@ "multer": "1.4.3", "nested-property": "4.0.0", "node-fetch": "2.6.1", - "nodemailer": "6.6.5", + "nodemailer": "6.7.0", "os-utils": "0.0.14", "parse5": "6.0.1", "pg": "8.7.1", "portscanner": "2.2.0", "postcss": "8.3.9", - "postcss-loader": "6.1.1", + "postcss-loader": "6.2.0", "prismjs": "1.25.0", - "private-ip": "2.2.1", + "private-ip": "2.3.0", "probe-image-size": "7.2.1", "promise-limit": "2.7.0", "pug": "3.0.2", @@ -203,8 +203,8 @@ "rimraf": "3.0.2", "rndstr": "1.0.0", "s-age": "1.1.2", - "sass": "1.42.1", - "sass-loader": "12.1.0", + "sass": "1.43.2", + "sass-loader": "12.2.0", "seedrandom": "3.0.5", "sharp": "0.29.1", "speakeasy": "2.0.0", @@ -212,7 +212,7 @@ "style-loader": "3.3.0", "summaly": "2.4.1", "syslog-pro": "1.0.0", - "systeminformation": "5.9.4", + "systeminformation": "5.9.7", "syuilo-password-strength": "0.0.1", "textarea-caret": "3.1.0", "three": "0.117.1", @@ -220,19 +220,19 @@ "tinycolor2": "1.4.2", "tmp": "0.2.1", "ts-loader": "9.2.6", - "ts-node": "10.2.1", + "ts-node": "10.3.0", "tsc-alias": "1.3.10", "tsconfig-paths": "3.11.0", "tslint": "6.1.3", "tslint-sonarts": "1.9.0", "twemoji-parser": "13.1.0", "typeorm": "0.2.38", - "typescript": "4.4.3", + "typescript": "4.4.4", "ulid": "2.3.0", "uuid": "8.3.2", "v-debounce": "0.1.2", "vanilla-tilt": "1.7.2", - "vue": "3.2.19", + "vue": "3.2.20", "vue-loader": "16.7.0", "vue-prism-editor": "2.0.0-alpha.2", "vue-router": "4.0.5", @@ -240,7 +240,7 @@ "vue-svg-loader": "0.17.0-beta.2", "vuedraggable": "4.0.1", "web-push": "3.4.5", - "webpack": "5.58.0", + "webpack": "5.58.2", "webpack-cli": "4.9.0", "websocket": "1.0.34", "ws": "8.2.3", diff --git a/yarn.lock b/yarn.lock index b5ef779125..cd76b59af6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,13 +2,6 @@ # yarn lockfile v1 -"@babel/code-frame@7.12.11": - version "7.12.11" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f" - integrity sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw== - dependencies: - "@babel/highlight" "^7.10.4" - "@babel/code-frame@^7.0.0": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.13.tgz#dcfc826beef65e75c50e21d3837d7d95798dd658" @@ -21,7 +14,7 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz#c9a1f021917dcb5ccf0d4e453e399022981fc9ed" integrity sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw== -"@babel/highlight@^7.10.4", "@babel/highlight@^7.12.13": +"@babel/highlight@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.12.13.tgz#8ab538393e00370b26271b01fa08f7f27f2e795c" integrity sha512-kocDQvIbgMKlWxXe9fof3TQ+gkIPOUSEYhJjqUjvKMez3krV7vbzYCDq39Oj11UAVK7JqPVGQPlgE85dPNlQww== @@ -61,10 +54,10 @@ resolved "https://registry.yarnpkg.com/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz#33bf4b7b39c178821606f669bbc447a6a629786b" integrity sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg== -"@cspotcode/source-map-support@0.6.1": - version "0.6.1" - resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.6.1.tgz#118511f316e2e87ee4294761868e254d3da47960" - integrity sha512-DX3Z+T5dt1ockmPdobJS/FAsQPW4V4SrWEhD2iYQT2Cb2tQsiMnYxrcUH9By/Z3B+v0S5LMBkQtV/XOBbpLEOg== +"@cspotcode/source-map-support@0.7.0": + version "0.7.0" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz#4789840aa859e46d2f3173727ab707c66bf344f5" + integrity sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA== dependencies: "@cspotcode/source-map-consumer" "0.8.0" @@ -131,14 +124,14 @@ pump "^3.0.0" secure-json-parse "^2.1.0" -"@eslint/eslintrc@^0.4.3": - version "0.4.3" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.3.tgz#9e42981ef035beb3dd49add17acb96e8ff6f394c" - integrity sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw== +"@eslint/eslintrc@^1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.0.3.tgz#41f08c597025605f672251dcc4e8be66b5ed7366" + integrity sha512-DHI1wDPoKCBPoLZA3qDR91+3te/wDSc1YhKg3jR8NxKKRJq2hwHwcWv31cSwSYvIBrmbENoYMWcenW8uproQqg== dependencies: ajv "^6.12.4" - debug "^4.1.1" - espree "^7.3.0" + debug "^4.3.2" + espree "^9.0.0" globals "^13.9.0" ignore "^4.0.6" import-fresh "^3.2.1" @@ -158,10 +151,10 @@ dependencies: "@hapi/hoek" "^9.0.0" -"@humanwhocodes/config-array@^0.5.0": - version "0.5.0" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.5.0.tgz#1407967d4c6eecd7388f83acf1eaf4d0c6e58ef9" - integrity sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg== +"@humanwhocodes/config-array@^0.6.0": + version "0.6.0" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.6.0.tgz#b5621fdb3b32309d2d16575456cbc277fa8f021a" + integrity sha512-JQlEKbcgEUjBFhLIF4iqM7u/9lwgHRBcpHrmUNCALK0Q3amXN6lxdoXLnF0sm11E9VqTmBALR87IlUg1bZ8A9A== dependencies: "@humanwhocodes/object-schema" "^1.2.0" debug "^4.1.1" @@ -1189,144 +1182,144 @@ resolved "https://registry.yarnpkg.com/@types/zen-observable/-/zen-observable-0.8.2.tgz#808c9fa7e4517274ed555fa158f2de4b4f468e71" integrity sha512-HrCIVMLjE1MOozVoD86622S7aunluLb2PJdPfb3nYiEtohm8mIB/vyv0Fd37AdeMFrTUQXEunw78YloMA3Qilg== -"@typescript-eslint/parser@4.33.0": - version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.33.0.tgz#dfe797570d9694e560528d18eecad86c8c744899" - integrity sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA== +"@typescript-eslint/parser@5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.0.0.tgz#50d1be2e0def82d73e863cceba74aeeac9973592" + integrity sha512-B6D5rmmQ14I1fdzs71eL3DAuvnPHTY/t7rQABrL9BLnx/H51Un8ox1xqYAchs0/V2trcoyxB1lMJLlrwrJCDgw== dependencies: - "@typescript-eslint/scope-manager" "4.33.0" - "@typescript-eslint/types" "4.33.0" - "@typescript-eslint/typescript-estree" "4.33.0" + "@typescript-eslint/scope-manager" "5.0.0" + "@typescript-eslint/types" "5.0.0" + "@typescript-eslint/typescript-estree" "5.0.0" debug "^4.3.1" -"@typescript-eslint/scope-manager@4.33.0": - version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.33.0.tgz#d38e49280d983e8772e29121cf8c6e9221f280a3" - integrity sha512-5IfJHpgTsTZuONKbODctL4kKuQje/bzBRkwHE8UOZ4f89Zeddg+EGZs8PD8NcN4LdM3ygHWYB3ukPAYjvl/qbQ== +"@typescript-eslint/scope-manager@5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.0.0.tgz#aea0fb0e2480c1169a02e89d9005ac3f2835713f" + integrity sha512-5RFjdA/ain/MDUHYXdF173btOKncIrLuBmA9s6FJhzDrRAyVSA+70BHg0/MW6TE+UiKVyRtX91XpVS0gVNwVDQ== dependencies: - "@typescript-eslint/types" "4.33.0" - "@typescript-eslint/visitor-keys" "4.33.0" + "@typescript-eslint/types" "5.0.0" + "@typescript-eslint/visitor-keys" "5.0.0" -"@typescript-eslint/types@4.33.0": - version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.33.0.tgz#a1e59036a3b53ae8430ceebf2a919dc7f9af6d72" - integrity sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ== +"@typescript-eslint/types@5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.0.0.tgz#25d93f6d269b2d25fdc51a0407eb81ccba60eb0f" + integrity sha512-dU/pKBUpehdEqYuvkojmlv0FtHuZnLXFBn16zsDmlFF3LXkOpkAQ2vrKc3BidIIve9EMH2zfTlxqw9XM0fFN5w== -"@typescript-eslint/typescript-estree@4.33.0": - version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz#0dfb51c2908f68c5c08d82aefeaf166a17c24609" - integrity sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA== +"@typescript-eslint/typescript-estree@5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.0.0.tgz#bc20f413c6e572c7309dbe5fa3be027984952af3" + integrity sha512-V/6w+PPQMhinWKSn+fCiX5jwvd1vRBm7AX7SJQXEGQtwtBvjMPjaU3YTQ1ik2UF1u96X7tsB96HMnulG3eLi9Q== dependencies: - "@typescript-eslint/types" "4.33.0" - "@typescript-eslint/visitor-keys" "4.33.0" + "@typescript-eslint/types" "5.0.0" + "@typescript-eslint/visitor-keys" "5.0.0" debug "^4.3.1" globby "^11.0.3" is-glob "^4.0.1" semver "^7.3.5" tsutils "^3.21.0" -"@typescript-eslint/visitor-keys@4.33.0": - version "4.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz#2a22f77a41604289b7a186586e9ec48ca92ef1dd" - integrity sha512-uqi/2aSz9g2ftcHWf8uLPJA70rUv6yuMW5Bohw+bwcuzaxQIHaKFZCKGoGXIrc9vkTJ3+0txM73K0Hq3d5wgIg== +"@typescript-eslint/visitor-keys@5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.0.0.tgz#b789f7cd105e59bee5c0983a353942a5a48f56df" + integrity sha512-yRyd2++o/IrJdyHuYMxyFyBhU762MRHQ/bAGQeTnN3pGikfh+nEmM61XTqaDH1XDp53afZ+waXrk0ZvenoZ6xw== dependencies: - "@typescript-eslint/types" "4.33.0" - eslint-visitor-keys "^2.0.0" + "@typescript-eslint/types" "5.0.0" + eslint-visitor-keys "^3.0.0" "@ungap/promise-all-settled@1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44" integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q== -"@vue/compiler-core@3.2.19": - version "3.2.19" - resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.2.19.tgz#b537dd377ce51fdb64e9b30ebfbff7cd70a64cb9" - integrity sha512-8dOPX0YOtaXol0Zf2cfLQ4NU/yHYl2H7DCKsLEZ7gdvPK6ZSEwGLJ7IdghhY2YEshEpC5RB9QKdC5I07z8Dtjg== +"@vue/compiler-core@3.2.20": + version "3.2.20" + resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.2.20.tgz#af5a3c5237818835b0d0be837eb5885a8d21c160" + integrity sha512-vcEXlKXoPwBXFP5aUTHN9GTZaDfwCofa9Yu9bbW2C5O/QSa9Esdt7OG4+0RRd3EHEMxUvEdj4RZrd/KpQeiJbA== dependencies: "@babel/parser" "^7.15.0" - "@vue/shared" "3.2.19" + "@vue/shared" "3.2.20" estree-walker "^2.0.2" source-map "^0.6.1" -"@vue/compiler-dom@3.2.19": - version "3.2.19" - resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.2.19.tgz#0607bc90de6af55fde73b09b3c4d0bf8cb597ed8" - integrity sha512-WzQoE8rfkFjPtIioc7SSgTsnz9g2oG61DU8KHnzPrRS7fW/lji6H2uCYJfp4Z6kZE8GjnHc1Ljwl3/gxDes0cw== +"@vue/compiler-dom@3.2.20": + version "3.2.20" + resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.2.20.tgz#8e0ef354449c0faf41519b00bfc2045eae01dcb5" + integrity sha512-QnI77ec/JtV7R0YBbcVayYTDCRcI9OCbxiUQK6izVyqQO0658n0zQuoNwe+bYgtqnvGAIqTR3FShTd5y4oOjdg== dependencies: - "@vue/compiler-core" "3.2.19" - "@vue/shared" "3.2.19" + "@vue/compiler-core" "3.2.20" + "@vue/shared" "3.2.20" -"@vue/compiler-sfc@3.2.19": - version "3.2.19" - resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.2.19.tgz#d412195a98ebd49b84602f171719294a1d9549be" - integrity sha512-pLlbgkO1UHTO02MSpa/sFOXUwIDxSMiKZ1ozE5n71CY4DM+YmI+G3gT/ZHZ46WBId7f3VTF/D8pGwMygcQbrQA== +"@vue/compiler-sfc@3.2.20": + version "3.2.20" + resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.2.20.tgz#2d7668e76f066c566dd7c09c15c9acce4e876e0a" + integrity sha512-03aZo+6tQKiFLfunHKSPZvdK4Jsn/ftRCyaro8AQIWkuxJbvSosbKK6HTTn+D2c3nPScG155akJoxKENw7rftQ== dependencies: "@babel/parser" "^7.15.0" - "@vue/compiler-core" "3.2.19" - "@vue/compiler-dom" "3.2.19" - "@vue/compiler-ssr" "3.2.19" - "@vue/ref-transform" "3.2.19" - "@vue/shared" "3.2.19" + "@vue/compiler-core" "3.2.20" + "@vue/compiler-dom" "3.2.20" + "@vue/compiler-ssr" "3.2.20" + "@vue/ref-transform" "3.2.20" + "@vue/shared" "3.2.20" estree-walker "^2.0.2" magic-string "^0.25.7" postcss "^8.1.10" source-map "^0.6.1" -"@vue/compiler-ssr@3.2.19": - version "3.2.19" - resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.2.19.tgz#3e91ecf70f8f961c5f63eacd2139bcdab9a7a07c" - integrity sha512-oLon0Cn3O7WEYzzmzZavGoqXH+199LT+smdjBT3Uf3UX4HwDNuBFCmvL0TsqV9SQnIgKvBRbQ7lhbpnd4lqM3w== +"@vue/compiler-ssr@3.2.20": + version "3.2.20" + resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.2.20.tgz#9cceb6261d9932cb5568202610c1c28f86c5e521" + integrity sha512-rzzVVYivm+EjbfiGQvNeyiYZWzr6Hkej97RZLZvcumacQlnKv9176Xo9rRyeWwFbBlxmtNdrVMslRXtipMXk2w== dependencies: - "@vue/compiler-dom" "3.2.19" - "@vue/shared" "3.2.19" + "@vue/compiler-dom" "3.2.20" + "@vue/shared" "3.2.20" -"@vue/reactivity@3.2.19": - version "3.2.19" - resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.2.19.tgz#fc6e0f0106f295226835cfed5ff5f84d927bea65" - integrity sha512-FtachoYs2SnyrWup5UikP54xDX6ZJ1s5VgHcJp4rkGoutU3Ry61jhs+nCX7J64zjX992Mh9gGUC0LqTs8q9vCA== +"@vue/reactivity@3.2.20": + version "3.2.20" + resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.2.20.tgz#81fe1c368e7f20bc0ec1dec1045bbee253582de8" + integrity sha512-nSmoLojUTk+H8HNTAkrUduB4+yIUBK2HPihJo2uXVSH4Spry6oqN6lFzE5zpLK+F27Sja+UqR9R1+/kIOsHV5w== dependencies: - "@vue/shared" "3.2.19" + "@vue/shared" "3.2.20" -"@vue/ref-transform@3.2.19": - version "3.2.19" - resolved "https://registry.yarnpkg.com/@vue/ref-transform/-/ref-transform-3.2.19.tgz#cf0f986486bb26838fbd09749e927bab19745600" - integrity sha512-03wwUnoIAeKti5IGGx6Vk/HEBJ+zUcm5wrUM3+PQsGf7IYnXTbeIfHHpx4HeSeWhnLAjqZjADQwW8uA4rBmVbg== +"@vue/ref-transform@3.2.20": + version "3.2.20" + resolved "https://registry.yarnpkg.com/@vue/ref-transform/-/ref-transform-3.2.20.tgz#2a59ec90caf8e5c7336776a0900bff0a8b81c090" + integrity sha512-Y42d3PGlYZ1lXcF3dbd3+qU/C/a3wYEZ949fyOI5ptzkjDWlkfU6vn74fmOjsLjEcjs10BXK2qO99FqQIK2r1Q== dependencies: "@babel/parser" "^7.15.0" - "@vue/compiler-core" "3.2.19" - "@vue/shared" "3.2.19" + "@vue/compiler-core" "3.2.20" + "@vue/shared" "3.2.20" estree-walker "^2.0.2" magic-string "^0.25.7" -"@vue/runtime-core@3.2.19": - version "3.2.19" - resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.2.19.tgz#807715b7f4728abb84fa4a8efdbe37d8ddb4c6d3" - integrity sha512-qArZSWKxWsgKfxk9BelZ32nY0MZ31CAW2kUUyVJyxh4cTfHaXGbjiQB5JgsvKc49ROMNffv9t3/qjasQqAH+RQ== +"@vue/runtime-core@3.2.20": + version "3.2.20" + resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.2.20.tgz#8f63e956a3f88fb772541443c45a7701211012cb" + integrity sha512-d1xfUGhZPfiZzAN7SatStD4vRtT8deJSXib2+Cz3x0brjMWKxe32asQc154FF1E2fFgMCHtnfd4A90bQEzV4GQ== dependencies: - "@vue/reactivity" "3.2.19" - "@vue/shared" "3.2.19" + "@vue/reactivity" "3.2.20" + "@vue/shared" "3.2.20" -"@vue/runtime-dom@3.2.19": - version "3.2.19" - resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.2.19.tgz#7e8bf645754703e360fa132e4be9113edf2377bb" - integrity sha512-hIRboxXwafeHhbZEkZYNV0MiJXPNf4fP0X6hM2TJb0vssz8BKhD9cF92BkRgZztTQevecbhk0gu4uAPJ3dxL9A== +"@vue/runtime-dom@3.2.20": + version "3.2.20" + resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.2.20.tgz#8aa56ae6c30f9cd4a71ca0e9ec3c4bdc67148d15" + integrity sha512-4TCvZMLhESWCFHFYgqN4QmMA/onnINAlUovhopjlS8ST27G1A8Z0tyxPzLoXLa+b5JrOpbMPheEMPvdKExTJig== dependencies: - "@vue/runtime-core" "3.2.19" - "@vue/shared" "3.2.19" + "@vue/runtime-core" "3.2.20" + "@vue/shared" "3.2.20" csstype "^2.6.8" -"@vue/server-renderer@3.2.19": - version "3.2.19" - resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.2.19.tgz#870bcec9f7cdaee0c2187a169b6e636ab4362fb1" - integrity sha512-A9FNT7fgQJXItwdzWREntAgWKVtKYuXHBKGev/H4+ByTu8vB7gQXGcim01QxaJshdNg4dYuH2tEBZXCNCNx+/w== +"@vue/server-renderer@3.2.20": + version "3.2.20" + resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.2.20.tgz#705e07ae9425132b2b6227d308a51a13f4d4ec81" + integrity sha512-viIbZGep9XabnrRcaxWIi00cOh1x21QYm2upIL5W0zqzTJ54VdTzpI+zi1osNp+VfRQDTHpV2U7H3Kn4ljYJvg== dependencies: - "@vue/compiler-ssr" "3.2.19" - "@vue/shared" "3.2.19" + "@vue/compiler-ssr" "3.2.20" + "@vue/shared" "3.2.20" -"@vue/shared@3.2.19": - version "3.2.19" - resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.2.19.tgz#111ec3da18337d86274446984c49925b1b2b2dd7" - integrity sha512-Knqhx7WieLdVgwCAZgTVrDCXZ50uItuecLh9JdLC8O+a5ayaSyIQYveUK3hCRNC7ws5zalHmZwfdLMGaS8r4Ew== +"@vue/shared@3.2.20": + version "3.2.20" + resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.2.20.tgz#53746961f731a8ea666e3316271e944238dc31db" + integrity sha512-FbpX+hD5BvXCQerEYO7jtAGHlhAkhTQ4KIV73kmLWNlawWhTiVuQxizgVb0BOkX5oG9cIRZ42EG++d/k/Efp0w== "@webassemblyjs/ast@1.11.0": version "1.11.0" @@ -1655,7 +1648,7 @@ acorn-walk@^8.1.1: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.1.1.tgz#3ddab7f84e4a7e2313f6c414c5b7dac85f4e3ebc" integrity sha512-FbJdceMlPHEAWJOILDk1fXD8lnTlEIWFkqtfk+MvmL5q/qlHfN7GEHcsFZWt/Tea9jRNPWUZG4G976nqAAmU9w== -acorn@^7.1.1, acorn@^7.4.0: +acorn@^7.1.1: version "7.4.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== @@ -1675,6 +1668,11 @@ acorn@^8.4.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.4.1.tgz#56c36251fc7cabc7096adc18f05afe814321a28c" integrity sha512-asabaBSkEKosYKMITunzX177CXxQ4Q8BSSzMTKD+FefUhipQC70gfW5SiUDhYQ3vk8G+81HqQk7Fv9OXwwn9KA== +acorn@^8.5.0: + version "8.5.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.5.0.tgz#4512ccb99b3698c752591e9bb4472e38ad43cee2" + integrity sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q== + agent-base@6: version "6.0.2" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" @@ -1714,16 +1712,6 @@ ajv@^6.10.0, ajv@^6.12.4, ajv@^6.12.5, ajv@^6.5.5: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^8.0.1: - version "8.5.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.5.0.tgz#695528274bcb5afc865446aa275484049a18ae4b" - integrity sha512-Y2l399Tt1AguU3BPRP9Fn4eN+Or+StUGWCUpbnFyXSo8NZ9S4uj+AG2pjs5apK+ZMOwYOz1+a+VKvKH7CudXgQ== - dependencies: - fast-deep-equal "^3.1.1" - json-schema-traverse "^1.0.0" - require-from-string "^2.0.2" - uri-js "^4.2.2" - alphanum-sort@^1.0.1, alphanum-sort@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" @@ -2422,10 +2410,10 @@ builtin-modules@^1.1.1: resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8= -bull@3.29.2: - version "3.29.2" - resolved "https://registry.yarnpkg.com/bull/-/bull-3.29.2.tgz#30051fd14c7214b1e90c212585674f77fd982650" - integrity sha512-zWHyza/ElwVvJUqIEDJdUhGKd1V9EHjituUL7sJAmJoxS9Z7QMhYcMOWcgbUlWPgtiKN1g9ZlOtFAoq7C4/SQw== +bull@3.29.3: + version "3.29.3" + resolved "https://registry.yarnpkg.com/bull/-/bull-3.29.3.tgz#5b0059b172685b0d6f011d56214e1898ff3a7a0b" + integrity sha512-MOqV1dKLy1YQgP9m3lFolyMxaU+1+o4afzYYf0H4wNM+x/S0I1QPQfkgGlLiH00EyFrvSmeubeCYFP47rTfpjg== dependencies: cron-parser "^2.13.0" debuglog "^1.0.0" @@ -2497,10 +2485,10 @@ cache-content-type@^1.0.0: mime-types "^2.1.18" ylru "^1.2.0" -cacheable-lookup@6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-6.0.2.tgz#8df03d6239c91bb9f6394700d7ba4a100abbad67" - integrity sha512-9RJkUl1k/A1dFhaRfrEUdISvvou0WKx8LboMO0j1BpsqgAuolwZgwaEtn0dmFMk5HQxpFtHF1bHCnIQMywUpvw== +cacheable-lookup@6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-6.0.3.tgz#61d6171f6818fab230666b11f7cf3f5a48df7818" + integrity sha512-xdwIK7MEC8NpRIt0dx2PL7pTRKaSmDb+zirzuM+cJTRWDfwfVu4XyASkODIU4XbjsyFHKo/tDOPSs64Z3yfFWg== cacheable-lookup@^5.0.3: version "5.0.3" @@ -3325,10 +3313,10 @@ css-declaration-sorter@^6.0.3: dependencies: timsort "^0.3.0" -css-loader@6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.3.0.tgz#334d3500ff0a0c14cfbd4b0670088dbb5b5c1530" - integrity sha512-9NGvHOR+L6ps13Ilw/b216++Q8q+5RpJcVufCdW9S/9iCzs4KBDNa8qnA/n3FK/sSfWmH35PAIK/cfPi7LOSUg== +css-loader@6.4.0: + version "6.4.0" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.4.0.tgz#01c57ea776024e18ca193428dcad3ff6b42a0130" + integrity sha512-Dlt6qfsxI/w1vU0r8qDd4BtMPxWqJeY5qQU7SmmZfvbpe6Xl18McO4GhyaMLns24Y2VNPiZwJPQ8JSbg4qvQLw== dependencies: icss-utils "^5.1.0" postcss "^8.2.15" @@ -4280,6 +4268,14 @@ eslint-scope@5.1.1, eslint-scope@^5.1.1: esrecurse "^4.3.0" estraverse "^4.1.1" +eslint-scope@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-6.0.0.tgz#9cf45b13c5ac8f3d4c50f46a5121f61b3e318978" + integrity sha512-uRDL9MWmQCkaFus8RF5K9/L/2fn+80yoW3jkD53l4shjCh26fCtvJGasxjUqP5OT87SYTxCVA3BwTUzuELx9kA== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + eslint-utils@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" @@ -4287,7 +4283,14 @@ eslint-utils@^2.1.0: dependencies: eslint-visitor-keys "^1.1.0" -eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0: +eslint-utils@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672" + integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== + dependencies: + eslint-visitor-keys "^2.0.0" + +eslint-visitor-keys@^1.1.0: version "1.3.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== @@ -4297,37 +4300,41 @@ eslint-visitor-keys@^2.0.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8" integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ== -eslint@7.32.0: - version "7.32.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.32.0.tgz#c6d328a14be3fb08c8d1d21e12c02fdb7a2a812d" - integrity sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA== +eslint-visitor-keys@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.0.0.tgz#e32e99c6cdc2eb063f204eda5db67bfe58bb4186" + integrity sha512-mJOZa35trBTb3IyRmo8xmKBZlxf+N7OnUl4+ZhJHs/r+0770Wh/LEACE2pqMGMe27G/4y8P2bYGk4J70IC5k1Q== + +eslint@8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.0.1.tgz#3610e7fe4a05c2154669515ca60835a76a19f700" + integrity sha512-LsgcwZgQ72vZ+SMp4K6pAnk2yFDWL7Ti4pJaRvsZ0Hsw2h8ZjUIW38a9AFn2cZXdBMlScMFYYgsSp4ttFI/0bA== dependencies: - "@babel/code-frame" "7.12.11" - "@eslint/eslintrc" "^0.4.3" - "@humanwhocodes/config-array" "^0.5.0" + "@eslint/eslintrc" "^1.0.3" + "@humanwhocodes/config-array" "^0.6.0" ajv "^6.10.0" chalk "^4.0.0" cross-spawn "^7.0.2" - debug "^4.0.1" + debug "^4.3.2" doctrine "^3.0.0" enquirer "^2.3.5" escape-string-regexp "^4.0.0" - eslint-scope "^5.1.1" - eslint-utils "^2.1.0" - eslint-visitor-keys "^2.0.0" - espree "^7.3.1" + eslint-scope "^6.0.0" + eslint-utils "^3.0.0" + eslint-visitor-keys "^3.0.0" + espree "^9.0.0" esquery "^1.4.0" esutils "^2.0.2" fast-deep-equal "^3.1.3" file-entry-cache "^6.0.1" functional-red-black-tree "^1.0.1" - glob-parent "^5.1.2" + glob-parent "^6.0.1" globals "^13.6.0" ignore "^4.0.6" import-fresh "^3.0.0" imurmurhash "^0.1.4" is-glob "^4.0.0" - js-yaml "^3.13.1" + js-yaml "^4.1.0" json-stable-stringify-without-jsonify "^1.0.1" levn "^0.4.1" lodash.merge "^4.6.2" @@ -4335,11 +4342,10 @@ eslint@7.32.0: natural-compare "^1.4.0" optionator "^0.9.1" progress "^2.0.0" - regexpp "^3.1.0" + regexpp "^3.2.0" semver "^7.2.1" strip-ansi "^6.0.0" strip-json-comments "^3.1.0" - table "^6.0.9" text-table "^0.2.0" v8-compile-cache "^2.0.3" @@ -4357,14 +4363,14 @@ espree@^6.2.1: acorn-jsx "^5.2.0" eslint-visitor-keys "^1.1.0" -espree@^7.3.0, espree@^7.3.1: - version "7.3.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.1.tgz#f2df330b752c6f55019f8bd89b7660039c1bbbb6" - integrity sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g== +espree@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.0.0.tgz#e90a2965698228502e771c7a58489b1a9d107090" + integrity sha512-r5EQJcYZ2oaGbeR0jR0fFVijGOcwai07/690YRXLINuhmVeRY4UKSAsQPe/0BNuDgwP7Ophoc1PRsr2E3tkbdQ== dependencies: - acorn "^7.4.0" + acorn "^8.5.0" acorn-jsx "^5.3.1" - eslint-visitor-keys "^1.3.0" + eslint-visitor-keys "^3.0.0" esprima@^2.6.0: version "2.7.3" @@ -5043,12 +5049,12 @@ glob-parent@^5.1.0, glob-parent@~5.1.0: dependencies: is-glob "^4.0.1" -glob-parent@^5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" - integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== +glob-parent@^6.0.1: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== dependencies: - is-glob "^4.0.1" + is-glob "^4.0.3" glob-stream@^6.1.0: version "6.1.0" @@ -5763,7 +5769,7 @@ ip-cidr@3.0.4: ip-address "^7.1.0" jsbn "^1.1.0" -ip-regex@^4.3.0: +ip-regex@^4.0.0, ip-regex@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-4.3.0.tgz#687275ab0f57fa76978ff8f4dddc8a23d5990db5" integrity sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q== @@ -5773,6 +5779,11 @@ ip@^1.1.5: resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= +ipaddr.js@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.0.1.tgz#eca256a7a877e917aeb368b0a7497ddf42ef81c0" + integrity sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng== + is-absolute-url@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6" @@ -5951,6 +5962,13 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" +is-glob@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + is-installed-globally@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.4.0.tgz#9a0fd407949c30f86eb6959ef1b7994ed0b7b520" @@ -5959,6 +5977,13 @@ is-installed-globally@~0.4.0: global-dirs "^3.0.0" is-path-inside "^3.0.2" +is-ip@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-ip/-/is-ip-3.1.0.tgz#2ae5ddfafaf05cb8008a62093cf29734f657c5d8" + integrity sha512-35vd5necO7IitFPjd/YBeqwWnyDWbuLH9ZXQdMfDA8TEo7pv5X8yfrvVO3xbJbLUlERCMvf6X0hTUamQxCYJ9Q== + dependencies: + ip-regex "^4.0.0" + is-lambda@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5" @@ -6241,7 +6266,7 @@ js-yaml@4.0.0: dependencies: argparse "^2.0.1" -js-yaml@4.1.0, js-yaml@^4.0.0: +js-yaml@4.1.0, js-yaml@^4.0.0, js-yaml@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== @@ -6838,11 +6863,6 @@ lodash.bind@^4.1.4: resolved "https://registry.yarnpkg.com/lodash.bind/-/lodash.bind-4.2.1.tgz#7ae3017e939622ac31b7d7d7dcb1b34db1690d35" integrity sha1-euMBfpOWIqwxt9fX3LGzTbFpDTU= -lodash.clonedeep@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" - integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= - lodash.defaults@^4.0.1, lodash.defaults@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" @@ -6923,11 +6943,6 @@ lodash.sortby@^4.7.0: resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= -lodash.truncate@^4.4.2: - version "4.4.2" - resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" - integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM= - lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" @@ -7560,10 +7575,10 @@ node-releases@^1.1.70, node-releases@^1.1.71: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.71.tgz#cb1334b179896b1c89ecfdd4b725fb7bbdfc7dbb" integrity sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg== -nodemailer@6.6.5: - version "6.6.5" - resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.6.5.tgz#f9f6953cee5cfe82cbea152eeddacf7a0442049a" - integrity sha512-C/v856DBijUzHcHIgGpQoTrfsH3suKIRAGliIzCstatM2cAa+MYX3LuyCrABiO/cdJTxgBBHXxV1ztiqUwst5A== +nodemailer@6.7.0: + version "6.7.0" + resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.7.0.tgz#86614722c4e0c33d1b5b02aecb90d6d629932b0d" + integrity sha512-AtiTVUFHLiiDnMQ43zi0YgkzHOEWUkhDgPlBXrsDzJiJvB29Alo4OKxHQ0ugF3gRqRQIneCLtZU3yiUo7pItZw== nofilter@^2.0.3: version "2.0.3" @@ -8398,10 +8413,10 @@ postcss-filter-plugins@^2.0.0: dependencies: postcss "^5.0.4" -postcss-loader@6.1.1: - version "6.1.1" - resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-6.1.1.tgz#58dd0a3accd9bc87cc52eff75244db578d11301a" - integrity sha512-lBmJMvRh1D40dqpWKr9Rpygwxn8M74U9uaCSeYGNKLGInbk9mXBt1ultHf2dH9Ghk6Ue4UXlXWwGMH9QdUJ5ug== +postcss-loader@6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-6.2.0.tgz#714370a3f567141cf4cadcdf9575f5234d186bc5" + integrity sha512-H9hv447QjQJVDbHj3OUdciyAXY3v5+UDduzEytAlZCVHCpNAAg/mCSwhYYqZr9BiGYhmYspU8QXxZwiHTLn3yA== dependencies: cosmiconfig "^7.0.0" klona "^2.0.4" @@ -8911,12 +8926,14 @@ prismjs@1.25.0: resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.25.0.tgz#6f822df1bdad965734b310b315a23315cf999756" integrity sha512-WCjJHl1KEWbnkQom1+SzftbtXMKQoezOCYs5rECqMN+jP+apI7ftoflyqigqzopSO3hMhTEb0mFClA8lkolgEg== -private-ip@2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/private-ip/-/private-ip-2.2.1.tgz#4fe167d04e12eca5c67cdcbd3224e86b38c79253" - integrity sha512-jN1WT/br/VNW9xEcwHr6DjtOKxQ5qOIqmh7o+co2TWgq56pZJw99iO3UT1tWdfgsQiyK9FqG4ji3ykwpjFqITA== +private-ip@2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/private-ip/-/private-ip-2.3.0.tgz#aa07bf623c60ea75ee5d140814f492648c001717" + integrity sha512-3lTFzg+Z1Q2VNQChw8AW2c5LM7getyUJ67SqeWtvnkUO4gihjbf5oIEK4+jAQ4v1sCqOVV7r+GzKrgowW/W3tw== dependencies: ip-regex "^4.3.0" + ipaddr.js "^2.0.1" + is-ip "^3.1.0" netmask "^2.0.2" probe-image-size@7.2.1: @@ -9411,10 +9428,10 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" -regexpp@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2" - integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q== +regexpp@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" + integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== remove-bom-buffer@^3.0.0: version "3.0.0" @@ -9739,18 +9756,18 @@ safe-regex@^1.1.0: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -sass-loader@12.1.0: - version "12.1.0" - resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-12.1.0.tgz#b73324622231009da6fba61ab76013256380d201" - integrity sha512-FVJZ9kxVRYNZTIe2xhw93n3xJNYZADr+q69/s98l9nTCrWASo+DR2Ot0s5xTKQDDEosUkatsGeHxcH4QBp5bSg== +sass-loader@12.2.0: + version "12.2.0" + resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-12.2.0.tgz#b370010fb0ababae2ef9c6c89e05d6c6debc6042" + integrity sha512-qducnp5vSV+8A8MZxuH6zV0MUg4MOVISScl2wDTCAn/2WJX+9Auxh92O/rnkdR2bvi5QxZBafnzkzRrWGZvm7w== dependencies: klona "^2.0.4" neo-async "^2.6.2" -sass@1.42.1: - version "1.42.1" - resolved "https://registry.yarnpkg.com/sass/-/sass-1.42.1.tgz#5ab17bebc1cb1881ad2e0c9a932c66ad64e441e2" - integrity sha512-/zvGoN8B7dspKc5mC6HlaygyCBRvnyzzgD5khiaCfglWztY99cYoiTUksVx11NlnemrcfH5CEaCpsUKoW0cQqg== +sass@1.43.2: + version "1.43.2" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.43.2.tgz#c02501520c624ad6622529a8b3724eb08da82d65" + integrity sha512-DncYhjl3wBaPMMJR0kIUaH3sF536rVrOcqqVGmTZHQRRzj7LQlyGV7Mb8aCKFyILMr5VsPHwRYtyKpnKYlmQSQ== dependencies: chokidar ">=3.0.0 <4.0.0" @@ -10615,28 +10632,16 @@ syslog-pro@1.0.0: dependencies: moment "^2.22.2" -systeminformation@5.9.4: - version "5.9.4" - resolved "https://registry.yarnpkg.com/systeminformation/-/systeminformation-5.9.4.tgz#1f0e29e0aa376dec8f69cc517eeefc5cdcda411a" - integrity sha512-FOsiTn0CyJZoj9kIhla11ndsMzbbwwuriul81wpqIBt9IpbxHZ6P/oZCphIFgJrwqjTnme0Qp1HDzIkUD9Xr/g== +systeminformation@5.9.7: + version "5.9.7" + resolved "https://registry.yarnpkg.com/systeminformation/-/systeminformation-5.9.7.tgz#8a5041d3aa1518e962b17740e20a3c75ff390e6d" + integrity sha512-Vcmc8HaWPMFM4DoasuKN2lpvIwS2AqaoPuEGZc4HCT6tlRJH+IQ5GhA2BrUgjpBDJjFMj2Bti6qLOzP3T1arCw== syuilo-password-strength@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/syuilo-password-strength/-/syuilo-password-strength-0.0.1.tgz#08f71a8f0ecb77db649f3d9a6424510d9d945f52" integrity sha1-CPcajw7Ld9tknz2aZCRRDZ2UX1I= -table@^6.0.9: - version "6.7.1" - resolved "https://registry.yarnpkg.com/table/-/table-6.7.1.tgz#ee05592b7143831a8c94f3cee6aae4c1ccef33e2" - integrity sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg== - dependencies: - ajv "^8.0.1" - lodash.clonedeep "^4.5.0" - lodash.truncate "^4.4.2" - slice-ansi "^4.0.0" - string-width "^4.2.0" - strip-ansi "^6.0.0" - tapable@^2.1.1, tapable@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.0.tgz#5c373d281d9c672848213d0e037d1c4165ab426b" @@ -10967,12 +10972,12 @@ ts-loader@9.2.6: micromatch "^4.0.0" semver "^7.3.4" -ts-node@10.2.1: - version "10.2.1" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.2.1.tgz#4cc93bea0a7aba2179497e65bb08ddfc198b3ab5" - integrity sha512-hCnyOyuGmD5wHleOQX6NIjJtYVIO8bPP8F2acWkB4W06wdlkgyvJtubO/I9NkI88hCFECbsEgoLc0VNkYmcSfw== +ts-node@10.3.0: + version "10.3.0" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.3.0.tgz#a797f2ed3ff50c9a5d814ce400437cb0c1c048b4" + integrity sha512-RYIy3i8IgpFH45AX4fQHExrT8BxDeKTdC83QFJkNzkvt8uFB6QJ8XMyhynYiKMLxt9a7yuXaDBZNOYS3XjDcYw== dependencies: - "@cspotcode/source-map-support" "0.6.1" + "@cspotcode/source-map-support" "0.7.0" "@tsconfig/node10" "^1.0.7" "@tsconfig/node12" "^1.0.7" "@tsconfig/node14" "^1.0.0" @@ -11182,10 +11187,10 @@ typeorm@0.2.38: yargs "^17.0.1" zen-observable-ts "^1.0.0" -typescript@4.4.3: - version "4.4.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.4.3.tgz#bdc5407caa2b109efd4f82fe130656f977a29324" - integrity sha512-4xfscpisVgqqDfPaJo5vkd+Qd/ItkoagnHpufr+i2QCHBsNYp+G7UAoyFl8aPtx879u38wPV65rZ8qbGZijalA== +typescript@4.4.4: + version "4.4.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.4.4.tgz#2cd01a1a1f160704d3101fd5a58ff0f9fcb8030c" + integrity sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA== uc.micro@^1.0.1, uc.micro@^1.0.5: version "1.0.6" @@ -11553,16 +11558,16 @@ vue-svg-loader@0.17.0-beta.2: semver "^7.3.2" svgo "^1.3.2" -vue@3.2.19: - version "3.2.19" - resolved "https://registry.yarnpkg.com/vue/-/vue-3.2.19.tgz#da2c80a6a0271c7097fee9e31692adfd9d569c8f" - integrity sha512-6KAMdIfAtlK+qohTIUE4urwAv4A3YRuo8uAbByApUmiB0CziGAAPs6qVugN6oHPia8YIafHB/37K0O6KZ7sGmA== +vue@3.2.20: + version "3.2.20" + resolved "https://registry.yarnpkg.com/vue/-/vue-3.2.20.tgz#940f8aa8bf3e3be78243ca582bad41fcd45ae3e6" + integrity sha512-81JjEP4OGk9oO8+CU0h2nFPGgJBm9mNa3kdCX2k6FuRdrWrC+CNe+tOnuIeTg8EWwQuI+wwdra5Q7vSzp7p4Iw== dependencies: - "@vue/compiler-dom" "3.2.19" - "@vue/compiler-sfc" "3.2.19" - "@vue/runtime-dom" "3.2.19" - "@vue/server-renderer" "3.2.19" - "@vue/shared" "3.2.19" + "@vue/compiler-dom" "3.2.20" + "@vue/compiler-sfc" "3.2.20" + "@vue/runtime-dom" "3.2.20" + "@vue/server-renderer" "3.2.20" + "@vue/shared" "3.2.20" vuedraggable@4.0.1: version "4.0.1" @@ -11674,10 +11679,10 @@ webpack-sources@^3.2.0: resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.0.tgz#b16973bcf844ebcdb3afde32eda1c04d0b90f89d" integrity sha512-fahN08Et7P9trej8xz/Z7eRu8ltyiygEo/hnRi9KqBUs80KeDcnf96ZJo++ewWd84fEf3xSX9bp4ZS9hbw0OBw== -webpack@5.58.0: - version "5.58.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.58.0.tgz#9ec621cf8534f23c25e779e7c35dfde1211d5ccb" - integrity sha512-xc2k5MLbR1iah24Z5xUm1nBh1PZXEdUnrX6YkTSOScq/VWbl5JCLREXJzGYqEAUbIO8tZI+Dzv82lGtnuUnVCQ== +webpack@5.58.2: + version "5.58.2" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.58.2.tgz#6b4af12fc9bd5cbedc00dc0a2fc2b9592db16b44" + integrity sha512-3S6e9Vo1W2ijk4F4PPWRIu6D/uGgqaPmqw+av3W3jLDujuNkdxX5h5c+RQ6GkjVR+WwIPOfgY8av+j5j4tMqJw== dependencies: "@types/eslint-scope" "^3.7.0" "@types/estree" "^0.0.50" From 133936652d24f9a934ac70972d4c1cee9fde000d Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 16 Oct 2021 19:38:51 +0900 Subject: [PATCH 104/107] :art: --- src/client/ui/default.header.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/ui/default.header.vue b/src/client/ui/default.header.vue index c64b6cf695..4f6363e82d 100644 --- a/src/client/ui/default.header.vue +++ b/src/client/ui/default.header.vue @@ -29,7 +29,7 @@ <MkAvatar :user="$i" class="avatar"/><MkAcct class="acct" :user="$i"/> </button> <div class="post" @click="post"> - <MkButton class="button" primary full rounded> + <MkButton class="button" gradate full rounded> <i class="fas fa-pencil-alt fa-fw"></i> </MkButton> </div> From 73cdf5ca81c92a404918b434ca7436be1bbfb2a5 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 16 Oct 2021 19:44:32 +0900 Subject: [PATCH 105/107] Update docker-develop.yml --- .github/workflows/docker-develop.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-develop.yml b/.github/workflows/docker-develop.yml index f03ad23ad1..eb617e7cdb 100644 --- a/.github/workflows/docker-develop.yml +++ b/.github/workflows/docker-develop.yml @@ -29,5 +29,5 @@ jobs: with: context: . push: true - tags: develop + tags: misskey:develop labels: develop From 6d3e2b9386c192e8c31ade55f5ee68ba3a5341bb Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 16 Oct 2021 19:49:37 +0900 Subject: [PATCH 106/107] Update docker-develop.yml --- .github/workflows/docker-develop.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-develop.yml b/.github/workflows/docker-develop.yml index eb617e7cdb..09331edd1a 100644 --- a/.github/workflows/docker-develop.yml +++ b/.github/workflows/docker-develop.yml @@ -29,5 +29,5 @@ jobs: with: context: . push: true - tags: misskey:develop + tags: misskey/misskey:develop labels: develop From 8b646822fc7b6f8a0cc62f1f9e07d53774f2efeb Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 16 Oct 2021 19:55:26 +0900 Subject: [PATCH 107/107] 12.92.0 --- CHANGELOG.md | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dbe98f4ae5..873716a3d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ --> -## 12.x.x (unreleased) +## 12.92.0 (2021/10/16) ### Improvements - アカウント登録にメールアドレスの設定を必須にするオプション diff --git a/package.json b/package.json index e23b574725..c1fcff7126 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "misskey", "author": "syuilo <syuilotan@yahoo.co.jp>", - "version": "12.91.0", + "version": "12.92.0", "codename": "indigo", "repository": { "type": "git",