From 65577e43c88d6dda587e64db663b19b1972131ea Mon Sep 17 00:00:00 2001 From: tamaina <tamaina@hotmail.co.jp> Date: Mon, 16 Jan 2023 14:41:11 +0000 Subject: [PATCH 01/70] add docker note to CHANGELOG --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ef711d579..8fe41ef15a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,8 @@ You should also include the user name that made the change. - ユーザーごとのドライブ容量設定はロールに統合されました。 - インスタンスデフォルトのドライブ容量設定はロールに統合されました。アップデート後、ベースロールのドライブ容量を編集してください。 - LTL/GTLの解放状態はロールに統合されました。 +- Dockerの実行をrootで行わないようにしました。Dockerかつオブジェクトストレージを使用していない場合は`chown -hR 991.991 ./files`を実行してください。 + https://github.com/misskey-dev/misskey/pull/9560 #### For users - ノートのウォッチ機能が削除されました From fe98ad88490efd0bbdd69a51369da4cdc206c2bd Mon Sep 17 00:00:00 2001 From: tamaina <tamaina@hotmail.co.jp> Date: Mon, 16 Jan 2023 14:53:57 +0000 Subject: [PATCH 02/70] add comments to CHANGELOG --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8fe41ef15a..20efe62615 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,12 +32,13 @@ You should also include the user name that made the change. - Elasticsearchのサポートが削除されました - 代わりに今後任意の検索プロバイダを設定できる仕組みを構想しています。その仕組みを使えば今まで通りElasticsearchも利用できます - Yarnからpnpmに移行されました + corepackの有効化を推奨します: `sudo corepack enable` - インスタンスブロックはサブドメインにも適用されるようになります - ロールの導入に伴い、いくつかの機能がロールと統合されました - モデレーターはロールに統合されました。今までのモデレーター情報は失われるため、予めモデレーター一覧を記録しておき、アップデート後にモデレーターロールを作りアサインし直してください。 - サイレンスはロールに統合されました。今までのユーザーは恩赦されるため、予めサイレンス一覧を記録しておくのをおすすめします。 - ユーザーごとのドライブ容量設定はロールに統合されました。 - - インスタンスデフォルトのドライブ容量設定はロールに統合されました。アップデート後、ベースロールのドライブ容量を編集してください。 + - インスタンスデフォルトのドライブ容量設定はロールに統合されました。アップデート後、ベースロールもしくはコンディショナルロールでドライブ容量を編集してください。 - LTL/GTLの解放状態はロールに統合されました。 - Dockerの実行をrootで行わないようにしました。Dockerかつオブジェクトストレージを使用していない場合は`chown -hR 991.991 ./files`を実行してください。 https://github.com/misskey-dev/misskey/pull/9560 From 3feaf39294052c1edd0753fb9c3968ba2c05f57f Mon Sep 17 00:00:00 2001 From: Mary <Ipadlover8322@gmail.com> Date: Mon, 16 Jan 2023 12:21:15 -0500 Subject: [PATCH 03/70] ApRequestService: don't generate our own Host header (#9378) --- packages/backend/src/core/activitypub/ApRequestService.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/core/activitypub/ApRequestService.ts b/packages/backend/src/core/activitypub/ApRequestService.ts index d44d06a442..ab22a0c411 100644 --- a/packages/backend/src/core/activitypub/ApRequestService.ts +++ b/packages/backend/src/core/activitypub/ApRequestService.ts @@ -57,7 +57,7 @@ export class ApRequestService { method: 'POST', headers: this.objectAssignWithLcKey({ 'Date': new Date().toUTCString(), - 'Host': u.hostname, + 'Host': u.host, 'Content-Type': 'application/activity+json', 'Digest': digestHeader, }, args.additionalHeaders), @@ -83,7 +83,7 @@ export class ApRequestService { headers: this.objectAssignWithLcKey({ 'Accept': 'application/activity+json, application/ld+json', 'Date': new Date().toUTCString(), - 'Host': new URL(args.url).hostname, + 'Host': new URL(args.url).host, }, args.additionalHeaders), }; @@ -106,6 +106,8 @@ export class ApRequestService { request.headers = this.objectAssignWithLcKey(request.headers, { Signature: signatureHeader, }); + // node-fetch will generate this for us. if we keep 'Host', it won't change with redirects! + delete request.headers['host']; return { request, From 84b8ffb7d0663087b1b0c9df04e20b88b3a3e2d7 Mon Sep 17 00:00:00 2001 From: Kainoa Kanter <44733677+ThatOneCalculator@users.noreply.github.com> Date: Mon, 16 Jan 2023 10:26:41 -0800 Subject: [PATCH 04/70] enhance(client): Force error screen (#8947) --- packages/backend/src/server/web/boot.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/backend/src/server/web/boot.js b/packages/backend/src/server/web/boot.js index e2fc27fecd..a4513696a1 100644 --- a/packages/backend/src/server/web/boot.js +++ b/packages/backend/src/server/web/boot.js @@ -24,6 +24,11 @@ const v = localStorage.getItem('v') || VERSION; + let forceError = localStorage.getItem('forceError'); + if (forceError != null) { + renderError('FORCED_ERROR', 'This error is forced by having forceError in local storage.') + } + //#region Detect language & fetch translations const localeVersion = localStorage.getItem('localeVersion'); const localeOutdated = (localeVersion == null || localeVersion !== v); From a69c78e709dd9fd679b33706b455047c286d5fd7 Mon Sep 17 00:00:00 2001 From: Takuya Yoshida <hawaiianphoto@geekhost.net> Date: Tue, 17 Jan 2023 05:34:14 +0900 Subject: [PATCH 05/70] Use corepack (#9620) --- Dockerfile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 175be0fdb4..47fe31bca7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,6 +6,8 @@ RUN apt-get update \ && apt-get install -y --no-install-recommends \ build-essential +RUN corepack enable + WORKDIR /misskey COPY ["pnpm-lock.yaml", "pnpm-workspace.yaml", "package.json", "./"] @@ -14,7 +16,6 @@ COPY ["packages/backend/package.json", "./packages/backend/"] COPY ["packages/frontend/package.json", "./packages/frontend/"] COPY ["packages/sw/package.json", "./packages/sw/"] -RUN npm i -g pnpm RUN pnpm i --frozen-lockfile COPY . ./ @@ -34,10 +35,10 @@ RUN apt-get update \ ffmpeg tini \ && apt-get -y clean \ && rm -rf /var/lib/apt/lists/* \ + && corepack enable \ && groupadd -g "${GID}" misskey \ && useradd -l -u "${UID}" -g "${GID}" -m -d /misskey misskey -RUN npm i -g pnpm USER misskey WORKDIR /misskey From f8d09020807b6db6341bed43b1bb25a182ee29ca Mon Sep 17 00:00:00 2001 From: tamaina <tamaina@hotmail.co.jp> Date: Tue, 17 Jan 2023 06:22:57 +0900 Subject: [PATCH 06/70] =?UTF-8?q?enhahce(client):=20:art:=20=E9=80=9A?= =?UTF-8?q?=E7=9F=A5=E3=81=AE=E3=83=8E=E3=83=BC=E3=83=88=E3=82=B5=E3=83=9E?= =?UTF-8?q?=E3=83=AA=E3=83=BC=E3=82=921=E8=A1=8C=E3=81=AB=E3=81=99?= =?UTF-8?q?=E3=82=8B=20(#9625)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * :art: add nowrap to notification * :art: 通知のノートサマリーを1行にする --- packages/frontend/src/components/MkNotification.vue | 6 +++--- packages/frontend/src/components/MkNotifications.vue | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/frontend/src/components/MkNotification.vue b/packages/frontend/src/components/MkNotification.vue index 4f82579917..5b8041c1d4 100644 --- a/packages/frontend/src/components/MkNotification.vue +++ b/packages/frontend/src/components/MkNotification.vue @@ -267,9 +267,9 @@ useTooltip(reactionRef, (showing) => { } .text { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; + display: flex; + width: 100%; + overflow: clip; } .quote { diff --git a/packages/frontend/src/components/MkNotifications.vue b/packages/frontend/src/components/MkNotifications.vue index ab5dff8db5..f5ae7bcee4 100644 --- a/packages/frontend/src/components/MkNotifications.vue +++ b/packages/frontend/src/components/MkNotifications.vue @@ -10,7 +10,7 @@ <template #default="{ items: notifications }"> <MkDateSeparatedList v-slot="{ item: notification }" :class="$style.list" :items="notifications" :no-gap="true"> <XNote v-if="['reply', 'quote', 'mention'].includes(notification.type)" :key="notification.id" :note="notification.note"/> - <XNotification v-else :key="notification.id" :notification="notification" :with-time="true" :full="true" class="_panel notification"/> + <XNotification v-else :key="notification.id" :notification="notification" :with-time="true" :full="false" class="_panel notification"/> </MkDateSeparatedList> </template> </MkPagination> From d79478c26567e15ff37cd3bfd8c6ff5b18c0a243 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Tue, 17 Jan 2023 13:40:27 +0900 Subject: [PATCH 07/70] =?UTF-8?q?fix(client):=20play=E3=82=92=E5=89=8A?= =?UTF-8?q?=E9=99=A4=E3=81=99=E3=82=8B=E6=89=8B=E6=AE=B5=E3=81=8C=E3=81=AA?= =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=9F=E3=81=AE=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 #9630 --- CHANGELOG.md | 7 +++++++ packages/frontend/src/pages/flash/flash-edit.vue | 14 ++++++++++++++ packages/frontend/src/pages/flash/flash-index.vue | 14 ++++++++------ 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 20efe62615..56b25eae2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,13 @@ You should also include the user name that made the change. --> +## 13.x.x (unreleased) + +### Improvements + +### Bugfixes +- playを削除する手段がなかったのを修正 + ## 13.0.0 (2023/01/16) ### TL;DR diff --git a/packages/frontend/src/pages/flash/flash-edit.vue b/packages/frontend/src/pages/flash/flash-edit.vue index 5ba226c10d..539aeec85d 100644 --- a/packages/frontend/src/pages/flash/flash-edit.vue +++ b/packages/frontend/src/pages/flash/flash-edit.vue @@ -16,6 +16,7 @@ <div class="_buttons"> <MkButton primary @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton> <MkButton @click="show"><i class="ti ti-eye"></i> {{ i18n.ts.show }}</MkButton> + <MkButton v-if="flash" danger @click="del"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton> </div> </div> </MkSpacer> @@ -212,6 +213,19 @@ function show() { } } +async function del() { + const { canceled } = await os.confirm({ + type: 'warning', + text: i18n.t('deleteAreYouSure', { x: flash.title }), + }); + if (canceled) return; + + await os.apiWithDialog('flash/delete', { + flashId: props.id, + }); + router.push('/play'); +} + const headerActions = $computed(() => []); const headerTabs = $computed(() => []); diff --git a/packages/frontend/src/pages/flash/flash-index.vue b/packages/frontend/src/pages/flash/flash-index.vue index 7a1080d3f0..a3a48d3b97 100644 --- a/packages/frontend/src/pages/flash/flash-index.vue +++ b/packages/frontend/src/pages/flash/flash-index.vue @@ -11,12 +11,14 @@ </div> <div v-else-if="tab === 'my'" class="my"> - <MkButton class="new" @click="create()"><i class="ti ti-plus"></i></MkButton> - <MkPagination v-slot="{items}" :pagination="myFlashsPagination"> - <div class="_gaps_s"> - <MkFlashPreview v-for="flash in items" :key="flash.id" class="" :flash="flash"/> - </div> - </MkPagination> + <div class="_gaps"> + <MkButton class="new" gradate rounded style="margin: 0 auto;" @click="create()"><i class="ti ti-plus"></i></MkButton> + <MkPagination v-slot="{items}" :pagination="myFlashsPagination"> + <div class="_gaps_s"> + <MkFlashPreview v-for="flash in items" :key="flash.id" class="" :flash="flash"/> + </div> + </MkPagination> + </div> </div> <div v-else-if="tab === 'liked'" class=""> From 0de41063daaeb4f679bd0f99968dbc6809aa1ac9 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Tue, 17 Jan 2023 14:10:25 +0900 Subject: [PATCH 08/70] :art: --- packages/frontend/src/components/MkAsUi.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/src/components/MkAsUi.vue b/packages/frontend/src/components/MkAsUi.vue index cbdf924538..9543ccc4da 100644 --- a/packages/frontend/src/components/MkAsUi.vue +++ b/packages/frontend/src/components/MkAsUi.vue @@ -7,7 +7,7 @@ </div> <span v-else-if="c.type === 'text'" :class="{ [$style.fontSerif]: c.font === 'serif', [$style.fontMonospace]: c.font === 'monospace' }" :style="{ fontSize: c.size ? `${c.size * 100}%` : null, fontWeight: c.bold ? 'bold' : null, color: c.color ?? null }">{{ c.text }}</span> <Mfm v-else-if="c.type === 'mfm'" :class="{ [$style.fontSerif]: c.font === 'serif', [$style.fontMonospace]: c.font === 'monospace' }" :style="{ fontSize: c.size ? `${c.size * 100}%` : null, fontWeight: c.bold ? 'bold' : null, color: c.color ?? null }" :text="c.text"/> - <MkButton v-else-if="c.type === 'button'" :primary="c.primary" :rounded="c.rounded" :small="size === 'small'" @click="c.onClick">{{ c.text }}</MkButton> + <MkButton v-else-if="c.type === 'button'" :primary="c.primary" :rounded="c.rounded" :small="size === 'small'" inline @click="c.onClick">{{ c.text }}</MkButton> <div v-else-if="c.type === 'buttons'" class="_buttons"> <MkButton v-for="button in c.buttons" :primary="button.primary" :rounded="button.rounded" :small="size === 'small'" @click="button.onClick">{{ button.text }}</MkButton> </div> From c62a4d62821687bdf249c00d9025b38df903b69a Mon Sep 17 00:00:00 2001 From: Takuya Yoshida <hawaiianphoto@geekhost.net> Date: Tue, 17 Jan 2023 15:27:01 +0900 Subject: [PATCH 09/70] Update actions (#9638) --- .github/workflows/docker-develop.yml | 6 +++--- .github/workflows/docker.yml | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/docker-develop.yml b/.github/workflows/docker-develop.yml index f04888e982..d2cf4bf629 100644 --- a/.github/workflows/docker-develop.yml +++ b/.github/workflows/docker-develop.yml @@ -16,16 +16,16 @@ jobs: uses: actions/checkout@v3.3.0 - name: Docker meta id: meta - uses: docker/metadata-action@v3 + uses: docker/metadata-action@v4 with: images: misskey/misskey - name: Log in to Docker Hub - uses: docker/login-action@v1 + uses: docker/login-action@v2 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Build and Push to Docker Hub - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v3 with: context: . push: true diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 84d36f8465..d7803ce3ec 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -15,7 +15,7 @@ jobs: uses: actions/checkout@v3.3.0 - name: Docker meta id: meta - uses: docker/metadata-action@v3 + uses: docker/metadata-action@v4 with: images: misskey/misskey tags: | @@ -26,12 +26,12 @@ jobs: type=semver,pattern={{major}}.{{minor}} type=semver,pattern={{major}} - name: Log in to Docker Hub - uses: docker/login-action@v1 + uses: docker/login-action@v2 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Build and Push to Docker Hub - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v3 with: context: . push: true From fe4fbafcf0846f100a1614a47a20fb1bbbcf58da Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Tue, 17 Jan 2023 15:58:12 +0900 Subject: [PATCH 10/70] =?UTF-8?q?=E5=AD=98=E5=9C=A8=E3=81=97=E3=81=AA?= =?UTF-8?q?=E3=81=84=E3=82=AB=E3=82=B9=E3=82=BF=E3=83=A0=E7=B5=B5=E6=96=87?= =?UTF-8?q?=E5=AD=97=E3=82=92=E3=83=86=E3=82=AD=E3=82=B9=E3=83=88=E3=81=A7?= =?UTF-8?q?=E8=A1=A8=E7=A4=BA=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 #9516 --- CHANGELOG.md | 1 + packages/backend/src/server/ServerService.ts | 9 +++++++-- packages/frontend/src/components/global/MkEmoji.vue | 4 +++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 56b25eae2d..c877be07b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ You should also include the user name that made the change. ## 13.x.x (unreleased) ### Improvements +- 存在しないカスタム絵文字をテキストで表示するように ### Bugfixes - playを削除する手段がなかったのを修正 diff --git a/packages/backend/src/server/ServerService.ts b/packages/backend/src/server/ServerService.ts index fac8497b5e..47d11ed0ae 100644 --- a/packages/backend/src/server/ServerService.ts +++ b/packages/backend/src/server/ServerService.ts @@ -14,6 +14,7 @@ import { genIdenticon } from '@/misc/gen-identicon.js'; import { createTemp } from '@/misc/create-temp.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { LoggerService } from '@/core/LoggerService.js'; +import { bindThis } from '@/decorators.js'; import { ActivityPubServerService } from './ActivityPubServerService.js'; import { NodeinfoServerService } from './NodeinfoServerService.js'; import { ApiServerService } from './api/ApiServerService.js'; @@ -22,7 +23,6 @@ import { WellKnownServerService } from './WellKnownServerService.js'; import { MediaProxyServerService } from './MediaProxyServerService.js'; import { FileServerService } from './FileServerService.js'; import { ClientServerService } from './web/ClientServerService.js'; -import { bindThis } from '@/decorators.js'; @Injectable() export class ServerService { @@ -101,7 +101,12 @@ export class ServerService { reply.header('Content-Security-Policy', 'default-src \'none\'; style-src \'unsafe-inline\''); if (emoji == null) { - return await reply.redirect('/static-assets/emoji-unknown.png'); + if ('fallback' in request.query) { + return await reply.redirect('/static-assets/emoji-unknown.png'); + } else { + reply.code(404); + return; + } } const url = new URL('/proxy/emoji.webp', this.config.url); diff --git a/packages/frontend/src/components/global/MkEmoji.vue b/packages/frontend/src/components/global/MkEmoji.vue index bc88cf3be4..b7dd0296cd 100644 --- a/packages/frontend/src/components/global/MkEmoji.vue +++ b/packages/frontend/src/components/global/MkEmoji.vue @@ -1,5 +1,6 @@ <template> -<img v-if="isCustom" :class="[$style.root, $style.custom, { [$style.normal]: normal, [$style.noStyle]: noStyle }]" :src="url" :alt="alt" :title="alt" decoding="async"/> +<span v-if="isCustom && errored">:{{ customEmojiName }}:</span> +<img v-else-if="isCustom" :class="[$style.root, $style.custom, { [$style.normal]: normal, [$style.noStyle]: noStyle }]" :src="url" :alt="alt" :title="alt" decoding="async" @error="errored = true"/> <img v-else-if="char && !useOsNativeEmojis" :class="$style.root" :src="url" :alt="alt" decoding="async" @pointerenter="computeTitle"/> <span v-else-if="char && useOsNativeEmojis" :alt="alt" @pointerenter="computeTitle">{{ char }}</span> <span v-else>{{ emoji }}</span> @@ -37,6 +38,7 @@ const url = computed(() => { } }); const alt = computed(() => isCustom.value ? `:${customEmojiName}:` : char.value); +let errored = $ref(false); // Searching from an array with 2000 items for every emoji felt like too energy-consuming, so I decided to do it lazily on pointerenter function computeTitle(event: PointerEvent): void { From 8f0c598772fdb70d9381d5d83046515241b7052a Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Tue, 17 Jan 2023 17:09:43 +0900 Subject: [PATCH 11/70] improve aiscript ui --- packages/frontend/src/components/MkAsUi.vue | 10 ++++++---- packages/frontend/src/scripts/aiscript/ui.ts | 7 +++++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/packages/frontend/src/components/MkAsUi.vue b/packages/frontend/src/components/MkAsUi.vue index 9543ccc4da..4f463d73d9 100644 --- a/packages/frontend/src/components/MkAsUi.vue +++ b/packages/frontend/src/components/MkAsUi.vue @@ -7,9 +7,9 @@ </div> <span v-else-if="c.type === 'text'" :class="{ [$style.fontSerif]: c.font === 'serif', [$style.fontMonospace]: c.font === 'monospace' }" :style="{ fontSize: c.size ? `${c.size * 100}%` : null, fontWeight: c.bold ? 'bold' : null, color: c.color ?? null }">{{ c.text }}</span> <Mfm v-else-if="c.type === 'mfm'" :class="{ [$style.fontSerif]: c.font === 'serif', [$style.fontMonospace]: c.font === 'monospace' }" :style="{ fontSize: c.size ? `${c.size * 100}%` : null, fontWeight: c.bold ? 'bold' : null, color: c.color ?? null }" :text="c.text"/> - <MkButton v-else-if="c.type === 'button'" :primary="c.primary" :rounded="c.rounded" :small="size === 'small'" inline @click="c.onClick">{{ c.text }}</MkButton> - <div v-else-if="c.type === 'buttons'" class="_buttons"> - <MkButton v-for="button in c.buttons" :primary="button.primary" :rounded="button.rounded" :small="size === 'small'" @click="button.onClick">{{ button.text }}</MkButton> + <MkButton v-else-if="c.type === 'button'" :primary="c.primary" :rounded="c.rounded" :disabled="c.disabled" :small="size === 'small'" inline @click="c.onClick">{{ c.text }}</MkButton> + <div v-else-if="c.type === 'buttons'" class="_buttons" :style="{ justifyContent: align }"> + <MkButton v-for="button in c.buttons" :primary="button.primary" :rounded="button.rounded" :disabled="button.disabled" inline :small="size === 'small'" @click="button.onClick">{{ button.text }}</MkButton> </div> <MkSwitch v-else-if="c.type === 'switch'" :model-value="valueForSwitch" @update:model-value="onSwitchUpdate"> <template v-if="c.label" #label>{{ c.label }}</template> @@ -41,7 +41,7 @@ </MkFolder> <div v-else-if="c.type === 'container'" :class="[$style.container, { [$style.fontSerif]: c.font === 'serif', [$style.fontMonospace]: c.font === 'monospace', [$style.containerCenter]: c.align === 'center' }]" :style="{ backgroundColor: c.bgColor ?? null, color: c.fgColor ?? null, borderWidth: c.borderWidth ? `${c.borderWidth}px` : 0, borderColor: c.borderColor ?? 'var(--divider)', padding: c.padding ? `${c.padding}px` : 0, borderRadius: c.rounded ? '8px' : 0 }"> <template v-for="child in c.children" :key="child"> - <MkAsUi v-if="!g(child).hidden" :component="g(child)" :components="props.components" :size="size"/> + <MkAsUi v-if="!g(child).hidden" :component="g(child)" :components="props.components" :size="size" :align="c.align"/> </template> </div> </div> @@ -62,8 +62,10 @@ const props = withDefaults(defineProps<{ component: AsUiComponent; components: Ref<AsUiComponent>[]; size: 'small' | 'medium' | 'large'; + align: 'left' | 'center' | 'right'; }>(), { size: 'medium', + align: 'left', }); const c = props.component; diff --git a/packages/frontend/src/scripts/aiscript/ui.ts b/packages/frontend/src/scripts/aiscript/ui.ts index 2555cd391b..b1895a5f33 100644 --- a/packages/frontend/src/scripts/aiscript/ui.ts +++ b/packages/frontend/src/scripts/aiscript/ui.ts @@ -50,6 +50,7 @@ export type AsUiButton = AsUiComponentBase & { onClick?: () => void; primary?: boolean; rounded?: boolean; + disabled?: boolean; }; export type AsUiButtons = AsUiComponentBase & { @@ -302,6 +303,8 @@ function getButtonOptions(def: values.Value | undefined, call: (fn: values.VFn, if (primary) utils.assertBoolean(primary); const rounded = def.value.get('rounded'); if (rounded) utils.assertBoolean(rounded); + const disabled = button.value.get('disabled'); + if (disabled) utils.assertBoolean(disabled); return { text: text?.value, @@ -310,6 +313,7 @@ function getButtonOptions(def: values.Value | undefined, call: (fn: values.VFn, }, primary: primary?.value, rounded: rounded?.value, + disabled: disabled?.value, }; } @@ -330,6 +334,8 @@ function getButtonsOptions(def: values.Value | undefined, call: (fn: values.VFn, if (primary) utils.assertBoolean(primary); const rounded = button.value.get('rounded'); if (rounded) utils.assertBoolean(rounded); + const disabled = button.value.get('disabled'); + if (disabled) utils.assertBoolean(disabled); return { text: text.value, @@ -338,6 +344,7 @@ function getButtonsOptions(def: values.Value | undefined, call: (fn: values.VFn, }, primary: primary?.value, rounded: rounded?.value, + disabled: disabled?.value, }; }) : [], }; From d75225e23b537bbc76c6ce7f506d28662171418b Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Tue, 17 Jan 2023 17:10:33 +0900 Subject: [PATCH 12/70] add new play preset --- .../frontend/src/pages/flash/flash-edit.vue | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/packages/frontend/src/pages/flash/flash-edit.vue b/packages/frontend/src/pages/flash/flash-edit.vue index 539aeec85d..321477259b 100644 --- a/packages/frontend/src/pages/flash/flash-edit.vue +++ b/packages/frontend/src/pages/flash/flash-edit.vue @@ -95,6 +95,85 @@ Ui:render([ ]) `; +const PRESET_SHUFFLE = `/// @ 0.12.2 +// 巻き戻し可能な文字シャッフルのプリセット + +let string = "ペペロンチーノ" +let length = string.len + +// 過去の結果を保存しておくやつ +var results = [] + +// どれだけ巻き戻しているか +var cursor = 0 + +@do() { + if (cursor != 0) { + results = results.slice(0 (cursor + 1)) + cursor = 0 + } + + let chars = [] + for (let i, length) { + let r = Math:rnd(0 (length - 1)) + chars.push(string.pick(r)) + } + let result = chars.join("") + + results.push(result) + + // UIを表示 + render(result) +} + +@back() { + cursor = cursor + 1 + let result = results[results.len - (cursor + 1)] + render(result) +} + +@forward() { + cursor = cursor - 1 + let result = results[results.len - (cursor + 1)] + render(result) +} + +@render(result) { + Ui:render([ + Ui:C:container({ + align: 'center' + children: [ + Ui:C:mfm({ text: result }) + Ui:C:buttons({ + buttons: [{ + text: "←" + disabled: !(results.len > 1 && (results.len - cursor) > 1) + onClick: back + } { + text: "→" + disabled: !(results.len > 1 && cursor > 0) + onClick: forward + } { + text: "引き直す" + onClick: do + }] + }) + Ui:C:postFormButton({ + text: "投稿する" + rounded: true + primary: true + form: { + text: \`{result}{Str:lf}{THIS_URL}\` + } + }) + ] + }) + ]) +} + +do() +`; + const PRESET_TIMELINE = `/// @ 0.12.2 // APIリクエストを行いローカルタイムラインを表示するプリセット @@ -175,6 +254,11 @@ function selectPreset(ev: MouseEvent) { action: () => { script = PRESET_OMIKUJI; }, + }, { + text: 'Shuffle', + action: () => { + script = PRESET_SHUFFLE; + }, }, { text: 'Timeline viewer', action: () => { From 424919ffd0ef2b8083f388bc454a2731d5f1a438 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Tue, 17 Jan 2023 17:11:02 +0900 Subject: [PATCH 13/70] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c877be07b7..d782945153 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ You should also include the user name that made the change. ## 13.x.x (unreleased) ### Improvements +- Playのプリセットを追加 +- AiScript GUIの強化 - 存在しないカスタム絵文字をテキストで表示するように ### Bugfixes From d456308653bcadbb95dbd954b01eab1df9464c03 Mon Sep 17 00:00:00 2001 From: tamaina <tamaina@hotmail.co.jp> Date: Tue, 17 Jan 2023 17:36:18 +0900 Subject: [PATCH 14/70] pref: Optimize client imports (#9506) * pref: Optimize client imports * split api? * fix * :v: * no vue split? * Revert "no vue split?" This reverts commit 27ccec971e2925184a58da65c3472655def2617c. * function => const * :v: * Revert "function => const" This reverts commit 34f2feb224f65cc4fca090b29450adb00e85c2c5. * function api --- packages/frontend/src/os.ts | 39 ++++++++++++++++------------ packages/frontend/src/scripts/api.ts | 2 +- packages/frontend/vite.config.ts | 1 + 3 files changed, 25 insertions(+), 17 deletions(-) diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts index b19443aa55..111bd0cd8d 100644 --- a/packages/frontend/src/os.ts +++ b/packages/frontend/src/os.ts @@ -1,5 +1,7 @@ // TODO: なんでもかんでもos.tsに突っ込むのやめたいのでよしなに分割する +import { pendingApiRequestsCount, api, apiGet } from '@/scripts/api'; +export { pendingApiRequestsCount, api, apiGet }; import { Component, markRaw, Ref, ref, defineAsyncComponent } from 'vue'; import { EventEmitter } from 'eventemitter3'; import insertTextAtCursor from 'insert-text-at-cursor'; @@ -7,9 +9,14 @@ import * as Misskey from 'misskey-js'; import { i18n } from './i18n'; import MkPostFormDialog from '@/components/MkPostFormDialog.vue'; import MkWaitingDialog from '@/components/MkWaitingDialog.vue'; +import MkPageWindow from '@/components/MkPageWindow.vue' +import MkToast from '@/components/MkToast.vue'; +import MkDialog from '@/components/MkDialog.vue'; +import MkEmojiPickerDialog from '@/components/MkEmojiPickerDialog.vue'; +import MkEmojiPickerWindow from '@/components/MkEmojiPickerWindow.vue'; +import MkPopupMenu from '@/components/MkPopupMenu.vue'; +import MkContextMenu from '@/components/MkContextMenu.vue'; import { MenuItem } from '@/types/menu'; -import { pendingApiRequestsCount, api, apiGet } from '@/scripts/api'; -export { pendingApiRequestsCount, api, apiGet }; export const apiWithDialog = (( endpoint: string, @@ -124,7 +131,7 @@ export async function popup(component: Component, props: Record<string, any>, ev } export function pageWindow(path: string) { - popup(defineAsyncComponent(() => import('@/components/MkPageWindow.vue')), { + popup(MkPageWindow, { initialPath: path, }, {}, 'closed'); } @@ -136,7 +143,7 @@ export function modalPageWindow(path: string) { } export function toast(message: string) { - popup(defineAsyncComponent(() => import('@/components/MkToast.vue')), { + popup(MkToast, { message, }, {}, 'closed'); } @@ -147,7 +154,7 @@ export function alert(props: { text?: string | null; }): Promise<void> { return new Promise((resolve, reject) => { - popup(defineAsyncComponent(() => import('@/components/MkDialog.vue')), props, { + popup(MkDialog, props, { done: result => { resolve(); }, @@ -161,7 +168,7 @@ export function confirm(props: { text?: string | null; }): Promise<{ canceled: boolean }> { return new Promise((resolve, reject) => { - popup(defineAsyncComponent(() => import('@/components/MkDialog.vue')), { + popup(MkDialog, { ...props, showCancelButton: true, }, { @@ -182,7 +189,7 @@ export function inputText(props: { canceled: false; result: string; }> { return new Promise((resolve, reject) => { - popup(defineAsyncComponent(() => import('@/components/MkDialog.vue')), { + popup(MkDialog, { title: props.title, text: props.text, input: { @@ -207,7 +214,7 @@ export function inputNumber(props: { canceled: false; result: number; }> { return new Promise((resolve, reject) => { - popup(defineAsyncComponent(() => import('@/components/MkDialog.vue')), { + popup(MkDialog, { title: props.title, text: props.text, input: { @@ -232,7 +239,7 @@ export function inputDate(props: { canceled: false; result: Date; }> { return new Promise((resolve, reject) => { - popup(defineAsyncComponent(() => import('@/components/MkDialog.vue')), { + popup(MkDialog, { title: props.title, text: props.text, input: { @@ -269,7 +276,7 @@ export function select<C = any>(props: { canceled: false; result: C; }> { return new Promise((resolve, reject) => { - popup(defineAsyncComponent(() => import('@/components/MkDialog.vue')), { + popup(MkDialog, { title: props.title, text: props.text, select: { @@ -291,7 +298,7 @@ export function success() { window.setTimeout(() => { showing.value = false; }, 1000); - popup(defineAsyncComponent(() => import('@/components/MkWaitingDialog.vue')), { + popup(MkWaitingDialog, { success: true, showing: showing, }, { @@ -303,7 +310,7 @@ export function success() { export function waiting() { return new Promise((resolve, reject) => { const showing = ref(true); - popup(defineAsyncComponent(() => import('@/components/MkWaitingDialog.vue')), { + popup(MkWaitingDialog, { success: false, showing: showing, }, { @@ -366,7 +373,7 @@ export async function selectDriveFolder(multiple: boolean) { export async function pickEmoji(src: HTMLElement | null, opts) { return new Promise((resolve, reject) => { - popup(defineAsyncComponent(() => import('@/components/MkEmojiPickerDialog.vue')), { + popup(MkEmojiPickerDialog, { src, ...opts, }, { @@ -431,7 +438,7 @@ export async function openEmojiPicker(src?: HTMLElement, opts, initialTextarea: characterData: false, }); - openingEmojiPicker = await popup(defineAsyncComponent(() => import('@/components/MkEmojiPickerWindow.vue')), { + openingEmojiPicker = await popup(MkEmojiPickerWindow, { src, ...opts, }, { @@ -454,7 +461,7 @@ export function popupMenu(items: MenuItem[] | Ref<MenuItem[]>, src?: HTMLElement }) { return new Promise((resolve, reject) => { let dispose; - popup(defineAsyncComponent(() => import('@/components/MkPopupMenu.vue')), { + popup(MkPopupMenu, { items, src, width: options?.width, @@ -478,7 +485,7 @@ export function contextMenu(items: MenuItem[] | Ref<MenuItem[]>, ev: MouseEvent) ev.preventDefault(); return new Promise((resolve, reject) => { let dispose; - popup(defineAsyncComponent(() => import('@/components/MkContextMenu.vue')), { + popup(MkContextMenu, { items, ev, }, { diff --git a/packages/frontend/src/scripts/api.ts b/packages/frontend/src/scripts/api.ts index f9fd11f069..5f34f5333e 100644 --- a/packages/frontend/src/scripts/api.ts +++ b/packages/frontend/src/scripts/api.ts @@ -45,7 +45,7 @@ export function api<E extends keyof Endpoints, P extends Endpoints[E]['req']>(en } // Implements Misskey.api.ApiClient.request -export function apiGet<E extends keyof Endpoints, P extends Endpoints[E]['req']>(endpoint: E, data: P = {} as any): Promise<Endpoints[E]['res']> { +export function apiGet <E extends keyof Endpoints, P extends Endpoints[E]['req']>(endpoint: E, data: P = {} as any): Promise<Endpoints[E]['res']> { pendingApiRequestsCount.value++; const onFinally = () => { diff --git a/packages/frontend/vite.config.ts b/packages/frontend/vite.config.ts index ba82eda609..0bf35ec1b4 100644 --- a/packages/frontend/vite.config.ts +++ b/packages/frontend/vite.config.ts @@ -97,6 +97,7 @@ export default defineConfig(({ command, mode }) => { output: { manualChunks: { vue: ['vue'], + photoswipe: ['photoswipe', 'photoswipe/lightbox', 'photoswipe/style.css'], }, }, }, From 79212bbd375705f0fd658dd5b50b47f77d622fb8 Mon Sep 17 00:00:00 2001 From: tamaina <tamaina@hotmail.co.jp> Date: Tue, 17 Jan 2023 17:12:41 +0000 Subject: [PATCH 15/70] refactor(server): notify url when fetch error --- packages/backend/src/core/HttpRequestService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/core/HttpRequestService.ts b/packages/backend/src/core/HttpRequestService.ts index 8639b5713d..2864ad4405 100644 --- a/packages/backend/src/core/HttpRequestService.ts +++ b/packages/backend/src/core/HttpRequestService.ts @@ -125,7 +125,7 @@ export class UndiciFetcher { ...(options.headers ?? {}), }, }).catch((err) => { - this.logger?.error('fetch error', err); + this.logger?.error(`fetch error to ${typeof url === 'string' ? url : url.href}`, err); throw new StatusError('Resource Unreachable', 500, 'Resource Unreachable'); }); if (!res.ok && !privateOptions.noOkError) { From 57b1fe44d402c1ee4b2218808b275f9dab4c3de3 Mon Sep 17 00:00:00 2001 From: atsuchan <83960488+atsu1125@users.noreply.github.com> Date: Wed, 18 Jan 2023 06:13:44 +0900 Subject: [PATCH 16/70] Bump Postgres 12.2 to 15.x on docker-compose.yml (#9641) --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 01bd1b1e6e..ca7ce1ca2d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -36,7 +36,7 @@ services: db: restart: always - image: postgres:12.2-alpine + image: postgres:15-alpine networks: - internal_network env_file: From eea47ca2e862c6a8fbe86bd4fff23bab5c22c6e5 Mon Sep 17 00:00:00 2001 From: Takuya Yoshida <hawaiianphoto@geekhost.net> Date: Wed, 18 Jan 2023 16:18:23 +0900 Subject: [PATCH 17/70] Update redis to 7 (#9654) --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index ca7ce1ca2d..b0c4a914d5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -24,7 +24,7 @@ services: redis: restart: always - image: redis:4.0-alpine + image: redis:7-alpine networks: - internal_network volumes: From 85f3df4c0ebb179fe9446f29deb7b4ebbb87a85f Mon Sep 17 00:00:00 2001 From: tamaina <tamaina@hotmail.co.jp> Date: Wed, 18 Jan 2023 20:26:38 +0900 Subject: [PATCH 18/70] =?UTF-8?q?fix(client):=20messaging-room=E5=91=A8?= =?UTF-8?q?=E3=82=8A=20(#9643)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * wip * :v: * clean up --- .../src/pages/messaging/messaging-room.vue | 204 +++++++++--------- 1 file changed, 103 insertions(+), 101 deletions(-) diff --git a/packages/frontend/src/pages/messaging/messaging-room.vue b/packages/frontend/src/pages/messaging/messaging-room.vue index f1749d449d..0867f003a3 100644 --- a/packages/frontend/src/pages/messaging/messaging-room.vue +++ b/packages/frontend/src/pages/messaging/messaging-room.vue @@ -1,11 +1,15 @@ <template> +<MkStickyContainer> +<template #header> + <MkPageHeader /> +</template> <div ref="rootEl" - class="root" + :class="$style['root']" @dragover.prevent.stop="onDragover" @drop.prevent.stop="onDrop" > - <div class="body"> + <div :class="$style['body']"> <MkPagination v-if="pagination" ref="pagingComponent" :key="userAcct || groupId" :pagination="pagination"> <template #empty> <div class="_fullinfo"> @@ -17,7 +21,7 @@ <MkDateSeparatedList v-if="messages.length > 0" v-slot="{ item: message }" - :class="{ messages: true, 'deny-move-transition': pFetching }" + :class="{ [$style['messages']]: true, 'deny-move-transition': pFetching }" :items="messages" direction="up" reversed @@ -27,23 +31,26 @@ </template> </MkPagination> </div> - <footer> - <div v-if="typers.length > 0" class="typers"> - <I18n :src="i18n.ts.typingUsers" text-tag="span" class="users"> + <footer :class="$style['footer']"> + <div v-if="typers.length > 0" :class="$style['typers']"> + <I18n :src="i18n.ts.typingUsers" text-tag="span"> <template #users> - <b v-for="typer in typers" :key="typer.id" class="user">{{ typer.username }}</b> + <b v-for="typer in typers" :key="typer.id" :class="$style['user']">{{ typer.username }}</b> </template> </I18n> <MkEllipsis/> </div> <Transition :name="animation ? 'fade' : ''"> - <div v-show="showIndicator" class="new-message"> - <button class="_buttonPrimary" @click="onIndicatorClick"><i class="fas ti-fw fa-arrow-circle-down"></i>{{ i18n.ts.newMessageExists }}</button> + <div v-show="showIndicator" :class="$style['new-message']"> + <button class="_buttonPrimary" @click="onIndicatorClick" :class="$style['new-message-button']"> + <i class="fas ti-fw fa-arrow-circle-down" :class="$style['new-message-icon']"></i>{{ i18n.ts.newMessageExists }} + </button> </div> </Transition> - <XForm v-if="!fetching" ref="formEl" :user="user" :group="group" class="form"/> + <XForm v-if="!fetching" ref="formEl" :user="user" :group="group" :class="$style['form']"/> </footer> </div> +</MkStickyContainer> </template> <script lang="ts" setup> @@ -303,103 +310,98 @@ definePageMetadata(computed(() => !fetching ? user ? { } : null)); </script> -<style lang="scss" scoped> +<style lang="scss" module> .root { display: content; +} - > .body { - min-height: 80%; +.body { + min-height: 80%; +} - .more { - display: block; - margin: 16px auto; - padding: 0 12px; - line-height: 24px; - color: #fff; - background: rgba(#000, 0.3); - border-radius: 12px; - - &:hover { - background: rgba(#000, 0.4); - } - - &:active { - background: rgba(#000, 0.5); - } - - &.fetching { - cursor: wait; - } - - > i { - margin-right: 4px; - } - } - - .messages { - padding: 8px 0; - - > ::v-deep(*) { - margin-bottom: 16px; - } - } +.more { + display: block; + margin: 16px auto; + padding: 0 12px; + line-height: 24px; + color: #fff; + background: rgba(#000, 0.3); + border-radius: 12px; + &:hover { + background: rgba(#000, 0.4); } - - > footer { - width: 100%; - position: sticky; - z-index: 2; - padding-top: 8px; - bottom: 0; - bottom: env(safe-area-inset-bottom, 0px); - - > .new-message { - width: 100%; - padding-bottom: 8px; - text-align: center; - - > button { - display: inline-block; - margin: 0; - padding: 0 12px; - line-height: 32px; - font-size: 12px; - border-radius: 16px; - - > i { - display: inline-block; - margin-right: 8px; - } - } - } - - > .typers { - position: absolute; - bottom: 100%; - padding: 0 8px 0 8px; - font-size: 0.9em; - color: var(--fgTransparentWeak); - - > .users { - > .user + .user:before { - content: ", "; - font-weight: normal; - } - - > .user:last-of-type:after { - content: " "; - } - } - } - - > .form { - max-height: 12em; - overflow-y: scroll; - border-top: solid 0.5px var(--divider); - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; - } + &:active { + background: rgba(#000, 0.5); } + > i { + margin-right: 4px; + } +} + +.fetching { + cursor: wait; +} + +.messages { + padding: 16px 0 0; + + > * { + margin-bottom: 16px; + } +} + +.footer { + width: 100%; + position: sticky; + z-index: 2; + padding-top: 8px; + bottom: var(--minBottomSpacing); +} + +.new-message { + width: 100%; + padding-bottom: 8px; + text-align: center; +} + +.new-message-button { + display: inline-block; + margin: 0; + padding: 0 12px; + line-height: 32px; + font-size: 12px; + border-radius: 16px; +} + +.new-message-icon { + display: inline-block; + margin-right: 8px; +} + +.typers { + position: absolute; + bottom: 100%; + padding: 0 8px 0 8px; + font-size: 0.9em; + color: var(--fgTransparentWeak); +} + + +.user + .user:before { + content: ", "; + font-weight: normal; +} + +.user:last-of-type:after { + content: " "; +} + +.form { + max-height: 12em; + overflow-y: scroll; + border-top: solid 0.5px var(--divider); + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; } .fade-enter-active, .fade-leave-active { From afc0be67908f92fd57803329cebdee454fd75442 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Thu, 19 Jan 2023 09:06:11 +0900 Subject: [PATCH 19/70] =?UTF-8?q?enhance:=20play=E3=81=AEscript=E3=81=AE?= =?UTF-8?q?=E6=96=87=E5=AD=97=E6=95=B0=E5=88=B6=E9=99=90=E3=82=92=E7=B7=A9?= =?UTF-8?q?=E5=92=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + .../migration/1674086433654-flashScriptLength.js | 11 +++++++++++ packages/backend/src/models/entities/Flash.ts | 2 +- 3 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 packages/backend/migration/1674086433654-flashScriptLength.js diff --git a/CHANGELOG.md b/CHANGELOG.md index d782945153..69f1123f09 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ You should also include the user name that made the change. ### Improvements - Playのプリセットを追加 +- Playのscriptの文字数制限を緩和 - AiScript GUIの強化 - 存在しないカスタム絵文字をテキストで表示するように diff --git a/packages/backend/migration/1674086433654-flashScriptLength.js b/packages/backend/migration/1674086433654-flashScriptLength.js new file mode 100644 index 0000000000..a4d149fe15 --- /dev/null +++ b/packages/backend/migration/1674086433654-flashScriptLength.js @@ -0,0 +1,11 @@ +export class flashScriptLength1674086433654 { + name = 'flashScriptLength1674086433654' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "flash" ALTER COLUMN "script" TYPE character varying(32768)`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "flash" ALTER COLUMN "script" TYPE character varying(16384)`); + } +} diff --git a/packages/backend/src/models/entities/Flash.ts b/packages/backend/src/models/entities/Flash.ts index d9a6ac987c..07039d4fa1 100644 --- a/packages/backend/src/models/entities/Flash.ts +++ b/packages/backend/src/models/entities/Flash.ts @@ -44,7 +44,7 @@ export class Flash { public user: User | null; @Column('varchar', { - length: 16384, + length: 32768, }) public script: string; From d05ffc0a7cb856f19187e7966f287aee3a98bdbc Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Thu, 19 Jan 2023 09:12:52 +0900 Subject: [PATCH 20/70] New Crowdin updates (#9616) * New translations ja-JP.yml (English) * New translations ja-JP.yml (German) * New translations ja-JP.yml (Chinese Simplified) * New translations ja-JP.yml (English) * New translations ja-JP.yml (Indonesian) * New translations ja-JP.yml (Korean) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Chinese Traditional) * New translations ja-JP.yml (German) * New translations ja-JP.yml (English) * New translations ja-JP.yml (Chinese Traditional) * New translations ja-JP.yml (Korean) * New translations ja-JP.yml (Chinese Simplified) * New translations ja-JP.yml (Ukrainian) * New translations ja-JP.yml (Ukrainian) --- locales/de-DE.yml | 11 +++++++---- locales/en-US.yml | 20 ++++++++++---------- locales/id-ID.yml | 17 +++++++++++++++++ locales/it-IT.yml | 5 ++++- locales/ko-KR.yml | 21 ++++++++++++--------- locales/uk-UA.yml | 5 ++++- locales/zh-CN.yml | 7 ++++--- locales/zh-TW.yml | 12 +++++++++++- 8 files changed, 69 insertions(+), 29 deletions(-) diff --git a/locales/de-DE.yml b/locales/de-DE.yml index ea4a72dc0c..71ed02cd43 100644 --- a/locales/de-DE.yml +++ b/locales/de-DE.yml @@ -931,10 +931,12 @@ undefined: "Undefiniert" assign: "Zuweisen" unassign: "Entfernen" color: "Farbe" -manageCustomEmojis: "Benutzerdefinierte Emojis verwalten" +manageCustomEmojis: "Kann benutzerdefinierte Emojis verwalten" youCannotCreateAnymore: "Du hast das Erstellungslimit erreicht." cannotPerformTemporary: "Vorübergehend nicht verfügbar" cannotPerformTemporaryDescription: "Diese Aktion ist wegen des Überschreitenes des Ausführungslimits temporär nicht verfügbar. Bitte versuche es nach einiger Zeit erneut." +preset: "Vorlage" +selectFromPresets: "Aus Vorlagen wählen" _role: new: "Rolle erstellen" edit: "Rolle bearbeiten" @@ -943,7 +945,7 @@ _role: permission: "Rollenberechtigungen" descriptionOfPermission: "<b>Moderatoren</b> können grundlegende Verwaltungsaufgaben erledigen.\n<b>Administratoren</b> können alle Einstellungen der Instanz verwalten." assignTarget: "Zuweisungsart" - descriptionOfAssignTarget: "<b>Manuell</b> bedeutet, dass die Liste der Benutzer einer Rolle manuell verwaltet wird.\n<b>Konditionell</b> bedeutet, dass die Liste der Benutzer einer Rolle durch eine Bedingung automatisch verwaltet wird." + descriptionOfAssignTarget: "<b>Manuell</b> bedeutet, dass die Liste der Benutzer einer Rolle manuell verwaltet wird.\n<b>Konditional</b> bedeutet, dass die Liste der Benutzer einer Rolle durch eine Bedingung automatisch verwaltet wird." manual: "Manuell" conditional: "Konditional" condition: "Bedingung" @@ -966,7 +968,7 @@ _role: gtlAvailable: "Kann auf die globale Chronik zugreifen" ltlAvailable: "Kann auf die lokale Chronik zugreifen" canPublicNote: "Kann öffentliche Notizen erstellen" - canInvite: "Einladungscodes für diese Instanz erstellen" + canInvite: "Kann Einladungscodes für diese Instanz erstellen" canManageCustomEmojis: "Benutzerdefinierte Emojis verwalten" driveCapacity: "Drive-Kapazität" pinMax: "Maximale Anzahl an angehefteten Notizen" @@ -979,6 +981,7 @@ _role: userEachUserListsMax: "Maximale Anzahl an Benutzerlisten" rateLimitFactor: "Versuchsanzahl" descriptionOfRateLimitFactor: "Je niedriger desto weniger restriktiv, je höher destro restriktiver." + canHideAds: "Kann Werbung ausblenden" _condition: isLocal: "Lokaler Benutzer" isRemote: "Benutzer fremder Instanz" @@ -1023,7 +1026,7 @@ _accountDelete: _ad: back: "Zurück" reduceFrequencyOfThisAd: "Diese Werbung weniger anzeigen" - hide: "Nie anzeigen" + hide: "Ausblenden" _forgotPassword: enterEmail: "Gib die Email-Adresse ein, mit der du dich registriert hast. An diese wird ein Link gesendet, mit dem du dein Passwort zurücksetzen kannst." ifNoEmail: "Solltest du bei der Registrierung keine Email-Adresse angegeben haben, wende dich bitte an den Administrator." diff --git a/locales/en-US.yml b/locales/en-US.yml index b92ea24f2c..4cecca84f0 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -935,7 +935,7 @@ manageCustomEmojis: "Manage Custom Emojis" youCannotCreateAnymore: "You've hit the creation limit." cannotPerformTemporary: "Temporarily unavailable" cannotPerformTemporaryDescription: "This action cannot be performed temporarily due to exceeding the execution limit. Please wait for a while and then try again." -preset: "Presets" +preset: "Preset" selectFromPresets: "Choose from presets" _role: new: "New role" @@ -954,10 +954,10 @@ _role: descriptionOfIsPublic: "Anyone will be able to view a list of users assigned to this role. In addition, this role will be displayed in the profiles of assigned users." options: "Role options" policies: "Policies" - baseRole: "Base role" - useBaseValue: "Use base role value" + baseRole: "Role template" + useBaseValue: "Use role template value" chooseRoleToAssign: "Select the role to assign" - canEditMembersByModerator: "Allow moderators to edit the list members of this role" + canEditMembersByModerator: "Allow moderators to edit the list of members for this role" descriptionOfCanEditMembersByModerator: "When turned on, moderators as well as administrators will be able to assign and unassign users to this role. When turned off, only administrators will be able to assign users." priority: "Priority" _priority: @@ -965,11 +965,11 @@ _role: middle: "Medium" high: "High" _options: - gtlAvailable: "Viewing the global timeline" - ltlAvailable: "Viewing the local timeline" + gtlAvailable: "Can view the global timeline" + ltlAvailable: "Can view the local timeline" canPublicNote: "Can send public notes" - canInvite: "Create instance invite codes" - canManageCustomEmojis: "Manage Custom Emojis" + canInvite: "Can create instance invite codes" + canManageCustomEmojis: "Can manage custom emojis" driveCapacity: "Drive capacity" pinMax: "Maximum number of pinned notes" antennaMax: "Maximum number of antennas" @@ -981,7 +981,7 @@ _role: userEachUserListsMax: "Maximum number of users within a user list" rateLimitFactor: "Rate limit" descriptionOfRateLimitFactor: "Lower rate limits are less restrictive, higher ones more restrictive. " - canHideAds: "Remove ads" + canHideAds: "Can hide ads" _condition: isLocal: "Local user" isRemote: "Remote user" @@ -1026,7 +1026,7 @@ _accountDelete: _ad: back: "Back" reduceFrequencyOfThisAd: "Show this ad less" - hide: "Never show" + hide: "Hide" _forgotPassword: enterEmail: "Enter the email address you used to register. A link with which you can reset your password will then be sent to it." ifNoEmail: "If you did not use an email during registration, please contact the instance administrator instead." diff --git a/locales/id-ID.yml b/locales/id-ID.yml index e3fa177f3d..7f7d582ee3 100644 --- a/locales/id-ID.yml +++ b/locales/id-ID.yml @@ -2,6 +2,7 @@ _lang_: "Bahasa Indonesia" headlineMisskey: "Jaringan terhubung melalui catatan" introMisskey: "Selamat datang! Misskey adalah perangkat mikroblog tercatu bersifat sumber terbuka.\nMulailah menuliskan catatan, bagikan peristiwa terkini, serta ceritakan segala tentangmu.📡\nTunjukkan juga reaksimu pada catatan pengguna lain.👍\nMari jelajahi dunia baru🚀" +poweredByMisskeyDescription: "{name} adalah sebuah layanan (instance) yang menggunakan platform sumber terbuka <b>Misskey</b>." monthAndDay: "{day} {month}" search: "Penelusuran" notifications: "Pemberitahuan" @@ -47,6 +48,7 @@ deleteAndEdit: "Hapus dan sunting" deleteAndEditConfirm: "Apakah kamu yakin ingin menghapus note ini dan menyuntingnya? Kamu akan kehilangan semua reaksi, renote dan balasan di note ini." addToList: "Tambahkan ke daftar" sendMessage: "Kirim pesan" +copyRSS: "Salin RSS" copyUsername: "Salin nama pengguna" searchUser: "Cari pengguna" reply: "Balas" @@ -383,6 +385,7 @@ administrator: "Admin" token: "Token" twoStepAuthentication: "Otentikasi dua faktor" moderator: "Moderator" +moderation: "Moderasi" nUsersMentioned: "{n} pengguna disebut" securityKey: "Kunci keamanan" securityKeyName: "Nama kunci" @@ -449,6 +452,7 @@ language: "Bahasa" uiLanguage: "Bahasa antarmuka pengguna" groupInvited: "Telah diundang ke grup" aboutX: "Tentang {x}" +emojiStyle: "Gaya emoji" disableDrawer: "Jangan gunakan menu bergaya laci" youHaveNoGroups: "Kamu tidak memiliki grup" joinOrCreateGroup: "Bergabunglah dengan grup atau kamu dapat membuat grupmu sendiri." @@ -561,6 +565,7 @@ author: "Pembuat" leaveConfirm: "Ada perubahan yang belum disimpan. Apakah kamu ingin membuangnya?" manage: "Manajemen" plugins: "Plugin" +preferencesBackups: "Aturan pencadangan" deck: "Dek" undeck: "Keluar dari dek" useBlurEffectForModal: "Gunakan efek buram untuk modal" @@ -706,6 +711,7 @@ accentColor: "Aksen" textColor: "Teks" saveAs: "Simpan sebagai…" advanced: "Tingkat lanjut" +advancedSettings: "Pengaturan Lanjut" value: "Nilai" createdAt: "Dibuat pada" updatedAt: "Diperbarui pada" @@ -850,10 +856,21 @@ rateLimitExceeded: "Batas sudah terlampaui" cropImage: "potong gambar" cropImageAsk: "Ingin memotong gambar?" file: "Berkas" +noEmailServerWarning: "Mail Server tidak disetel." +recommended: "Disarankan" +check: "Cek" +deleteAccount: "Hapus Akun" +logoutConfirm: "Anda yakin ingin keluar?" +lastActiveDate: "Terakhir digunakan" +statusbar: "Bilah status" +pleaseSelect: "Pilih opsi..." reverse: "Balik" colored: "Diwarnai" +refreshInterval: "Jeda pembaharuan" label: "Label" +type: "Tipe" localOnly: "Hanya lokal" +shuffle: "Acak" account: "Akun" like: "Suka" unlike: "Tidak Suka" diff --git a/locales/it-IT.yml b/locales/it-IT.yml index bedeb94749..3879932e20 100644 --- a/locales/it-IT.yml +++ b/locales/it-IT.yml @@ -836,7 +836,7 @@ hide: "Nascondere" leaveGroup: "Esci dal gruppo" leaveGroupConfirm: "Uscire da「{name}」?" useDrawerReactionPickerForMobile: "Mostra sul drawer da dispositivo mobile" -welcomeBackWithName: "Eccoti di nuovo, {name}! Ciao!" +welcomeBackWithName: "Ciao, {name}! Eccoti di nuovo!" clickToFinishEmailVerification: "Fai click su [{ok}] per completare la verifica dell'indirizzo email." overridedDeviceKind: "Tipo di dispositivo" smartphone: "Smartphone" @@ -935,6 +935,8 @@ manageCustomEmojis: "Gestisci le emoji personalizzate" youCannotCreateAnymore: "Non puoi creare, hai raggiunto il limite." cannotPerformTemporary: "Indisponibilità temporanea" cannotPerformTemporaryDescription: "L'attività non può essere svolta, poiché si è raggiunto il limite di esecuzioni possibili. Per favore, riprova più tardi." +preset: "Preimpostato" +selectFromPresets: "Seleziona preimpostato" _role: new: "Nuovo ruolo" edit: "Modifica ruolo" @@ -979,6 +981,7 @@ _role: userEachUserListsMax: "Quantità massima di profili per lista" rateLimitFactor: "Limite del rapporto" descriptionOfRateLimitFactor: "I rapporti più bassi sono meno restrittivi, quelli più alti lo sono di più." + canHideAds: "Può nascondere i banner" _condition: isLocal: "Profilo locale" isRemote: "Profilo remoto" diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml index 628a631d28..4605e82960 100644 --- a/locales/ko-KR.yml +++ b/locales/ko-KR.yml @@ -935,12 +935,14 @@ manageCustomEmojis: "커스텀 이모지 관리" youCannotCreateAnymore: "더 이상 생성할 수 없습니다." cannotPerformTemporary: "일시적으로 사용할 수 없음" cannotPerformTemporaryDescription: "조작 횟수 제한을 초과하여 일시적으로 사용이 불가합니다. 잠시 후 다시 시도해 주세요." +preset: "프리셋" +selectFromPresets: "프리셋에서 선택" _role: new: "새 역할 생성" edit: "역할 수정" name: "역할 이름" description: "역할 설명" - permission: "역할의 권한" + permission: "역할 권한" descriptionOfPermission: "<b>모더레이터</b>는 기본적인 중재와 관련된 작업을 수행할 수 있습니다.\n<b>관리자</b>는 인스턴스의 모든 설정을 변경할 수 있습니다." assignTarget: "할당 대상" descriptionOfAssignTarget: "<b>수동</b>을 선택하면 누가 이 역할에 포함되는지를 수동으로 관리할 수 있습니다.\n<b>조건부</b>를 선택하면 조건을 설정해 일치하는 사용자를 자동으로 포함되게 할 수 있습니다." @@ -948,7 +950,7 @@ _role: conditional: "조건부" condition: "조건" isConditionalRole: "조건부 역할입니다." - isPublic: "공개 역할" + isPublic: "역할 공개" descriptionOfIsPublic: "역할에 할당된 사용자를 누구나 볼 수 있습니다. 또한 사용자 프로필에 이 역할이 표시됩니다." options: "옵션" policies: "정책" @@ -956,7 +958,7 @@ _role: useBaseValue: "기본값 사용" chooseRoleToAssign: "할당할 역할 선택" canEditMembersByModerator: "모더레이터의 역할 수정 허용" - descriptionOfCanEditMembersByModerator: "이 옵션을 켜면 모더레이터도 이 역할에 사용자를 추가하거나 삭제할 수 있습니다. 꺼져 있으면 관리자만 가능합니다." + descriptionOfCanEditMembersByModerator: "이 옵션을 켜면 모더레이터도 이 역할에 사용자를 할당하거나 삭제할 수 있습니다. 꺼져 있으면 관리자만 할당이 가능합니다." priority: "우선순위" _priority: low: "낮음" @@ -971,19 +973,20 @@ _role: driveCapacity: "드라이브 용량" pinMax: "고정할 수 있는 노트 수" antennaMax: "최대 안테나 생성 허용 수" - wordMuteMax: "뮤트할 수 있는 단어의 수" - webhookMax: "생성할 수 있는 WebHook의 수" + wordMuteMax: "단어 뮤트할 수 있는 문자 수" + webhookMax: "생성할 수 있는 웹훅 수" clipMax: "생성할 수 있는 클립 수" noteEachClipsMax: "각 클립에 추가할 수 있는 노트 수" - userListMax: "생성할 수 있는 리스트 수" - userEachUserListsMax: "리스트당 최대 사용자 수" + userListMax: "생성할 수 있는 유저 리스트 수" + userEachUserListsMax: "유저 리스트당 최대 사용자 수" rateLimitFactor: "속도 제한" descriptionOfRateLimitFactor: "작을수록 제한이 완화되고, 클수록 제한이 강화됩니다." + canHideAds: "광고 숨기기" _condition: isLocal: "로컬 사용자" isRemote: "리모트 사용자" - createdLessThan: "다음 일수 이내에 가입한 유저" - createdMoreThan: "다음 일수 이상 활동한 유저" + createdLessThan: "가압한 지 다음 일수 이내인 유저" + createdMoreThan: "가입한 지 다음 일수 이상인 유저" followersLessThanOrEq: "팔로워 수가 다음 이하인 유저" followersMoreThanOrEq: "팔로워 수가 다음 이상인 유저" followingLessThanOrEq: "팔로잉 수가 다음 이하인 유저" diff --git a/locales/uk-UA.yml b/locales/uk-UA.yml index 992275c7e6..72397fb068 100644 --- a/locales/uk-UA.yml +++ b/locales/uk-UA.yml @@ -586,7 +586,7 @@ pluginTokenRequestedDescription: "Цей плагін зможе викорис notificationType: "Тип сповіщення" edit: "Редагувати" useStarForReactionFallback: "Використовувати ★ як запасний варіант, якщо емодзі реакції невідомий" -emailServer: "Сервер електронної пошти" +emailServer: "Email сервер" enableEmail: "Увімкнути функцію доставки пошти" emailConfigInfo: "Використовується для підтвердження електронної пошти підчас реєстрації, а також для відновлення паролю." email: "E-mail" @@ -892,7 +892,10 @@ unsubscribePushNotification: "Вимкнути push-сповіщення" windowMaximize: "Розгорнути" windowRestore: "Відновити" caption: "Підпис" +tools: "Інструменти" like: "Вподобати" +unlike: "Не вподобати" +numberOfLikes: "Вподобання" show: "Відображення" color: "Колір" _role: diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index c857f04f95..d026422ca7 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -607,7 +607,7 @@ wordMute: "文字屏蔽" regexpError: "正则表达式错误" regexpErrorDescription: "{tab} 屏蔽文字的第 {line} 行的正则表达式有错误:" instanceMute: "实例的屏蔽" -userSaysSomething: "{name}说了什么,但是被您屏蔽了" +userSaysSomething: "{name}说了什么,但是被屏蔽词过滤了" makeActive: "启用" display: "显示" copy: "复制" @@ -826,7 +826,7 @@ makeReactionsPublicDescription: "将您发表过的回应设置成公开可见 classic: "经典" muteThread: "屏蔽帖子列表" unmuteThread: "取消屏蔽帖子列表" -ffVisibility: "连接的可见范围" +ffVisibility: "关注关系的可见范围" ffVisibilityDescription: "您可以设置您的关注/关注者信息的公开范围" continueThread: "查看更多帖子" deleteAccountConfirm: "将要删除账户。是否确认?" @@ -981,6 +981,7 @@ _role: userEachUserListsMax: "单个用户列表内用户数量限制" rateLimitFactor: "速率限制" descriptionOfRateLimitFactor: "值越小限制越少,值越大限制越多。" + canHideAds: "可以隐藏广告" _condition: isLocal: "是本地用户" isRemote: "是远程用户" @@ -1008,7 +1009,7 @@ _emailUnavailable: mx: "邮件服务器不正确" smtp: "邮件服务器没有响应" _ffVisibility: - public: "发布" + public: "公开" followers: "只有关注你的用户能看到" private: "私密" _signup: diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml index 50f4a12c7d..2a2cdfbc72 100644 --- a/locales/zh-TW.yml +++ b/locales/zh-TW.yml @@ -389,7 +389,7 @@ administrator: "管理員" token: "權杖" twoStepAuthentication: "兩階段驗證" moderator: "監察員" -moderation: "言論調節" +moderation: "監察" nUsersMentioned: "提到了{n}" securityKey: "安全金鑰" securityKeyName: "金鑰名稱" @@ -932,8 +932,11 @@ assign: "指派" unassign: "取消指派" color: "顏色" manageCustomEmojis: "管理自訂表情符號" +youCannotCreateAnymore: "您無法再建立更多了。" cannotPerformTemporary: "暫時無法進行" cannotPerformTemporaryDescription: "由於超過操作次數限制,暫時無法進行。請過一段時間之後再嘗試。" +preset: "預設值" +selectFromPresets: "從預設值中選擇" _role: new: "建立角色" edit: "編輯角色" @@ -970,8 +973,15 @@ _role: driveCapacity: "雲端硬碟容量" pinMax: "置頂貼文的最大數量" antennaMax: "可建立的天線數量" + wordMuteMax: "靜音文字的最大字數" webhookMax: "可建立的Webhook數量" clipMax: "可建立的摘錄數量" + noteEachClipsMax: "摘錄內貼文的最大數量" + userListMax: "可建立的使用者清單數量" + userEachUserListsMax: "使用者清單內使用者的最大數量" + rateLimitFactor: "速率限制" + descriptionOfRateLimitFactor: "值越小限制越少,值越大限制越多。" + canHideAds: "不顯示廣告" _condition: isLocal: "本地使用者" isRemote: "遠端使用者" From a160b01cffe563225f2b6716b4d701d941241ef3 Mon Sep 17 00:00:00 2001 From: nullobsi <me@nullob.si> Date: Wed, 18 Jan 2023 16:16:52 -0800 Subject: [PATCH 21/70] enhance: Alt text in image viewer (#9109) * Alt text in image viewer * :art: Co-authored-by: tamaina <tamaina@hotmail.co.jp> --- .../frontend/src/components/MkMediaImage.vue | 2 +- .../frontend/src/components/MkMediaList.vue | 54 ++++++++++++++++++- 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/packages/frontend/src/components/MkMediaImage.vue b/packages/frontend/src/components/MkMediaImage.vue index 9912faffe8..c0638c0feb 100644 --- a/packages/frontend/src/components/MkMediaImage.vue +++ b/packages/frontend/src/components/MkMediaImage.vue @@ -13,7 +13,7 @@ :href="image.url" :title="image.name" > - <ImgWithBlurhash :hash="image.blurhash" :src="url" :alt="image.comment" :title="image.comment" :cover="false"/> + <ImgWithBlurhash :hash="image.blurhash" :src="url" :alt="image.comment || image.name" :title="image.comment || image.name" :cover="false"/> <div v-if="image.type === 'image/gif'" class="gif">GIF</div> </a> <button v-tooltip="$ts.hide" class="_button hide" @click="hide = true"><i class="ti ti-eye-off"></i></button> diff --git a/packages/frontend/src/components/MkMediaList.vue b/packages/frontend/src/components/MkMediaList.vue index c6f8612182..f263ae0ce9 100644 --- a/packages/frontend/src/components/MkMediaList.vue +++ b/packages/frontend/src/components/MkMediaList.vue @@ -45,7 +45,8 @@ onMounted(() => { src: media.url, w: media.properties.width, h: media.properties.height, - alt: media.name, + alt: media.comment || media.name, + comment: media.comment || media.name, }; if (media.properties.orientation != null && media.properties.orientation >= 5) { [item.w, item.h] = [item.h, item.w]; @@ -69,6 +70,7 @@ onMounted(() => { }, imageClickAction: 'close', tapAction: 'toggle-controls', + bgOpacity: 1, pswpModule: PhotoSwipe, }); @@ -88,9 +90,28 @@ onMounted(() => { [itemData.w, itemData.h] = [itemData.h, itemData.w]; } itemData.msrc = file.thumbnailUrl; + itemData.alt = file.comment || file.name; + itemData.comment = file.comment || file.name; itemData.thumbCropped = true; }); + lightbox.on('uiRegister', () => { + lightbox.pswp.ui.registerElement({ + name: 'altText', + className: 'pwsp__alt-text-container', + appendTo: 'wrapper', + onInit: (el, pwsp) => { + let textBox = document.createElement('p'); + textBox.className = 'pwsp__alt-text _acrylic'; + el.appendChild(textBox); + + pwsp.on('change', (a) => { + textBox.textContent = pwsp.currSlide.data.comment; + }); + }, + }); + }); + lightbox.init(); }); @@ -185,5 +206,36 @@ const previewable = (file: misskey.entities.DriveFile): boolean => { // なぜか機能しない //z-index: v-bind(pswpZIndex); z-index: 2000000; + --pswp-bg: var(--modalBg); +} + +.pswp__bg { + background: var(--modalBg); + backdrop-filter: var(--modalBgFilter); +} + +.pwsp__alt-text-container { + display: flex; + flex-direction: row; + align-items: center; + + position: absolute; + bottom: 30px; + left: 50%; + transform: translateX(-50%); + + width: 75%; + max-width: 800px; +} + +.pwsp__alt-text { + color: var(--fg); + margin: 0 auto; + text-align: center; + padding: var(--margin); + border-radius: var(--radius); + max-height: 8em; + overflow-y: auto; + text-shadow: var(--bg) 0 0 10px, var(--bg) 0 0 3px, var(--bg) 0 0 3px; } </style> From ed9a49687ea516d6a6ff93c1187a65065fb53d5a Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Thu, 19 Jan 2023 09:17:18 +0900 Subject: [PATCH 22/70] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 69f1123f09..05ad4f8a80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ You should also include the user name that made the change. - Playのscriptの文字数制限を緩和 - AiScript GUIの強化 - 存在しないカスタム絵文字をテキストで表示するように +- Alt text in image viewer ### Bugfixes - playを削除する手段がなかったのを修正 From 336d8fe785d5b26f85088659db022a45b74c5d9f Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Thu, 19 Jan 2023 10:29:30 +0900 Subject: [PATCH 23/70] =?UTF-8?q?feat(client):=20=E3=83=AA=E3=82=A2?= =?UTF-8?q?=E3=82=AF=E3=82=B7=E3=83=A7=E3=83=B3=E4=B8=80=E8=A6=A7=E8=A9=B3?= =?UTF-8?q?=E7=B4=B0=E3=83=80=E3=82=A4=E3=82=A2=E3=83=AD=E3=82=B0=E3=82=92?= =?UTF-8?q?=E8=A1=A8=E7=A4=BA=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolve #9634 --- CHANGELOG.md | 1 + locales/ja-JP.yml | 1 + .../src/components/MkReactedUsersDialog.vue | 92 +++++++++++++++++++ .../src/components/MkUserCardMini.vue | 20 ++-- .../frontend/src/scripts/get-note-menu.ts | 12 +++ 5 files changed, 120 insertions(+), 6 deletions(-) create mode 100644 packages/frontend/src/components/MkReactedUsersDialog.vue diff --git a/CHANGELOG.md b/CHANGELOG.md index 05ad4f8a80..21fecb6817 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ You should also include the user name that made the change. - Playのプリセットを追加 - Playのscriptの文字数制限を緩和 - AiScript GUIの強化 +- リアクション一覧詳細ダイアログを表示できるように - 存在しないカスタム絵文字をテキストで表示するように - Alt text in image viewer diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index f0e53cb814..5721fcc887 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -110,6 +110,7 @@ clickToShow: "クリックして表示" sensitive: "閲覧注意" add: "追加" reaction: "リアクション" +reactions: "リアクション" reactionSetting: "ピッカーに表示するリアクション" reactionSettingDescription2: "ドラッグして並び替え、クリックして削除、+を押して追加します。" rememberNoteVisibility: "公開範囲を記憶する" diff --git a/packages/frontend/src/components/MkReactedUsersDialog.vue b/packages/frontend/src/components/MkReactedUsersDialog.vue new file mode 100644 index 0000000000..2a8dffc014 --- /dev/null +++ b/packages/frontend/src/components/MkReactedUsersDialog.vue @@ -0,0 +1,92 @@ +<template> +<MkModalWindow + ref="dialog" + :width="400" + :height="450" + @close="dialog.close()" + @closed="emit('closed')" +> + <template #header>{{ i18n.ts.reactions }}</template> + + <MkSpacer :margin-min="20" :margin-max="28"> + <div v-if="note" class="_gaps"> + <div :class="$style.tabs"> + <button v-for="reaction in reactions" :key="reaction" :class="[$style.tab, { [$style.tabActive]: tab === reaction }]" class="_button" @click="tab = reaction"> + <MkReactionIcon :reaction="reaction"/> + <span style="margin-left: 4px;">{{ note.reactions[reaction] }}</span> + </button> + </div> + <MkA v-for="user in users" :key="user.id" :to="userPage(user)"> + <MkUserCardMini :user="user" :with-chart="false"/> + </MkA> + </div> + <div v-else> + <MkLoading/> + </div> + </MkSpacer> +</MkModalWindow> +</template> + +<script lang="ts" setup> +import { onMounted, watch } from 'vue'; +import * as misskey from 'misskey-js'; +import MkModalWindow from '@/components/MkModalWindow.vue'; +import MkReactionIcon from '@/components/MkReactionIcon.vue'; +import MkUserCardMini from '@/components/MkUserCardMini.vue'; +import { userPage } from '@/filters/user'; +import { i18n } from '@/i18n'; +import * as os from '@/os'; + +const emit = defineEmits<{ + (ev: 'closed'): void, +}>(); + +const props = defineProps<{ + noteId: misskey.entities.Note['id']; +}>(); + +const dialog = $shallowRef<InstanceType<typeof MkModalWindow>>(); + +let note = $ref<misskey.entities.Note>(); +let tab = $ref<string>(); +let reactions = $ref<string[]>(); +let users = $ref(); + +watch($$(tab), async () => { + const res = await os.api('notes/reactions', { + noteId: props.noteId, + type: tab, + limit: 30, + }); + + users = res.map(x => x.user); +}); + +onMounted(() => { + os.api('notes/show', { + noteId: props.noteId, + }).then((res) => { + reactions = Object.keys(res.reactions); + tab = reactions[0]; + note = res; + }); +}); +</script> + +<style lang="scss" module> +.tabs { + display: flex; + gap: 8px; + flex-wrap: wrap; +} + +.tab { + padding: 4px 6px; + border: solid 1px var(--divider); + border-radius: 6px; +} + +.tabActive { + border-color: var(--accent); +} +</style> diff --git a/packages/frontend/src/components/MkUserCardMini.vue b/packages/frontend/src/components/MkUserCardMini.vue index ec199ad277..457504e6ca 100644 --- a/packages/frontend/src/components/MkUserCardMini.vue +++ b/packages/frontend/src/components/MkUserCardMini.vue @@ -11,20 +11,28 @@ <script lang="ts" setup> import * as misskey from 'misskey-js'; +import { onMounted } from 'vue'; import MkMiniChart from '@/components/MkMiniChart.vue'; import * as os from '@/os'; import { acct } from '@/filters/user'; -const props = defineProps<{ +const props = withDefaults(defineProps<{ user: misskey.entities.User; -}>(); + withChart: boolean; +}>(), { + withChart: true, +}); let chartValues = $ref<number[] | null>(null); -os.apiGet('charts/user/notes', { userId: props.user.id, limit: 16 + 1, span: 'day' }).then(res => { - // 今日のぶんの値はまだ途中の値であり、それも含めると大抵の場合前日よりも下降しているようなグラフになってしまうため今日は弾く - res.inc.splice(0, 1); - chartValues = res.inc; +onMounted(() => { + if (props.withChart) { + os.apiGet('charts/user/notes', { userId: props.user.id, limit: 16 + 1, span: 'day' }).then(res => { + // 今日のぶんの値はまだ途中の値であり、それも含めると大抵の場合前日よりも下降しているようなグラフになってしまうため今日は弾く + res.inc.splice(0, 1); + chartValues = res.inc; + }); + } }); </script> diff --git a/packages/frontend/src/scripts/get-note-menu.ts b/packages/frontend/src/scripts/get-note-menu.ts index 54de3d95df..7a426ec722 100644 --- a/packages/frontend/src/scripts/get-note-menu.ts +++ b/packages/frontend/src/scripts/get-note-menu.ts @@ -174,9 +174,17 @@ export function getNoteMenu(props: { url: `${url}/notes/${appearNote.id}`, }); } + function notedetails(): void { os.pageWindow(`/notes/${appearNote.id}`); } + + function showReactions(): void { + os.popup(defineAsyncComponent(() => import('@/components/MkReactedUsersDialog.vue')), { + noteId: appearNote.id, + }, {}, 'closed'); + } + async function translate(): Promise<void> { if (props.translation.value != null) return; props.translating.value = true; @@ -206,6 +214,10 @@ export function getNoteMenu(props: { icon: 'ti ti-info-circle', text: i18n.ts.details, action: notedetails, + }, { + icon: 'ti ti-users', + text: i18n.ts.reactions, + action: showReactions, }, { icon: 'ti ti-copy', text: i18n.ts.copyContent, From 02b6595d76bbe1e0cc256f2555d2b5e28f38f1ba Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Thu, 19 Jan 2023 10:39:23 +0900 Subject: [PATCH 24/70] :art: --- packages/frontend/src/pages/admin/roles.vue | 2 +- packages/frontend/src/pages/user-info.vue | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/frontend/src/pages/admin/roles.vue b/packages/frontend/src/pages/admin/roles.vue index 6e0c038982..ff8f8a356f 100644 --- a/packages/frontend/src/pages/admin/roles.vue +++ b/packages/frontend/src/pages/admin/roles.vue @@ -7,7 +7,7 @@ <MkButton primary rounded @click="create"><i class="ti ti-plus"></i> {{ i18n.ts._role.new }}</MkButton> <MkFolder> <template #label>{{ i18n.ts._role.baseRole }}</template> - <div class="_gaps"> + <div class="_gaps_s"> <MkFolder> <template #label>{{ i18n.ts._role._options.rateLimitFactor }}</template> <template #suffix>{{ Math.floor(policies.rateLimitFactor * 100) }}%</template> diff --git a/packages/frontend/src/pages/user-info.vue b/packages/frontend/src/pages/user-info.vue index 13942da7c7..7ba8a3d16b 100644 --- a/packages/frontend/src/pages/user-info.vue +++ b/packages/frontend/src/pages/user-info.vue @@ -113,7 +113,8 @@ <div v-for="role in info.roles" :key="role.id" :class="$style.roleItem"> <MkRolePreview :class="$style.role" :role="role"/> - <button class="_button" :class="$style.roleUnassign" @click="unassignRole(role, $event)"><i class="ti ti-x"></i></button> + <button v-if="role.target === 'manual'" class="_button" :class="$style.roleUnassign" @click="unassignRole(role, $event)"><i class="ti ti-x"></i></button> + <button v-else class="_button" :class="$style.roleUnassign" disabled><i class="ti ti-ban"></i></button> </div> </div> </MkFolder> From b8afabde2c65027f572d2b3e59e9697bf98c46a1 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Thu, 19 Jan 2023 16:57:37 +0900 Subject: [PATCH 25/70] add commands for build with swc --- packages/backend/.swcrc | 12 +- packages/backend/package.json | 3 + pnpm-lock.yaml | 711 +++++++++++++++++++++++++++++++++- 3 files changed, 704 insertions(+), 22 deletions(-) diff --git a/packages/backend/.swcrc b/packages/backend/.swcrc index c82564eab3..55a88456ef 100644 --- a/packages/backend/.swcrc +++ b/packages/backend/.swcrc @@ -9,7 +9,17 @@ "transform": { "legacyDecorator": true, "decoratorMetadata": true - } + }, + "experimental": { + "keepImportAssertions": true + }, + "baseUrl": ".", + "paths": { + "@/*": [ + "./src/*" + ] + }, + "target": "es2021" }, "minify": false } diff --git a/packages/backend/package.json b/packages/backend/package.json index a9ba3ebaf1..68cfbb05ad 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -7,6 +7,8 @@ "start": "node ./built/index.js", "start:test": "NODE_ENV=test node ./built/index.js", "migrate": "pnpm typeorm migration:run -d ormconfig.js", + "build:swc": "swc src -d built -D", + "watch:swc": "swc src -d built -D -w", "build": "tsc -p tsconfig.json || echo done. && tsc-alias -p tsconfig.json", "watch": "node watch.mjs", "lint": "tsc --noEmit && eslint --quiet \"src/**/*.ts\"", @@ -129,6 +131,7 @@ }, "devDependencies": { "@redocly/openapi-core": "1.0.0-beta.120", + "@swc/cli": "^0.1.59", "@swc/core": "1.3.26", "@swc/jest": "0.2.24", "@types/accepts": "1.3.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 43f34c5d95..993efd1746 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -65,6 +65,7 @@ importers: '@peertube/http-signature': 1.7.0 '@redocly/openapi-core': 1.0.0-beta.120 '@sinonjs/fake-timers': 10.0.2 + '@swc/cli': ^0.1.59 '@swc/core': 1.3.26 '@swc/jest': 0.2.24 '@tensorflow/tfjs': ^4.1.0 @@ -320,6 +321,7 @@ importers: '@tensorflow/tfjs-node': 4.1.0_seedrandom@3.0.5 devDependencies: '@redocly/openapi-core': 1.0.0-beta.120 + '@swc/cli': 0.1.59_cr4os3zuq4gmhe2qzzjtw2pxeu '@swc/core': 1.3.26 '@swc/jest': 0.2.24_@swc+core@1.3.26 '@types/accepts': 1.3.5 @@ -2116,6 +2118,11 @@ packages: resolution: {integrity: sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==} dev: true + /@sindresorhus/is/0.7.0: + resolution: {integrity: sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow==} + engines: {node: '>=4'} + dev: true + /@sindresorhus/is/4.6.0: resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} engines: {node: '>=10'} @@ -2154,6 +2161,27 @@ packages: resolution: {integrity: sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==} dev: false + /@swc/cli/0.1.59_cr4os3zuq4gmhe2qzzjtw2pxeu: + resolution: {integrity: sha512-BlX3wIxYTwdtR22dIqZ3FEIOJPqnlByAp4JY46OMZi2UXMB3ZbOzefawD2ZlLafRUWyy5NtiZZty5waKzaYRnA==} + engines: {node: '>= 12.13'} + hasBin: true + peerDependencies: + '@swc/core': ^1.2.66 + chokidar: ^3.5.1 + peerDependenciesMeta: + chokidar: + optional: true + dependencies: + '@swc/core': 1.3.26 + bin-wrapper: 4.1.0 + chokidar: 3.5.3 + commander: 7.2.0 + fast-glob: 3.2.12 + semver: 7.3.8 + slash: 3.0.0 + source-map: 0.7.4 + dev: true + /@swc/core-darwin-arm64/1.3.26: resolution: {integrity: sha512-FWWflBfKRYrUJtko2xiedC5XCa31O75IZZqnTWuLpe9g3C5tnUuF3M8LSXZS/dn6wprome1MhtG9GMPkSYkhkg==} engines: {node: '>=10'} @@ -2651,7 +2679,6 @@ packages: resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} dependencies: '@types/node': 18.11.18 - dev: false /@types/long/4.0.2: resolution: {integrity: sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==} @@ -2763,7 +2790,6 @@ packages: resolution: {integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==} dependencies: '@types/node': 18.11.18 - dev: false /@types/sanitize-html/2.8.0: resolution: {integrity: sha512-Uih6caOm3DsBYnVGOYn0A9NoTNe1c4aPStmHC/YA2JrpP9kx//jzaRcIklFvSpvVQEcpl/ZCr4DgISSf/YxTvg==} @@ -3423,6 +3449,13 @@ packages: resolution: {integrity: sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==} dev: true + /archive-type/4.0.0: + resolution: {integrity: sha512-zV4Ky0v1F8dBrdYElwTvQhweQ0P7Kwc1aluqJsYtOBP01jXcWCyW2IEfI1YiqsG+Iy7ZR+o5LF1N+PGECBxHWA==} + engines: {node: '>=4'} + dependencies: + file-type: 4.4.0 + dev: true + /archiver-utils/2.1.0: resolution: {integrity: sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==} engines: {node: '>= 6'} @@ -3880,6 +3913,43 @@ packages: resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==} dev: false + /bin-check/4.1.0: + resolution: {integrity: sha512-b6weQyEUKsDGFlACWSIOfveEnImkJyK/FGW6FAG42loyoquvjdtOIqO6yBFzHyqyVVhNgNkQxxx09SFLK28YnA==} + engines: {node: '>=4'} + dependencies: + execa: 0.7.0 + executable: 4.1.1 + dev: true + + /bin-version-check/4.0.0: + resolution: {integrity: sha512-sR631OrhC+1f8Cvs8WyVWOA33Y8tgwjETNPyyD/myRBXLkfS/vl74FmH/lFcRl9KY3zwGh7jFhvyk9vV3/3ilQ==} + engines: {node: '>=6'} + dependencies: + bin-version: 3.1.0 + semver: 5.7.1 + semver-truncate: 1.1.2 + dev: true + + /bin-version/3.1.0: + resolution: {integrity: sha512-Mkfm4iE1VFt4xd4vH+gx+0/71esbfus2LsnCGe8Pi4mndSPyT+NGES/Eg99jx8/lUGWfu3z2yuB/bt5UB+iVbQ==} + engines: {node: '>=6'} + dependencies: + execa: 1.0.0 + find-versions: 3.2.0 + dev: true + + /bin-wrapper/4.1.0: + resolution: {integrity: sha512-hfRmo7hWIXPkbpi0ZltboCMVrU+0ClXR/JgbCKKjlDjQf6igXa7OwdqNcFWQZPZTgiY7ZpzE3+LjjkLiTN2T7Q==} + engines: {node: '>=6'} + dependencies: + bin-check: 4.1.0 + bin-version-check: 4.0.0 + download: 7.1.0 + import-lazy: 3.1.0 + os-filter-obj: 2.0.0 + pify: 4.0.1 + dev: true + /binary-extensions/2.2.0: resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} engines: {node: '>=8'} @@ -3896,6 +3966,13 @@ packages: engines: {node: '>=0.8'} dev: false + /bl/1.2.3: + resolution: {integrity: sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww==} + dependencies: + readable-stream: 2.3.7 + safe-buffer: 5.2.1 + dev: true + /bl/4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} dependencies: @@ -3998,6 +4075,17 @@ packages: node-int64: 0.4.0 dev: true + /buffer-alloc-unsafe/1.1.0: + resolution: {integrity: sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==} + dev: true + + /buffer-alloc/1.2.0: + resolution: {integrity: sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==} + dependencies: + buffer-alloc-unsafe: 1.1.0 + buffer-fill: 1.0.0 + dev: true + /buffer-crc32/0.2.13: resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} @@ -4010,6 +4098,10 @@ packages: engines: {node: '>=0.4'} dev: false + /buffer-fill/1.0.0: + resolution: {integrity: sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==} + dev: true + /buffer-from/1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} @@ -4157,6 +4249,18 @@ packages: responselike: 3.0.0 dev: false + /cacheable-request/2.1.4: + resolution: {integrity: sha512-vag0O2LKZ/najSoUwDbVlnlCFvhBE/7mGTY2B5FgCBDcRD+oVV1HYTOwM6JZfMg/hIcM6IwnTZ1uQQL5/X3xIQ==} + dependencies: + clone-response: 1.0.2 + get-stream: 3.0.0 + http-cache-semantics: 3.8.1 + keyv: 3.0.0 + lowercase-keys: 1.0.0 + normalize-url: 2.0.1 + responselike: 1.0.2 + dev: true + /cacheable-request/7.0.2: resolution: {integrity: sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==} engines: {node: '>=8'} @@ -4228,6 +4332,16 @@ packages: /caseless/0.12.0: resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==} + /caw/2.0.1: + resolution: {integrity: sha512-Cg8/ZSBEa8ZVY9HspcGUYaK63d/bN7rqS3CYCzEGUxuYv6UlmcjzDUz2fCFFHyTvUW5Pk0I+3hkA3iXlIj6guA==} + engines: {node: '>=4'} + dependencies: + get-proxy: 2.1.0 + isurl: 1.0.0 + tunnel-agent: 0.6.0 + url-to-options: 1.0.1 + dev: true + /cbor/8.1.0: resolution: {integrity: sha512-DwGjNW9omn6EwP70aXsn7FQJx5kO12tX0bZkaTjzdVFM6/7nhA4t0EENocKGx6D2Bch9PE2KzCUf5SceBdeijg==} engines: {node: '>=12.19'} @@ -4485,6 +4599,12 @@ packages: engines: {node: '>= 0.10'} dev: false + /clone-response/1.0.2: + resolution: {integrity: sha512-yjLXh88P599UOyPTFX0POsd7WxnbsVsGohcwzHOLspIhhpalPw1BcqED8NblyZLKcGrL8dTgMlcaZxV2jAD41Q==} + dependencies: + mimic-response: 1.0.1 + dev: true + /clone-response/1.0.3: resolution: {integrity: sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==} dependencies: @@ -4639,13 +4759,17 @@ packages: /commander/2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} - dev: false /commander/5.1.0: resolution: {integrity: sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==} engines: {node: '>= 6'} dev: true + /commander/7.2.0: + resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} + engines: {node: '>= 10'} + dev: true + /commander/9.5.0: resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==} engines: {node: ^12.20.0 || >=14} @@ -4687,6 +4811,13 @@ packages: typedarray: 0.0.6 dev: false + /config-chain/1.1.13: + resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} + dependencies: + ini: 1.3.8 + proto-list: 1.2.4 + dev: true + /consola/2.15.3: resolution: {integrity: sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==} dev: false @@ -4707,7 +4838,6 @@ packages: engines: {node: '>= 0.6'} dependencies: safe-buffer: 5.2.1 - dev: false /content-type/1.0.4: resolution: {integrity: sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==} @@ -4756,7 +4886,6 @@ packages: /core-util-is/1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} - dev: false /crc-32/1.2.2: resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} @@ -4793,6 +4922,25 @@ packages: cross-spawn: 7.0.3 dev: true + /cross-spawn/5.1.0: + resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==} + dependencies: + lru-cache: 4.1.5 + shebang-command: 1.2.0 + which: 1.3.1 + dev: true + + /cross-spawn/6.0.5: + resolution: {integrity: sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==} + engines: {node: '>=4.8'} + dependencies: + nice-try: 1.0.5 + path-key: 2.0.1 + semver: 5.7.1 + shebang-command: 1.2.0 + which: 1.3.1 + dev: true + /cross-spawn/7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} @@ -5072,7 +5220,13 @@ packages: /decode-uri-component/0.2.2: resolution: {integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==} engines: {node: '>=0.10'} - dev: false + + /decompress-response/3.3.0: + resolution: {integrity: sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA==} + engines: {node: '>=4'} + dependencies: + mimic-response: 1.0.1 + dev: true /decompress-response/6.0.0: resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} @@ -5081,6 +5235,59 @@ packages: mimic-response: 3.1.0 dev: false + /decompress-tar/4.1.1: + resolution: {integrity: sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ==} + engines: {node: '>=4'} + dependencies: + file-type: 5.2.0 + is-stream: 1.1.0 + tar-stream: 1.6.2 + dev: true + + /decompress-tarbz2/4.1.1: + resolution: {integrity: sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A==} + engines: {node: '>=4'} + dependencies: + decompress-tar: 4.1.1 + file-type: 6.2.0 + is-stream: 1.1.0 + seek-bzip: 1.0.6 + unbzip2-stream: 1.4.3 + dev: true + + /decompress-targz/4.1.1: + resolution: {integrity: sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w==} + engines: {node: '>=4'} + dependencies: + decompress-tar: 4.1.1 + file-type: 5.2.0 + is-stream: 1.1.0 + dev: true + + /decompress-unzip/4.0.1: + resolution: {integrity: sha512-1fqeluvxgnn86MOh66u8FjbtJpAFv5wgCT9Iw8rcBqQcCo5tO8eiJw7NNTrvt9n4CRBVq7CstiS922oPgyGLrw==} + engines: {node: '>=4'} + dependencies: + file-type: 3.9.0 + get-stream: 2.3.1 + pify: 2.3.0 + yauzl: 2.10.0 + dev: true + + /decompress/4.2.1: + resolution: {integrity: sha512-e48kc2IjU+2Zw8cTb6VZcJQ3lgVbS4uuB1TfCHbiZIP/haNXm+SVyhu+87jts5/3ROpd82GSVCoNs/z8l4ZOaQ==} + engines: {node: '>=4'} + dependencies: + decompress-tar: 4.1.1 + decompress-tarbz2: 4.1.1 + decompress-targz: 4.1.1 + decompress-unzip: 4.0.1 + graceful-fs: 4.2.10 + make-dir: 1.3.0 + pify: 2.3.0 + strip-dirs: 2.1.0 + dev: true + /dedent/0.7.0: resolution: {integrity: sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==} dev: true @@ -5312,6 +5519,24 @@ packages: engines: {node: '>=12'} dev: false + /download/7.1.0: + resolution: {integrity: sha512-xqnBTVd/E+GxJVrX5/eUJiLYjCGPwMpdL+jGhGU57BvtcA7wwhtHVbXBeUk51kOpW3S7Jn3BQbN9Q1R1Km2qDQ==} + engines: {node: '>=6'} + dependencies: + archive-type: 4.0.0 + caw: 2.0.1 + content-disposition: 0.5.4 + decompress: 4.2.1 + ext-name: 5.0.0 + file-type: 8.1.0 + filenamify: 2.1.0 + get-stream: 3.0.0 + got: 8.3.2 + make-dir: 1.3.0 + p-event: 2.3.1 + pify: 3.0.0 + dev: true + /duplexer/0.1.2: resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} dev: true @@ -5322,6 +5547,10 @@ packages: readable-stream: 2.3.7 dev: false + /duplexer3/0.1.5: + resolution: {integrity: sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==} + dev: true + /duplexify/3.7.1: resolution: {integrity: sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==} dependencies: @@ -6069,6 +6298,32 @@ packages: engines: {node: '>=0.8.x'} dev: false + /execa/0.7.0: + resolution: {integrity: sha512-RztN09XglpYI7aBBrJCPW95jEH7YF1UEPOoX9yDhUTPdp7mK+CQvnLTuD10BNXZ3byLTu2uehZ8EcKT/4CGiFw==} + engines: {node: '>=4'} + dependencies: + cross-spawn: 5.1.0 + get-stream: 3.0.0 + is-stream: 1.1.0 + npm-run-path: 2.0.2 + p-finally: 1.0.0 + signal-exit: 3.0.7 + strip-eof: 1.0.0 + dev: true + + /execa/1.0.0: + resolution: {integrity: sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==} + engines: {node: '>=6'} + dependencies: + cross-spawn: 6.0.5 + get-stream: 4.1.0 + is-stream: 1.1.0 + npm-run-path: 2.0.2 + p-finally: 1.0.0 + signal-exit: 3.0.7 + strip-eof: 1.0.0 + dev: true + /execa/4.1.0: resolution: {integrity: sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==} engines: {node: '>=10'} @@ -6163,6 +6418,21 @@ packages: jest-util: 29.3.1 dev: true + /ext-list/2.2.2: + resolution: {integrity: sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA==} + engines: {node: '>=0.10.0'} + dependencies: + mime-db: 1.52.0 + dev: true + + /ext-name/5.0.0: + resolution: {integrity: sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ==} + engines: {node: '>=4'} + dependencies: + ext-list: 2.2.2 + sort-keys-length: 1.0.1 + dev: true + /ext/1.7.0: resolution: {integrity: sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==} dependencies: @@ -6375,12 +6645,51 @@ packages: token-types: 5.0.1 dev: false + /file-type/3.9.0: + resolution: {integrity: sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==} + engines: {node: '>=0.10.0'} + dev: true + + /file-type/4.4.0: + resolution: {integrity: sha512-f2UbFQEk7LXgWpi5ntcO86OeA/cC80fuDDDaX/fZ2ZGel+AF7leRQqBBW1eJNiiQkrZlAoM6P+VYP5P6bOlDEQ==} + engines: {node: '>=4'} + dev: true + + /file-type/5.2.0: + resolution: {integrity: sha512-Iq1nJ6D2+yIO4c8HHg4fyVb8mAJieo1Oloy1mLLaB2PvezNedhBVm+QU7g0qM42aiMbRXTxKKwGD17rjKNJYVQ==} + engines: {node: '>=4'} + dev: true + + /file-type/6.2.0: + resolution: {integrity: sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg==} + engines: {node: '>=4'} + dev: true + + /file-type/8.1.0: + resolution: {integrity: sha512-qyQ0pzAy78gVoJsmYeNgl8uH8yKhr1lVhW7JbzJmnlRi0I4R2eEDEJZVKG8agpDnLpacwNbDhLNG/LMdxHD2YQ==} + engines: {node: '>=6'} + dev: true + /filelist/1.0.4: resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} dependencies: minimatch: 5.1.2 dev: false + /filename-reserved-regex/2.0.0: + resolution: {integrity: sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ==} + engines: {node: '>=4'} + dev: true + + /filenamify/2.1.0: + resolution: {integrity: sha512-ICw7NTT6RsDp2rnYKVd8Fu4cr6ITzGy3+u4vUujPkabyaz+03F24NWEX7fs5fp+kBonlaqPH8fAO2NM+SXt/JA==} + engines: {node: '>=4'} + dependencies: + filename-reserved-regex: 2.0.0 + strip-outer: 1.0.1 + trim-repeated: 1.0.0 + dev: true + /fill-range/4.0.0: resolution: {integrity: sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==} engines: {node: '>=0.10.0'} @@ -6429,6 +6738,13 @@ packages: path-exists: 4.0.0 dev: true + /find-versions/3.2.0: + resolution: {integrity: sha512-P8WRou2S+oe222TOCHitLy8zj+SIsVJh52VP4lvXkaFVnOFFdoWv1H1Jjvel1aI6NCFOAaeAVm8qrI0odiLcww==} + engines: {node: '>=6'} + dependencies: + semver-regex: 2.0.0 + dev: true + /findup-sync/2.0.0: resolution: {integrity: sha512-vs+3unmJT45eczmcAZ6zMJtxN3l/QXeccaXQx5cu/MeJMhewVfoWZqibRkOxPnmoR59+Zy5hjabfQc6JLSah4g==} engines: {node: '>= 0.10'} @@ -6589,9 +6905,15 @@ packages: resolution: {integrity: sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==} dev: true + /from2/2.3.0: + resolution: {integrity: sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==} + dependencies: + inherits: 2.0.4 + readable-stream: 2.3.7 + dev: true + /fs-constants/1.0.0: resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} - dev: false /fs-extra/8.1.0: resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} @@ -6751,6 +7073,33 @@ packages: resolution: {integrity: sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==} engines: {node: '>=8'} + /get-proxy/2.1.0: + resolution: {integrity: sha512-zmZIaQTWnNQb4R4fJUEp/FC51eZsc6EkErspy3xtIYStaq8EB/hDIWipxsal+E8rz0qD7f2sL/NA9Xee4RInJw==} + engines: {node: '>=4'} + dependencies: + npm-conf: 1.1.3 + dev: true + + /get-stream/2.3.1: + resolution: {integrity: sha512-AUGhbbemXxrZJRD5cDvKtQxLuYaIbNtDTK8YqupCI393Q2KSTreEsLUN3ZxAWFGiKTzL6nKuzfcIvieflUX9qA==} + engines: {node: '>=0.10.0'} + dependencies: + object-assign: 4.1.1 + pinkie-promise: 2.0.1 + dev: true + + /get-stream/3.0.0: + resolution: {integrity: sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==} + engines: {node: '>=4'} + dev: true + + /get-stream/4.1.0: + resolution: {integrity: sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==} + engines: {node: '>=6'} + dependencies: + pump: 3.0.0 + dev: true + /get-stream/5.2.0: resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} engines: {node: '>=8'} @@ -6970,6 +7319,31 @@ packages: responselike: 3.0.0 dev: false + /got/8.3.2: + resolution: {integrity: sha512-qjUJ5U/hawxosMryILofZCkm3C84PLJS/0grRIpjAwu+Lkxxj5cxeCU25BG0/3mDSpXKTyZr8oh8wIgLaH0QCw==} + engines: {node: '>=4'} + dependencies: + '@sindresorhus/is': 0.7.0 + '@types/keyv': 3.1.4 + '@types/responselike': 1.0.0 + cacheable-request: 2.1.4 + decompress-response: 3.3.0 + duplexer3: 0.1.5 + get-stream: 3.0.0 + into-stream: 3.1.0 + is-retry-allowed: 1.2.0 + isurl: 1.0.0 + lowercase-keys: 1.0.1 + mimic-response: 1.0.1 + p-cancelable: 0.4.1 + p-timeout: 2.0.1 + pify: 3.0.0 + safe-buffer: 5.2.1 + timed-out: 4.0.1 + url-parse-lax: 3.0.0 + url-to-options: 1.0.1 + dev: true + /graceful-fs/4.2.10: resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} @@ -7113,10 +7487,20 @@ packages: dependencies: get-intrinsic: 1.1.3 + /has-symbol-support-x/1.4.2: + resolution: {integrity: sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw==} + dev: true + /has-symbols/1.0.3: resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} engines: {node: '>= 0.4'} + /has-to-string-tag-x/1.4.1: + resolution: {integrity: sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw==} + dependencies: + has-symbol-support-x: 1.4.2 + dev: true + /has-tostringtag/1.0.0: resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==} engines: {node: '>= 0.4'} @@ -7244,6 +7628,10 @@ packages: http-errors: 1.8.1 dev: false + /http-cache-semantics/3.8.1: + resolution: {integrity: sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w==} + dev: true + /http-cache-semantics/4.1.0: resolution: {integrity: sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==} dev: false @@ -7411,6 +7799,11 @@ packages: resolve-from: 4.0.0 dev: true + /import-lazy/3.1.0: + resolution: {integrity: sha512-8/gvXvX2JMn0F+CDlSC4l6kOmVaLOO3XLkksI7CI3Ud95KDYJuYur2b9P/PUt/i/pDAMd/DulQsNbbbmRRsDIQ==} + engines: {node: '>=6'} + dev: true + /import-local/3.1.0: resolution: {integrity: sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==} engines: {node: '>=8'} @@ -7447,7 +7840,6 @@ packages: /ini/1.3.8: resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} - dev: false /ini/2.0.0: resolution: {integrity: sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==} @@ -7477,6 +7869,14 @@ packages: engines: {node: '>= 0.10'} dev: false + /into-stream/3.1.0: + resolution: {integrity: sha512-TcdjPibTksa1NQximqep2r17ISRiNE9fwlfbg3F8ANdvP5/yrFTew86VcO//jk4QTaMlbjypPBq76HN2zaKfZQ==} + engines: {node: '>=4'} + dependencies: + from2: 2.3.0 + p-is-promise: 1.1.0 + dev: true + /invert-kv/1.0.0: resolution: {integrity: sha512-xgs2NH9AE66ucSq4cNG1nhSFghr5l6tdL15Pk+jl46bmmBapgoaY/AacXyaDznAqmGL99TiLSQgO/XazFSKYeQ==} engines: {node: '>=0.10.0'} @@ -7764,6 +8164,10 @@ packages: resolution: {integrity: sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==} dev: false + /is-natural-number/4.0.1: + resolution: {integrity: sha512-Y4LTamMe0DDQIIAlaer9eKebAlDSV6huy+TWhJVPlzZh2o4tRP5SQWFlLn5N0To4mDD22/qdOq+veo1cSISLgQ==} + dev: true + /is-negated-glob/1.0.0: resolution: {integrity: sha512-czXVVn/QEmgvej1f50BZ648vUI+em0xqMq2Sn+QncCLN4zj1UAxlT+kw/6ggQTOaZPd1HqKQGEqbpQVtJucWug==} engines: {node: '>=0.10.0'} @@ -7797,6 +8201,10 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} + /is-object/1.0.2: + resolution: {integrity: sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA==} + dev: true + /is-path-inside/3.0.3: resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} engines: {node: '>=8'} @@ -7805,7 +8213,6 @@ packages: /is-plain-obj/1.1.0: resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==} engines: {node: '>=0.10.0'} - dev: false /is-plain-object/2.0.4: resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==} @@ -7841,12 +8248,22 @@ packages: is-unc-path: 1.0.0 dev: false + /is-retry-allowed/1.2.0: + resolution: {integrity: sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==} + engines: {node: '>=0.10.0'} + dev: true + /is-shared-array-buffer/1.0.2: resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==} dependencies: call-bind: 1.0.2 dev: true + /is-stream/1.1.0: + resolution: {integrity: sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==} + engines: {node: '>=0.10.0'} + dev: true + /is-stream/2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} @@ -7936,7 +8353,6 @@ packages: /isarray/1.0.0: resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} - dev: false /isexe/2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} @@ -8010,6 +8426,14 @@ packages: textextensions: 3.3.0 dev: false + /isurl/1.0.0: + resolution: {integrity: sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==} + engines: {node: '>= 4'} + dependencies: + has-to-string-tag-x: 1.4.1 + is-object: 1.0.2 + dev: true + /iterare/1.2.1: resolution: {integrity: sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==} engines: {node: '>=6'} @@ -8560,6 +8984,10 @@ packages: hasBin: true dev: true + /json-buffer/3.0.0: + resolution: {integrity: sha512-CuUqjv0FUZIdXkHPI8MezCnFCdaTAacej1TZYulLoAg1h/PhwkdXFN4V/gzY4g+fMBCOV2xF+rp7t2XD2ns/NQ==} + dev: true + /json-buffer/3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} dev: false @@ -8701,6 +9129,12 @@ packages: tsscmp: 1.0.6 dev: false + /keyv/3.0.0: + resolution: {integrity: sha512-eguHnq22OE3uVoSYG0LVWNP+4ppamWr9+zWBe1bsNcovIMy6huUJFPgy4mGwCd/rnl3vOLGW1MTlu4c57CT1xA==} + dependencies: + json-buffer: 3.0.0 + dev: true + /keyv/4.5.2: resolution: {integrity: sha512-5MHbFaKn8cNSmVW7BYnijeAVlE4cYA/SVkifVgrh7yotnfhKmjuXpDKjrABLnT0SfHWV21P8ow07OGfRrNDg8g==} dependencies: @@ -9041,6 +9475,16 @@ packages: resolution: {integrity: sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==} dev: false + /lowercase-keys/1.0.0: + resolution: {integrity: sha512-RPlX0+PHuvxVDZ7xX+EBVAp4RsVxP/TdDSN2mJYdiq1Lc4Hz7EUSjUI7RZrKKlmrIzVhf6Jo2stj7++gVarS0A==} + engines: {node: '>=0.10.0'} + dev: true + + /lowercase-keys/1.0.1: + resolution: {integrity: sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==} + engines: {node: '>=0.10.0'} + dev: true + /lowercase-keys/2.0.0: resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==} engines: {node: '>=8'} @@ -9051,6 +9495,13 @@ packages: engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dev: false + /lru-cache/4.1.5: + resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} + dependencies: + pseudomap: 1.0.2 + yallist: 2.1.2 + dev: true + /lru-cache/5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} dependencies: @@ -9081,6 +9532,13 @@ packages: resolution: {integrity: sha512-3WjL8+ZDouZwKlyJBMp/4LeziLFXgleOdsYu87piGcMLqhBzCsy2QFdbtAwv757TFC/rtqd738fgJw1tFQCSgA==} dev: false + /make-dir/1.3.0: + resolution: {integrity: sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==} + engines: {node: '>=4'} + dependencies: + pify: 3.0.0 + dev: true + /make-dir/3.1.0: resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} engines: {node: '>=8'} @@ -9235,7 +9693,6 @@ packages: /mimic-response/1.0.1: resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==} engines: {node: '>=4'} - dev: false /mimic-response/3.1.0: resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} @@ -9542,6 +9999,10 @@ packages: resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==} dev: false + /nice-try/1.0.5: + resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==} + dev: true + /node-abi/3.31.0: resolution: {integrity: sha512-eSKV6s+APenqVh8ubJyiu/YhZgxQpGP66ntzUb3lY1xB9ukSRaGnx0AIxI+IM+1+IVYC1oWobgG5L3Lt9ARykQ==} engines: {node: '>=10'} @@ -9680,6 +10141,15 @@ packages: sort-keys: 1.1.2 dev: false + /normalize-url/2.0.1: + resolution: {integrity: sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw==} + engines: {node: '>=4'} + dependencies: + prepend-http: 2.0.0 + query-string: 5.1.1 + sort-keys: 2.0.0 + dev: true + /normalize-url/6.1.0: resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==} engines: {node: '>=10'} @@ -9697,6 +10167,21 @@ packages: once: 1.4.0 dev: false + /npm-conf/1.1.3: + resolution: {integrity: sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw==} + engines: {node: '>=4'} + dependencies: + config-chain: 1.1.13 + pify: 3.0.0 + dev: true + + /npm-run-path/2.0.2: + resolution: {integrity: sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==} + engines: {node: '>=4'} + dependencies: + path-key: 2.0.1 + dev: true + /npm-run-path/4.0.1: resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} engines: {node: '>=8'} @@ -9779,7 +10264,6 @@ packages: /object-assign/4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} - dev: false /object-copy/0.1.0: resolution: {integrity: sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==} @@ -9941,6 +10425,13 @@ packages: readable-stream: 2.3.7 dev: false + /os-filter-obj/2.0.0: + resolution: {integrity: sha512-uksVLsqG3pVdzzPvmAHpBK0wKxYItuzZr7SziusRPoz67tGV8rL1szZ6IdeUrbqLjGDwApBtN29eEE3IqGHOjg==} + engines: {node: '>=4'} + dependencies: + arch: 2.2.0 + dev: true + /os-locale/1.4.0: resolution: {integrity: sha512-PRT7ZORmwu2MEFt4/fv3Q+mEfN4zetKxufQrkShY2oGvUms9r8otu5HfdyIFHkYXjO7laNsoVGmM2MANfuTA8g==} engines: {node: '>=0.10.0'} @@ -9956,6 +10447,11 @@ packages: resolution: {integrity: sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==} dev: true + /p-cancelable/0.4.1: + resolution: {integrity: sha512-HNa1A8LvB1kie7cERyy21VNeHb2CWJJYqyyC2o3klWFfMGlFmWv2Z7sFgZH8ZiaYL95ydToKTFVXgMV/Os0bBQ==} + engines: {node: '>=4'} + dev: true + /p-cancelable/2.1.1: resolution: {integrity: sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==} engines: {node: '>=8'} @@ -9966,10 +10462,22 @@ packages: engines: {node: '>=12.20'} dev: false + /p-event/2.3.1: + resolution: {integrity: sha512-NQCqOFhbpVTMX4qMe8PF8lbGtzZ+LCiN7pcNrb/413Na7+TRoe1xkKUzuWa/YEJdGQ0FvKtj35EEbDoVPO2kbA==} + engines: {node: '>=6'} + dependencies: + p-timeout: 2.0.1 + dev: true + /p-finally/1.0.0: resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==} engines: {node: '>=4'} + /p-is-promise/1.1.0: + resolution: {integrity: sha512-zL7VE4JVS2IFSkR2GQKDSPEVxkoH43/p7oEnwpdCndKYJO0HVeRB7fA8TJwuLOTBREtK0ea8eHaxdwcpob5dmg==} + engines: {node: '>=4'} + dev: true + /p-limit/2.3.0: resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} engines: {node: '>=6'} @@ -10014,6 +10522,13 @@ packages: p-timeout: 3.2.0 dev: false + /p-timeout/2.0.1: + resolution: {integrity: sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA==} + engines: {node: '>=4'} + dependencies: + p-finally: 1.0.0 + dev: true + /p-timeout/3.2.0: resolution: {integrity: sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==} engines: {node: '>=8'} @@ -10129,6 +10644,11 @@ packages: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} engines: {node: '>=0.10.0'} + /path-key/2.0.1: + resolution: {integrity: sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==} + engines: {node: '>=4'} + dev: true + /path-key/3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} @@ -10257,17 +10777,25 @@ packages: resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} engines: {node: '>=0.10.0'} + /pify/3.0.0: + resolution: {integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==} + engines: {node: '>=4'} + dev: true + + /pify/4.0.1: + resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} + engines: {node: '>=6'} + dev: true + /pinkie-promise/2.0.1: resolution: {integrity: sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==} engines: {node: '>=0.10.0'} dependencies: pinkie: 2.0.4 - dev: false /pinkie/2.0.4: resolution: {integrity: sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==} engines: {node: '>=0.10.0'} - dev: false /pino-abstract-transport/1.0.0: resolution: {integrity: sha512-c7vo5OpW4wIS42hUVcT5REsL8ZljsUfBjqV/e2sFxmFEFZiq1XLUp5EYLtuDH6PEHq9W1egWqRbnLUP5FuZmOA==} @@ -10629,6 +11157,11 @@ packages: engines: {node: '>=0.10.0'} dev: false + /prepend-http/2.0.0: + resolution: {integrity: sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA==} + engines: {node: '>=4'} + dev: true + /pretty-bytes/5.6.0: resolution: {integrity: sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==} engines: {node: '>=6'} @@ -10735,6 +11268,10 @@ packages: sisteransi: 1.0.5 dev: true + /proto-list/1.2.4: + resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} + dev: true + /proxy-addr/2.0.7: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} engines: {node: '>= 0.10'} @@ -10755,6 +11292,10 @@ packages: event-stream: 3.3.4 dev: true + /pseudomap/1.0.2: + resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==} + dev: true + /psl/1.9.0: resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} @@ -10923,6 +11464,15 @@ packages: strict-uri-encode: 1.1.0 dev: false + /query-string/5.1.1: + resolution: {integrity: sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==} + engines: {node: '>=0.10.0'} + dependencies: + decode-uri-component: 0.2.2 + object-assign: 4.1.1 + strict-uri-encode: 1.1.0 + dev: true + /querystring/0.2.0: resolution: {integrity: sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==} engines: {node: '>=0.4.x'} @@ -11044,7 +11594,6 @@ packages: safe-buffer: 5.1.2 string_decoder: 1.1.1 util-deprecate: 1.0.2 - dev: false /readable-stream/3.6.0: resolution: {integrity: sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==} @@ -11353,6 +11902,12 @@ packages: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 + /responselike/1.0.2: + resolution: {integrity: sha512-/Fpe5guzJk1gPqdJLJR5u7eG/gNY4nImjbRDaVWVMRhne55TCmj2i9Q+54PBRfatRC8v/rIiv9BN0pMd9OV5EQ==} + dependencies: + lowercase-keys: 1.0.1 + dev: true + /responselike/2.0.1: resolution: {integrity: sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==} dependencies: @@ -11451,7 +12006,6 @@ packages: /safe-buffer/5.1.2: resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} - dev: false /safe-buffer/5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} @@ -11541,6 +12095,13 @@ packages: resolution: {integrity: sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==} dev: false + /seek-bzip/1.0.6: + resolution: {integrity: sha512-e1QtP3YL5tWww8uKaOCQ18UxIT2laNBXHjV/S2WYCiK4udiv8lkG89KRIoCjUagnAmCBurjF4zEVX2ByBbnCjQ==} + hasBin: true + dependencies: + commander: 2.20.3 + dev: true + /semver-greatest-satisfied-range/1.1.0: resolution: {integrity: sha512-Ny/iyOzSSa8M5ML46IAx3iXc6tfOsYU2R4AXi2UpHk60Zrgyq6eqPj/xiOfS0rRl/iiQ/rdJkVjw/5cdUyCntQ==} engines: {node: '>= 0.10'} @@ -11548,10 +12109,21 @@ packages: sver-compat: 1.5.0 dev: false + /semver-regex/2.0.0: + resolution: {integrity: sha512-mUdIBBvdn0PLOeP3TEkMH7HHeUP3GjsXCwKarjv/kGmUFOYg1VqEemKhoQpWMu6X2I8kHeuVdGibLGkVK+/5Qw==} + engines: {node: '>=6'} + dev: true + + /semver-truncate/1.1.2: + resolution: {integrity: sha512-V1fGg9i4CL3qesB6U0L6XAm4xOJiHmt4QAacazumuasc03BvtFGIMCduv01JWQ69Nv+JST9TqhSCiJoxoY031w==} + engines: {node: '>=0.10.0'} + dependencies: + semver: 5.7.1 + dev: true + /semver/5.7.1: resolution: {integrity: sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==} hasBin: true - dev: false /semver/6.3.0: resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==} @@ -11613,12 +12185,24 @@ packages: tunnel-agent: 0.6.0 dev: false + /shebang-command/1.2.0: + resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} + engines: {node: '>=0.10.0'} + dependencies: + shebang-regex: 1.0.0 + dev: true + /shebang-command/2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} dependencies: shebang-regex: 3.0.0 + /shebang-regex/1.0.0: + resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==} + engines: {node: '>=0.10.0'} + dev: true + /shebang-regex/3.0.0: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} @@ -11745,12 +12329,25 @@ packages: atomic-sleep: 1.0.0 dev: false + /sort-keys-length/1.0.1: + resolution: {integrity: sha512-GRbEOUqCxemTAk/b32F2xa8wDTs+Z1QHOkbhJDQTvv/6G3ZkbJ+frYWsTcc7cBB3Fu4wy4XlLCuNtJuMn7Gsvw==} + engines: {node: '>=0.10.0'} + dependencies: + sort-keys: 1.1.2 + dev: true + /sort-keys/1.1.2: resolution: {integrity: sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg==} engines: {node: '>=0.10.0'} dependencies: is-plain-obj: 1.1.0 - dev: false + + /sort-keys/2.0.0: + resolution: {integrity: sha512-/dPCrG1s3ePpWm6yBbxZq5Be1dXGLyLn9Z791chDC3NFrpkVbWGzkBwPN1knaciexFXgRJ7hzdnwZ4stHSDmjg==} + engines: {node: '>=4'} + dependencies: + is-plain-obj: 1.1.0 + dev: true /sortablejs/1.14.0: resolution: {integrity: sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w==} @@ -11799,6 +12396,11 @@ packages: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} + /source-map/0.7.4: + resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} + engines: {node: '>= 8'} + dev: true + /sourcemap-codec/1.4.8: resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} deprecated: Please use @jridgewell/sourcemap-codec instead @@ -11971,7 +12573,6 @@ packages: /strict-uri-encode/1.1.0: resolution: {integrity: sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ==} engines: {node: '>=0.10.0'} - dev: false /string-length/4.0.2: resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} @@ -12022,7 +12623,6 @@ packages: resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} dependencies: safe-buffer: 5.1.2 - dev: false /string_decoder/1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} @@ -12065,6 +12665,17 @@ packages: engines: {node: '>=8'} dev: true + /strip-dirs/2.1.0: + resolution: {integrity: sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g==} + dependencies: + is-natural-number: 4.0.1 + dev: true + + /strip-eof/1.0.0: + resolution: {integrity: sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==} + engines: {node: '>=0.10.0'} + dev: true + /strip-final-newline/2.0.0: resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} engines: {node: '>=6'} @@ -12084,6 +12695,13 @@ packages: engines: {node: '>=8'} dev: true + /strip-outer/1.0.1: + resolution: {integrity: sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==} + engines: {node: '>=0.10.0'} + dependencies: + escape-string-regexp: 1.0.5 + dev: true + /strnum/1.0.5: resolution: {integrity: sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==} dev: false @@ -12196,6 +12814,19 @@ packages: tar-stream: 2.2.0 dev: false + /tar-stream/1.6.2: + resolution: {integrity: sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==} + engines: {node: '>= 0.8.0'} + dependencies: + bl: 1.2.3 + buffer-alloc: 1.2.0 + end-of-stream: 1.4.4 + fs-constants: 1.0.0 + readable-stream: 2.3.7 + to-buffer: 1.1.1 + xtend: 4.0.2 + dev: true + /tar-stream/2.2.0: resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} engines: {node: '>=6'} @@ -12334,6 +12965,11 @@ packages: engines: {node: '>=0.10.0'} dev: false + /timed-out/4.0.1: + resolution: {integrity: sha512-G7r3AhovYtr5YKOWQkta8RKAPb+J9IsO4uVmzjl8AZwfhs8UcUwTiD6gcJYSgOtzyjvQKrKYn41syHbUWMkafA==} + engines: {node: '>=0.10.0'} + dev: true + /tiny-lru/10.0.1: resolution: {integrity: sha512-Vst+6kEsWvb17Zpz14sRJV/f8bUWKhqm6Dc+v08iShmIJ/WxqWytHzCTd6m88pS33rE2zpX34TRmOpAJPloNCA==} engines: {node: '>=6'} @@ -12361,6 +12997,10 @@ packages: is-negated-glob: 1.0.0 dev: false + /to-buffer/1.1.1: + resolution: {integrity: sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==} + dev: true + /to-fast-properties/2.0.0: resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} engines: {node: '>=4'} @@ -12455,6 +13095,13 @@ packages: resolution: {integrity: sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==} dev: false + /trim-repeated/1.0.0: + resolution: {integrity: sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg==} + engines: {node: '>=0.10.0'} + dependencies: + escape-string-regexp: 1.0.5 + dev: true + /tsc-alias/1.8.2: resolution: {integrity: sha512-ukBkcNekOgwtnSWYLD5QsMX3yQWg7JviAs8zg3qJGgu4LGtY3tsV4G6vnqvOXIDkbC+XL9vbhObWSpRA5/6wbg==} hasBin: true @@ -12673,6 +13320,13 @@ packages: which-boxed-primitive: 1.0.2 dev: true + /unbzip2-stream/1.4.3: + resolution: {integrity: sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==} + dependencies: + buffer: 5.7.1 + through: 2.3.8 + dev: true + /unc-path-regex/0.1.2: resolution: {integrity: sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg==} engines: {node: '>=0.10.0'} @@ -12813,6 +13467,13 @@ packages: deprecated: Please see https://github.com/lydell/urix#deprecated dev: false + /url-parse-lax/3.0.0: + resolution: {integrity: sha512-NjFKA0DidqPa5ciFcSrXnAltTtzz84ogy+NebPvfEgAck0+TNg4UJ4IN+fB7zRZfbgUf0syOo9MDxFkDSMuFaQ==} + engines: {node: '>=4'} + dependencies: + prepend-http: 2.0.0 + dev: true + /url-parse/1.5.10: resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} dependencies: @@ -12820,6 +13481,11 @@ packages: requires-port: 1.0.0 dev: false + /url-to-options/1.0.1: + resolution: {integrity: sha512-0kQLIzG4fdk/G5NONku64rSH/x32NOA39LVQqlK8Le6lvTF6GGRJpqaQFGgU+CLwySIqBSMdwYM0sYcW9f6P4A==} + engines: {node: '>= 4'} + dev: true + /url/0.10.3: resolution: {integrity: sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==} dependencies: @@ -13218,7 +13884,6 @@ packages: hasBin: true dependencies: isexe: 2.0.0 - dev: false /which/2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} @@ -13360,6 +14025,10 @@ packages: engines: {node: '>=0.10.32'} dev: false + /yallist/2.1.2: + resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==} + dev: true + /yallist/3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} From 65cd605b739ae0d213b3502308e9cd523d3e1ae7 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 21 Jan 2023 13:14:55 +0900 Subject: [PATCH 26/70] Achievements (#9665) * wip * Update ja-JP.yml * wip * wip * Update MkAchievements.vue * wip * :art: * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip --- locales/ja-JP.yml | 217 +++++++++ .../migration/1674118260469-achievement.js | 33 ++ .../migration/1674255666603-loggedInDates.js | 11 + .../backend/src/core/AchievementService.ts | 114 +++++ packages/backend/src/core/CoreModule.ts | 6 + .../entities/NotificationEntityService.ts | 3 + .../src/core/entities/UserEntityService.ts | 7 +- .../src/models/entities/Notification.ts | 6 + .../src/models/entities/UserProfile.ts | 13 + .../backend/src/server/api/EndpointsModule.ts | 8 + packages/backend/src/server/api/endpoints.ts | 4 + .../api/endpoints/drive/folders/update.ts | 4 +- .../backend/src/server/api/endpoints/i.ts | 27 +- .../api/endpoints/i/claim-achievement.ts | 28 ++ .../api/endpoints/users/achievements.ts | 31 ++ packages/backend/src/types.ts | 2 +- packages/frontend/src/account.ts | 7 +- .../src/components/MkAchievements.vue | 224 +++++++++ .../frontend/src/components/MkClickerGame.vue | 11 +- .../src/components/MkDrive.folder.vue | 7 +- packages/frontend/src/components/MkDrive.vue | 7 +- .../src/components/MkFollowButton.vue | 17 + packages/frontend/src/components/MkNote.vue | 4 + .../src/components/MkNoteDetailed.vue | 4 + .../src/components/MkNotification.vue | 13 + .../frontend/src/components/MkPostForm.vue | 31 +- .../components/MkReactionsViewer.reaction.vue | 4 + packages/frontend/src/init.ts | 77 ++++ packages/frontend/src/navbar.ts | 8 +- packages/frontend/src/pages/achievements.vue | 25 ++ .../frontend/src/pages/settings/profile.vue | 9 + packages/frontend/src/router.ts | 4 + packages/frontend/src/scripts/achievements.ts | 425 ++++++++++++++++++ .../frontend/src/scripts/get-note-menu.ts | 12 + 34 files changed, 1385 insertions(+), 18 deletions(-) create mode 100644 packages/backend/migration/1674118260469-achievement.js create mode 100644 packages/backend/migration/1674255666603-loggedInDates.js create mode 100644 packages/backend/src/core/AchievementService.ts create mode 100644 packages/backend/src/server/api/endpoints/i/claim-achievement.ts create mode 100644 packages/backend/src/server/api/endpoints/users/achievements.ts create mode 100644 packages/frontend/src/components/MkAchievements.vue create mode 100644 packages/frontend/src/pages/achievements.vue create mode 100644 packages/frontend/src/scripts/achievements.ts diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 5721fcc887..e46e6ab9de 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -938,6 +938,222 @@ cannotPerformTemporary: "一時的に利用できません" cannotPerformTemporaryDescription: "操作回数が制限を超過するため一時的に利用できません。しばらく時間を置いてから再度お試しください。" preset: "プリセット" selectFromPresets: "プリセットから選択" +achievements: "実績" + +_achievements: + earnedAt: "獲得日時" + _types: + _notes1: + title: "just setting up my msky" + description: "初めてノートを投稿した" + flavor: "良いMisskeyライフを!" + _notes10: + title: "いくつかのノート" + description: "ノートを10回投稿した" + _notes100: + title: "たくさんのノート" + description: "ノートを100回投稿した" + _notes500: + title: "ノートまみれ" + description: "ノートを500回投稿した" + _notes1000: + title: "ノートの山" + description: "ノートを1,000回投稿した" + _notes5000: + title: "湧き出るノート" + description: "ノートを5,000回投稿した" + _notes10000: + title: "スーパーノート" + description: "ノートを10,000回投稿した" + _notes20000: + title: "ニードモアノート" + description: "ノートを20,000回投稿した" + _notes30000: + title: "ノートノートノート" + description: "ノートを30,000回投稿した" + _notes40000: + title: "ノート工場" + description: "ノートを40,000回投稿した" + _notes50000: + title: "ノートの惑星" + description: "ノートを50,000回投稿した" + _notes60000: + title: "ノートクエーサー" + description: "ノートを60,000回投稿した" + _notes70000: + title: "ブラックノートホール" + description: "ノートを70,000回投稿した" + _notes80000: + title: "ノートギャラクシー" + description: "ノートを80,000回投稿した" + _notes90000: + title: "ノートバース" + description: "ノートを90,000回投稿した" + _notes100000: + title: "ALL YOUR NOTE ARE BELONG TO US" + description: "ノートを100,000回投稿した" + flavor: "そんなに書くことある?" + _login3: + title: "ビギナーⅠ" + description: "通算ログイン日数が3日" + flavor: "今日からね僕は ミスキストってことで" + _login7: + title: "ビギナーⅡ" + description: "通算ログイン日数が7日" + flavor: "慣れてきましたか?" + _login15: + title: "ビギナーⅢ" + description: "通算ログイン日数が15日" + _login30: + title: "ミスキストⅠ" + description: "通算ログイン日数が30日" + _login60: + title: "ミスキストⅡ" + description: "通算ログイン日数が60日" + _login100: + title: "ミスキストⅢ" + description: "通算ログイン日数が100日" + flavor: "そのユーザー、ミスキストにつき" + _login200: + title: "常連Ⅰ" + description: "通算ログイン日数が200日" + _login300: + title: "常連Ⅱ" + description: "通算ログイン日数が300日" + _login400: + title: "常連Ⅲ" + description: "通算ログイン日数が400日" + _login500: + title: "ベテランⅠ" + description: "通算ログイン日数が500日" + flavor: "諸君、私はノートが好きだ" + _login600: + title: "ベテランⅡ" + description: "通算ログイン日数が600日" + _login700: + title: "ベテランⅢ" + description: "通算ログイン日数が700日" + _login800: + title: "ノートマスターⅠ" + description: "通算ログイン日数が800日" + _login900: + title: "ノートマスターⅡ" + description: "通算ログイン日数が900日" + _login1000: + title: "ノートマスターⅢ" + description: "通算ログイン日数が1,000日" + flavor: "Misskeyを使ってくれてありがとう!" + _noteClipped1: + title: "クリップせずにはいられないな" + description: "初めてノートをクリップした" + _noteFavorited1: + title: "星をみるひと" + description: "初めてノートをお気に入りに登録した" + _profileFilled: + title: "準備万端" + description: "プロフィール設定を行った" + _markedAsCat: + title: "吾輩は猫である" + description: "アカウントをCatとして設定した" + flavor: "名前はまだない。" + _following1: + title: "はじめてのフォロー" + description: "初めてフォローした" + _following10: + title: "ついてく、ついてく" + description: "フォローが10人を超した" + _following50: + title: "友達たくさん" + description: "フォローが50人を超した" + _following100: + title: "友達100人" + description: "フォローが100人を超した" + _following300: + title: "友達過多" + description: "フォローが300人を超した" + _followers1: + title: "はじめてのフォロワー" + description: "初めてフォローされた" + _followers10: + title: "フォローミー!" + description: "フォロワーが10人を超した" + _followers50: + title: "ぞろぞろ" + description: "フォロワーが50人を超した" + _followers100: + title: "人気者" + description: "フォロワーが100人を超した" + _followers300: + title: "一列でお並びください" + description: "フォロワーが300人を超した" + _followers500: + title: "基地局" + description: "フォロワーが500人を超した" + _followers1000: + title: "インフルエンサー" + description: "フォロワーが1,000人を超した" + _collectAchievements30: + title: "実績コレクター" + description: "実績を30個以上獲得した" + _iLoveMisskey: + title: "I Love Misskey" + description: "\"I ❤ #Misskey\"を投稿した" + flavor: "Misskeyを使ってくださりありがとうございます! by 開発チーム" + _client30min: + title: "ひとやすみ" + description: "クライアントを起動してから30分以上経過した" + _noteDeletedWithin1min: + title: "いまのなし" + description: "投稿してから1分以内にその投稿を削除した" + _postedAtLateNight: + title: "夜行性" + description: "深夜にノートを投稿した" + flavor: "そろそろ寝よう。" + _postedAt0min0sec: + title: "時報" + description: "0分0秒にノートを投稿した" + flavor: "ポッ ポッ ポッ ピーン" + _selfQuote: + title: "自己言及" + description: "自分のノートを引用した" + _htl20npm: + title: "流れるTL" + description: "ホームタイムラインの流速が20npmを越す" + _driveFolderCircularReference: + title: "循環参照" + description: "ドライブのフォルダを再帰的な入れ子にしようとした" + _reactWithoutRead: + title: "ちゃんと読んだ?" + description: "100文字以上のテキストを含むノートに投稿されてから3秒以内にリアクションした" + _clickedClickHere: + title: "ここをクリック" + description: "ここをクリックした" + _justPlainLucky: + title: "単なるラッキー" + description: "10秒ごとに0.01%の確率で獲得" + _setNameToSyuilo: + title: "神様コンプレックス" + description: "名前を syuilo に設定した" + _passedSinceAccountCreated1: + title: "一周年" + description: "アカウント作成から1年経過した" + _passedSinceAccountCreated2: + title: "二周年" + description: "アカウント作成から2年経過した" + _passedSinceAccountCreated3: + title: "三周年" + description: "アカウント作成から3年経過した" + _loggedInOnBirthday: + title: "ハッピーバースデー" + description: "誕生日にログインした" + _cookieClicked: + title: "クッキーをクリックするゲーム" + description: "クッキーをクリックした" + flavor: "ソフト間違ってない?" + _brainDiver: + title: "Brain Diver" + description: "Brain Diverへのリンクを投稿した" + flavor: "Misskey-Misskey La-Tu-Ma" _role: new: "ロールの作成" @@ -1635,6 +1851,7 @@ _notification: pollEnded: "アンケートの結果が出ました" unreadAntennaNote: "アンテナ {name}" emptyPushNotificationMessage: "プッシュ通知の更新をしました" + achievementEarned: "実績を獲得" _types: all: "すべて" diff --git a/packages/backend/migration/1674118260469-achievement.js b/packages/backend/migration/1674118260469-achievement.js new file mode 100644 index 0000000000..131ab96f80 --- /dev/null +++ b/packages/backend/migration/1674118260469-achievement.js @@ -0,0 +1,33 @@ +export class achievement1674118260469 { + name = 'achievement1674118260469' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "notification" ADD "achievement" character varying(128)`); + await queryRunner.query(`ALTER TABLE "user_profile" ADD "achievements" jsonb NOT NULL DEFAULT '[]'`); + await queryRunner.query(`ALTER TYPE "public"."notification_type_enum" RENAME TO "notification_type_enum_old"`); + await queryRunner.query(`CREATE TYPE "public"."notification_type_enum" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'achievementEarned', 'app')`); + await queryRunner.query(`ALTER TABLE "notification" ALTER COLUMN "type" TYPE "public"."notification_type_enum" USING "type"::"text"::"public"."notification_type_enum"`); + await queryRunner.query(`DROP TYPE "public"."notification_type_enum_old"`); + await queryRunner.query(`ALTER TYPE "public"."user_profile_mutingnotificationtypes_enum" RENAME TO "user_profile_mutingnotificationtypes_enum_old"`); + await queryRunner.query(`CREATE TYPE "public"."user_profile_mutingnotificationtypes_enum" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'achievementEarned', 'app')`); + await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "mutingNotificationTypes" DROP DEFAULT`); + await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "mutingNotificationTypes" TYPE "public"."user_profile_mutingnotificationtypes_enum"[] USING "mutingNotificationTypes"::"text"::"public"."user_profile_mutingnotificationtypes_enum"[]`); + await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "mutingNotificationTypes" SET DEFAULT '{}'`); + await queryRunner.query(`DROP TYPE "public"."user_profile_mutingnotificationtypes_enum_old"`); + } + + async down(queryRunner) { + await queryRunner.query(`CREATE TYPE "public"."user_profile_mutingnotificationtypes_enum_old" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app', 'pollEnded')`); + await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "mutingNotificationTypes" DROP DEFAULT`); + await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "mutingNotificationTypes" TYPE "public"."user_profile_mutingnotificationtypes_enum_old"[] USING "mutingNotificationTypes"::"text"::"public"."user_profile_mutingnotificationtypes_enum_old"[]`); + await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "mutingNotificationTypes" SET DEFAULT '{}'`); + await queryRunner.query(`DROP TYPE "public"."user_profile_mutingnotificationtypes_enum"`); + await queryRunner.query(`ALTER TYPE "public"."user_profile_mutingnotificationtypes_enum_old" RENAME TO "user_profile_mutingnotificationtypes_enum"`); + await queryRunner.query(`CREATE TYPE "public"."notification_type_enum_old" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app')`); + await queryRunner.query(`ALTER TABLE "notification" ALTER COLUMN "type" TYPE "public"."notification_type_enum_old" USING "type"::"text"::"public"."notification_type_enum_old"`); + await queryRunner.query(`DROP TYPE "public"."notification_type_enum"`); + await queryRunner.query(`ALTER TYPE "public"."notification_type_enum_old" RENAME TO "notification_type_enum"`); + await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "achievements"`); + await queryRunner.query(`ALTER TABLE "notification" DROP COLUMN "achievement"`); + } +} diff --git a/packages/backend/migration/1674255666603-loggedInDates.js b/packages/backend/migration/1674255666603-loggedInDates.js new file mode 100644 index 0000000000..6d75ab6436 --- /dev/null +++ b/packages/backend/migration/1674255666603-loggedInDates.js @@ -0,0 +1,11 @@ +export class loggedInDates1674255666603 { + name = 'loggedInDates1674255666603' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "user_profile" ADD "loggedInDates" character varying(32) array NOT NULL DEFAULT '{}'`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "loggedInDates"`); + } +} diff --git a/packages/backend/src/core/AchievementService.ts b/packages/backend/src/core/AchievementService.ts new file mode 100644 index 0000000000..4ed75308eb --- /dev/null +++ b/packages/backend/src/core/AchievementService.ts @@ -0,0 +1,114 @@ +import { Inject, Injectable } from '@nestjs/common'; +import type { UserProfilesRepository, UsersRepository } from '@/models/index.js'; +import type { User } from '@/models/entities/User.js'; +import { DI } from '@/di-symbols.js'; +import { bindThis } from '@/decorators.js'; +import { CreateNotificationService } from '@/core/CreateNotificationService.js'; + +const ACHIEVEMENT_TYPES = [ + 'notes1', + 'notes10', + 'notes100', + 'notes500', + 'notes1000', + 'notes5000', + 'notes10000', + 'notes20000', + 'notes30000', + 'notes40000', + 'notes50000', + 'notes60000', + 'notes70000', + 'notes80000', + 'notes90000', + 'notes100000', + 'login3', + 'login7', + 'login15', + 'login30', + 'login60', + 'login100', + 'login200', + 'login300', + 'login400', + 'login500', + 'login600', + 'login700', + 'login800', + 'login900', + 'login1000', + 'passedSinceAccountCreated1', + 'passedSinceAccountCreated2', + 'passedSinceAccountCreated3', + 'loggedInOnBirthday', + 'noteClipped1', + 'noteFavorited1', + 'profileFilled', + 'markedAsCat', + 'following1', + 'following10', + 'following50', + 'following100', + 'following300', + 'followers1', + 'followers10', + 'followers50', + 'followers100', + 'followers300', + 'followers500', + 'followers1000', + 'collectAchievements30', + 'iLoveMisskey', + 'client30min', + 'noteDeletedWithin1min', + 'postedAtLateNight', + 'postedAt0min0sec', + 'selfQuote', + 'htl20npm', + 'driveFolderCircularReference', + 'reactWithoutRead', + 'clickedClickHere', + 'justPlainLucky', + 'setNameToSyuilo', + 'cookieClicked', + 'brainDiver', +] as const; + +@Injectable() +export class AchievementService { + constructor( + @Inject(DI.usersRepository) + private usersRepository: UsersRepository, + + @Inject(DI.userProfilesRepository) + private userProfilesRepository: UserProfilesRepository, + + private createNotificationService: CreateNotificationService, + ) { + } + + @bindThis + public async create( + userId: User['id'], + type: string, + ): Promise<void> { + if (!ACHIEVEMENT_TYPES.includes(type)) return; + + const date = Date.now(); + + const profile = await this.userProfilesRepository.findOneByOrFail({ userId: userId }); + + if (profile.achievements.some(a => a.name === type)) return; + + await this.userProfilesRepository.update(userId, { + achievements: [...profile.achievements, { + name: type, + unlockedAt: date, + }], + }); + + this.createNotificationService.createNotification(userId, 'achievementEarned', { + achievement: type, + }); + } +} diff --git a/packages/backend/src/core/CoreModule.ts b/packages/backend/src/core/CoreModule.ts index 0ae1ee32b2..eddf407940 100644 --- a/packages/backend/src/core/CoreModule.ts +++ b/packages/backend/src/core/CoreModule.ts @@ -4,6 +4,7 @@ import { AccountUpdateService } from './AccountUpdateService.js'; import { AiService } from './AiService.js'; import { AntennaService } from './AntennaService.js'; import { AppLockService } from './AppLockService.js'; +import { AchievementService } from './AchievementService.js'; import { CaptchaService } from './CaptchaService.js'; import { CreateNotificationService } from './CreateNotificationService.js'; import { CreateSystemUserService } from './CreateSystemUserService.js'; @@ -128,6 +129,7 @@ const $AccountUpdateService: Provider = { provide: 'AccountUpdateService', useEx const $AiService: Provider = { provide: 'AiService', useExisting: AiService }; const $AntennaService: Provider = { provide: 'AntennaService', useExisting: AntennaService }; const $AppLockService: Provider = { provide: 'AppLockService', useExisting: AppLockService }; +const $AchievementService: Provider = { provide: 'AchievementService', useExisting: AchievementService }; const $CaptchaService: Provider = { provide: 'CaptchaService', useExisting: CaptchaService }; const $CreateNotificationService: Provider = { provide: 'CreateNotificationService', useExisting: CreateNotificationService }; const $CreateSystemUserService: Provider = { provide: 'CreateSystemUserService', useExisting: CreateSystemUserService }; @@ -255,6 +257,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting AiService, AntennaService, AppLockService, + AchievementService, CaptchaService, CreateNotificationService, CreateSystemUserService, @@ -376,6 +379,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $AiService, $AntennaService, $AppLockService, + $AchievementService, $CaptchaService, $CreateNotificationService, $CreateSystemUserService, @@ -498,6 +502,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting AiService, AntennaService, AppLockService, + AchievementService, CaptchaService, CreateNotificationService, CreateSystemUserService, @@ -618,6 +623,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $AiService, $AntennaService, $AppLockService, + $AchievementService, $CaptchaService, $CreateNotificationService, $CreateSystemUserService, diff --git a/packages/backend/src/core/entities/NotificationEntityService.ts b/packages/backend/src/core/entities/NotificationEntityService.ts index a1c2c9cffb..a8210eea02 100644 --- a/packages/backend/src/core/entities/NotificationEntityService.ts +++ b/packages/backend/src/core/entities/NotificationEntityService.ts @@ -114,6 +114,9 @@ export class NotificationEntityService implements OnModuleInit { ...(notification.type === 'groupInvited' ? { invitation: this.userGroupInvitationEntityService.pack(notification.userGroupInvitationId!), } : {}), + ...(notification.type === 'achievementEarned' ? { + achievement: notification.achievement, + } : {}), ...(notification.type === 'app' ? { body: notification.customBody, header: notification.customHeader ?? token?.name, diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts index bf6f6f4553..34b523e143 100644 --- a/packages/backend/src/core/entities/UserEntityService.ts +++ b/packages/backend/src/core/entities/UserEntityService.ts @@ -12,7 +12,7 @@ import { Cache } from '@/misc/cache.js'; import type { Instance } from '@/models/entities/Instance.js'; import type { ILocalUser, IRemoteUser, User } from '@/models/entities/User.js'; import { birthdaySchema, descriptionSchema, localUsernameSchema, locationSchema, nameSchema, passwordSchema } from '@/models/entities/User.js'; -import type { UsersRepository, UserSecurityKeysRepository, FollowingsRepository, FollowRequestsRepository, BlockingsRepository, MutingsRepository, DriveFilesRepository, NoteUnreadsRepository, ChannelFollowingsRepository, NotificationsRepository, UserNotePiningsRepository, UserProfilesRepository, InstancesRepository, AnnouncementReadsRepository, MessagingMessagesRepository, UserGroupJoiningsRepository, AnnouncementsRepository, AntennaNotesRepository, PagesRepository } from '@/models/index.js'; +import type { UsersRepository, UserSecurityKeysRepository, FollowingsRepository, FollowRequestsRepository, BlockingsRepository, MutingsRepository, DriveFilesRepository, NoteUnreadsRepository, ChannelFollowingsRepository, NotificationsRepository, UserNotePiningsRepository, UserProfilesRepository, InstancesRepository, AnnouncementReadsRepository, MessagingMessagesRepository, UserGroupJoiningsRepository, AnnouncementsRepository, AntennaNotesRepository, PagesRepository, UserProfile } from '@/models/index.js'; import { bindThis } from '@/decorators.js'; import { RoleService } from '@/core/RoleService.js'; import type { OnModuleInit } from '@nestjs/common'; @@ -343,6 +343,7 @@ export class UserEntityService implements OnModuleInit { options?: { detail?: D, includeSecrets?: boolean, + userProfile?: UserProfile, }, ): Promise<IsMeAndIsUserDetailed<ExpectsMe, D>> { const opts = Object.assign({ @@ -375,7 +376,7 @@ export class UserEntityService implements OnModuleInit { .innerJoinAndSelect('pin.note', 'note') .orderBy('pin.id', 'DESC') .getMany() : []; - const profile = opts.detail ? await this.userProfilesRepository.findOneByOrFail({ userId: user.id }) : null; + const profile = opts.detail ? (opts.userProfile ?? await this.userProfilesRepository.findOneByOrFail({ userId: user.id })) : null; const followingCount = profile == null ? null : (profile.ffVisibility === 'public') || isMe ? user.followingCount : @@ -493,6 +494,8 @@ export class UserEntityService implements OnModuleInit { mutingNotificationTypes: profile!.mutingNotificationTypes, emailNotificationTypes: profile!.emailNotificationTypes, showTimelineReplies: user.showTimelineReplies ?? falsy, + achievements: profile!.achievements, + loggedInDays: profile!.loggedInDates.length, } : {}), ...(opts.includeSecrets ? { diff --git a/packages/backend/src/models/entities/Notification.ts b/packages/backend/src/models/entities/Notification.ts index 6679cdb809..66f131d1c0 100644 --- a/packages/backend/src/models/entities/Notification.ts +++ b/packages/backend/src/models/entities/Notification.ts @@ -64,6 +64,7 @@ export class Notification { * receiveFollowRequest - フォローリクエストされた * followRequestAccepted - 自分の送ったフォローリクエストが承認された * groupInvited - グループに招待された + * achievementEarned - 実績を獲得 * app - アプリ通知 */ @Index() @@ -129,6 +130,11 @@ export class Notification { }) public choice: number | null; + @Column('varchar', { + length: 128, nullable: true, + }) + public achievement: string | null; + /** * アプリ通知のbody */ diff --git a/packages/backend/src/models/entities/UserProfile.ts b/packages/backend/src/models/entities/UserProfile.ts index c561da87ce..86df8d5d98 100644 --- a/packages/backend/src/models/entities/UserProfile.ts +++ b/packages/backend/src/models/entities/UserProfile.ts @@ -213,6 +213,19 @@ export class UserProfile { }) public mutingNotificationTypes: typeof notificationTypes[number][]; + @Column('varchar', { + length: 32, array: true, default: '{}', + }) + public loggedInDates: string[]; + + @Column('jsonb', { + default: [], + }) + public achievements: { + name: string; + unlockedAt: number; + }[]; + //#region Denormalized fields @Index() @Column('varchar', { diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index 14927da7d6..466651f379 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -175,6 +175,7 @@ import * as ep___i_2fa_removeKey from './endpoints/i/2fa/remove-key.js'; import * as ep___i_2fa_unregister from './endpoints/i/2fa/unregister.js'; import * as ep___i_apps from './endpoints/i/apps.js'; import * as ep___i_authorizedApps from './endpoints/i/authorized-apps.js'; +import * as ep___i_claimAchievement from './endpoints/i/claim-achievement.js'; import * as ep___i_changePassword from './endpoints/i/change-password.js'; import * as ep___i_deleteAccount from './endpoints/i/delete-account.js'; import * as ep___i_exportBlocking from './endpoints/i/export-blocking.js'; @@ -329,6 +330,7 @@ import * as ep___users_searchByUsernameAndHost from './endpoints/users/search-by import * as ep___users_search from './endpoints/users/search.js'; import * as ep___users_show from './endpoints/users/show.js'; import * as ep___users_stats from './endpoints/users/stats.js'; +import * as ep___users_achievements from './endpoints/users/achievements.js'; import * as ep___fetchRss from './endpoints/fetch-rss.js'; import * as ep___retention from './endpoints/retention.js'; import { GetterService } from './GetterService.js'; @@ -509,6 +511,7 @@ const $i_2fa_removeKey: Provider = { provide: 'ep:i/2fa/remove-key', useClass: e const $i_2fa_unregister: Provider = { provide: 'ep:i/2fa/unregister', useClass: ep___i_2fa_unregister.default }; const $i_apps: Provider = { provide: 'ep:i/apps', useClass: ep___i_apps.default }; const $i_authorizedApps: Provider = { provide: 'ep:i/authorized-apps', useClass: ep___i_authorizedApps.default }; +const $i_claimAchievement: Provider = { provide: 'ep:i/claim-achievement', useClass: ep___i_claimAchievement.default }; const $i_changePassword: Provider = { provide: 'ep:i/change-password', useClass: ep___i_changePassword.default }; const $i_deleteAccount: Provider = { provide: 'ep:i/delete-account', useClass: ep___i_deleteAccount.default }; const $i_exportBlocking: Provider = { provide: 'ep:i/export-blocking', useClass: ep___i_exportBlocking.default }; @@ -663,6 +666,7 @@ const $users_searchByUsernameAndHost: Provider = { provide: 'ep:users/search-by- const $users_search: Provider = { provide: 'ep:users/search', useClass: ep___users_search.default }; const $users_show: Provider = { provide: 'ep:users/show', useClass: ep___users_show.default }; const $users_stats: Provider = { provide: 'ep:users/stats', useClass: ep___users_stats.default }; +const $users_achievements: Provider = { provide: 'ep:users/achievements', useClass: ep___users_achievements.default }; const $fetchRss: Provider = { provide: 'ep:fetch-rss', useClass: ep___fetchRss.default }; const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention.default }; @@ -847,6 +851,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $i_2fa_unregister, $i_apps, $i_authorizedApps, + $i_claimAchievement, $i_changePassword, $i_deleteAccount, $i_exportBlocking, @@ -1001,6 +1006,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $users_search, $users_show, $users_stats, + $users_achievements, $fetchRss, $retention, ], @@ -1179,6 +1185,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $i_2fa_unregister, $i_apps, $i_authorizedApps, + $i_claimAchievement, $i_changePassword, $i_deleteAccount, $i_exportBlocking, @@ -1331,6 +1338,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $users_search, $users_show, $users_stats, + $users_achievements, $fetchRss, $retention, ], diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index 54c4206ea4..3678fe14e8 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -174,6 +174,7 @@ import * as ep___i_2fa_removeKey from './endpoints/i/2fa/remove-key.js'; import * as ep___i_2fa_unregister from './endpoints/i/2fa/unregister.js'; import * as ep___i_apps from './endpoints/i/apps.js'; import * as ep___i_authorizedApps from './endpoints/i/authorized-apps.js'; +import * as ep___i_claimAchievement from './endpoints/i/claim-achievement.js'; import * as ep___i_changePassword from './endpoints/i/change-password.js'; import * as ep___i_deleteAccount from './endpoints/i/delete-account.js'; import * as ep___i_exportBlocking from './endpoints/i/export-blocking.js'; @@ -328,6 +329,7 @@ import * as ep___users_searchByUsernameAndHost from './endpoints/users/search-by import * as ep___users_search from './endpoints/users/search.js'; import * as ep___users_show from './endpoints/users/show.js'; import * as ep___users_stats from './endpoints/users/stats.js'; +import * as ep___users_achievements from './endpoints/users/achievements.js'; import * as ep___fetchRss from './endpoints/fetch-rss.js'; import * as ep___retention from './endpoints/retention.js'; @@ -506,6 +508,7 @@ const eps = [ ['i/2fa/unregister', ep___i_2fa_unregister], ['i/apps', ep___i_apps], ['i/authorized-apps', ep___i_authorizedApps], + ['i/claim-achievement', ep___i_claimAchievement], ['i/change-password', ep___i_changePassword], ['i/delete-account', ep___i_deleteAccount], ['i/export-blocking', ep___i_exportBlocking], @@ -660,6 +663,7 @@ const eps = [ ['users/search', ep___users_search], ['users/show', ep___users_show], ['users/stats', ep___users_stats], + ['users/achievements', ep___users_achievements], ['fetch-rss', ep___fetchRss], ['retention', ep___retention], ]; diff --git a/packages/backend/src/server/api/endpoints/drive/folders/update.ts b/packages/backend/src/server/api/endpoints/drive/folders/update.ts index ee63d291b2..ff0a78b929 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders/update.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders/update.ts @@ -28,8 +28,8 @@ export const meta = { recursiveNesting: { message: 'It can not be structured like nesting folders recursively.', - code: 'NO_SUCH_PARENT_FOLDER', - id: 'ce104e3a-faaf-49d5-b459-10ff0cbbcaa1', + code: 'RECURSIVE_NESTING', + id: 'dbeb024837894013aed44279f9199740', }, }, diff --git a/packages/backend/src/server/api/endpoints/i.ts b/packages/backend/src/server/api/endpoints/i.ts index 3bcd6ff8fb..6beef5ab85 100644 --- a/packages/backend/src/server/api/endpoints/i.ts +++ b/packages/backend/src/server/api/endpoints/i.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import type { UsersRepository } from '@/models/index.js'; +import type { UserProfilesRepository, UsersRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -29,15 +29,36 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { @Inject(DI.usersRepository) private usersRepository: UsersRepository, + @Inject(DI.userProfilesRepository) + private userProfilesRepository: UserProfilesRepository, + private userEntityService: UserEntityService, ) { super(meta, paramDef, async (ps, user, token) => { const isSecure = token == null; - // ここで渡ってきている user はキャッシュされていて古い可能性もあるので id だけ渡す - return await this.userEntityService.pack<true, true>(user.id, user, { + const now = new Date(); + const today = `${now.getFullYear()}/${now.getMonth() + 1}/${now.getDate()}`; + + // 渡ってきている user はキャッシュされていて古い可能性があるので改めて取得 + const userProfile = await this.userProfilesRepository.findOneOrFail({ + where: { + userId: user.id, + }, + relations: ['user'], + }); + + if (!userProfile.loggedInDates.includes(today)) { + this.userProfilesRepository.update({ userId: user.id }, { + loggedInDates: [...userProfile.loggedInDates, today], + }); + userProfile.loggedInDates = [...userProfile.loggedInDates, today]; + } + + return await this.userEntityService.pack<true, true>(userProfile.user!, userProfile.user!, { detail: true, includeSecrets: isSecure, + userProfile, }); }); } diff --git a/packages/backend/src/server/api/endpoints/i/claim-achievement.ts b/packages/backend/src/server/api/endpoints/i/claim-achievement.ts new file mode 100644 index 0000000000..52ae5475b6 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/i/claim-achievement.ts @@ -0,0 +1,28 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DI } from '@/di-symbols.js'; +import { AchievementService } from '@/core/AchievementService.js'; + +export const meta = { + requireCredential: true, +} as const; + +export const paramDef = { + type: 'object', + properties: { + name: { type: 'string' }, + }, + required: ['name'], +} as const; + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { + constructor( + private achievementService: AchievementService, + ) { + super(meta, paramDef, async (ps, me) => { + await this.achievementService.create(me.id, ps.name); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/users/achievements.ts b/packages/backend/src/server/api/endpoints/users/achievements.ts new file mode 100644 index 0000000000..2a095d83ea --- /dev/null +++ b/packages/backend/src/server/api/endpoints/users/achievements.ts @@ -0,0 +1,31 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import type { UserProfilesRepository } from '@/models/index.js'; +import { DI } from '@/di-symbols.js'; + +export const meta = { + requireCredential: true, +} as const; + +export const paramDef = { + type: 'object', + properties: { + userId: { type: 'string', format: 'misskey:id' }, + }, + required: ['userId'], +} as const; + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { + constructor( + @Inject(DI.userProfilesRepository) + private userProfilesRepository: UserProfilesRepository, + ) { + super(meta, paramDef, async (ps, me) => { + const profile = await this.userProfilesRepository.findOneByOrFail({ userId: ps.userId }); + + return profile.achievements; + }); + } +} diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts index 573e2faf87..7e9e193362 100644 --- a/packages/backend/src/types.ts +++ b/packages/backend/src/types.ts @@ -1,4 +1,4 @@ -export const notificationTypes = ['follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app'] as const; +export const notificationTypes = ['follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'achievementEarned', 'app'] as const; export const noteVisibilities = ['public', 'home', 'followers', 'specified'] as const; diff --git a/packages/frontend/src/account.ts b/packages/frontend/src/account.ts index 93916ccf2f..31c125d3ae 100644 --- a/packages/frontend/src/account.ts +++ b/packages/frontend/src/account.ts @@ -2,11 +2,11 @@ import { defineAsyncComponent, reactive } from 'vue'; import * as misskey from 'misskey-js'; import { showSuspendedDialog } from './scripts/show-suspended-dialog'; import { i18n } from './i18n'; +import { miLocalStorage } from './local-storage'; import { del, get, set } from '@/scripts/idb-proxy'; import { apiUrl } from '@/config'; import { waiting, api, popup, popupMenu, success, alert } from '@/os'; import { unisonReload, reloadChannel } from '@/scripts/unison-reload'; -import { miLocalStorage } from './local-storage'; // TODO: 他のタブと永続化されたstateを同期 @@ -20,6 +20,11 @@ export const $i = accountData ? reactive(JSON.parse(accountData) as Account) : n export const iAmModerator = $i != null && ($i.isAdmin || $i.isModerator); export const iAmAdmin = $i != null && $i.isAdmin; +export let notesCount = $i == null ? 0 : $i.notesCount; +export function incNotesCount() { + notesCount++; +} + export async function signout() { waiting(); miLocalStorage.removeItem('account'); diff --git a/packages/frontend/src/components/MkAchievements.vue b/packages/frontend/src/components/MkAchievements.vue new file mode 100644 index 0000000000..64fea96354 --- /dev/null +++ b/packages/frontend/src/components/MkAchievements.vue @@ -0,0 +1,224 @@ +<template> +<div> + <div v-if="achievements" :class="$style.root"> + <div v-for="achievement in achievements" :key="achievement" :class="$style.achievement" class="_panel"> + <div :class="$style.icon"> + <div :class="[$style.iconFrame, $style['iconFrame_' + ACHIEVEMENT_BADGES[achievement.name].frame]]"> + <div :class="[$style.iconInner]" :style="{ background: ACHIEVEMENT_BADGES[achievement.name].bg }"> + <img :class="$style.iconImg" :src="ACHIEVEMENT_BADGES[achievement.name].img"> + </div> + </div> + </div> + <div :class="$style.body"> + <div :class="$style.header"> + <span :class="$style.title">{{ i18n.ts._achievements._types['_' + achievement.name].title }}</span> + <span :class="$style.time"> + <time v-tooltip="new Date(achievement.unlockedAt).toLocaleString()">{{ new Date(achievement.unlockedAt).getFullYear() }}/{{ new Date(achievement.unlockedAt).getMonth() + 1 }}/{{ new Date(achievement.unlockedAt).getDate() }}</time> + </span> + </div> + <div :class="$style.description">{{ i18n.ts._achievements._types['_' + achievement.name].description }}</div> + <div v-if="i18n.ts._achievements._types['_' + achievement.name].flavor" :class="$style.flavor">{{ i18n.ts._achievements._types['_' + achievement.name].flavor }}</div> + </div> + </div> + <template v-if="withLocked"> + <div v-for="achievement in lockedAchievements" :key="achievement" :class="[$style.achievement, $style.locked]" class="_panel" @click="achievement === 'clickedClickHere' ? clickHere() : () => {}"> + <div :class="$style.icon"> + </div> + <div :class="$style.body"> + <div :class="$style.header"> + <span :class="$style.title">???</span> + </div> + <div :class="$style.description">???</div> + </div> + </div> + </template> + </div> + <div v-else> + <MkLoading/> + </div> +</div> +</template> + +<script lang="ts" setup> +import * as misskey from 'misskey-js'; +import { onMounted } from 'vue'; +import * as os from '@/os'; +import { i18n } from '@/i18n'; +import { ACHIEVEMENT_TYPES, ACHIEVEMENT_BADGES, claimAchievement } from '@/scripts/achievements'; + +const props = withDefaults(defineProps<{ + user: misskey.entities.User; + withLocked: boolean; +}>(), { + withLocked: true, +}); + +let achievements = $ref(); +const lockedAchievements = $computed(() => ACHIEVEMENT_TYPES.filter(x => !(achievements ?? []).some(a => a.name === x))); + +function fetch() { + os.api('users/achievements', { userId: props.user.id }).then(res => { + achievements = []; + for (const t of ACHIEVEMENT_TYPES) { + const a = res.find(x => x.name === t); + if (a) achievements.push(a); + } + //achievements = res.sort((a, b) => b.unlockedAt - a.unlockedAt); + }); +} + +function clickHere() { + claimAchievement('clickedClickHere'); + fetch(); +} + +onMounted(() => { + fetch(); +}); +</script> + +<style lang="scss" module> +.root { + display: grid; + grid-template-columns: repeat(auto-fill, min(380px, 100%)); + grid-gap: 12px; + place-content: center; +} + +.achievement { + display: flex; + padding: 16px; + + &.locked { + opacity: 0.5; + } +} + +.icon { + flex-shrink: 0; + margin-right: 12px; +} + +@keyframes shine { + 0% { translate: -30px; } + 100% { translate: -130px; } +} + +.iconFrame { + width: 58px; + height: 58px; + padding: 6px; + border-radius: 100%; + box-sizing: border-box; + pointer-events: none; + user-select: none; + filter: drop-shadow(0px 2px 2px #00000044); + box-shadow: 0 1px 0px #ffffff88 inset; + overflow: clip; +} +.iconFrame_bronze { + background: linear-gradient(0deg, #703827, #d37566); + + > .iconInner { + background: linear-gradient(0deg, #d37566, #703827); + } +} +.iconFrame_silver { + background: linear-gradient(0deg, #7c7c7c, #e1e1e1); + + > .iconInner { + background: linear-gradient(0deg, #e1e1e1, #7c7c7c); + } +} +.iconFrame_gold { + background: linear-gradient(0deg, rgba(255,182,85,1) 0%, rgba(233,133,0,1) 49%, rgba(255,243,93,1) 51%, rgba(255,187,25,1) 100%); + + > .iconInner { + background: linear-gradient(0deg, #ffee20, #eb7018); + } + + &:before { + content: ""; + display: block; + position: absolute; + top: 30px; + width: 200px; + height: 8px; + rotate: -45deg; + translate: -30px; + background: #ffffff88; + animation: shine 2s infinite; + } +} +.iconFrame_platinum { + background: linear-gradient(0deg, rgba(154,154,154,1) 0%, rgba(226,226,226,1) 49%, rgba(255,255,255,1) 51%, rgba(195,195,195,1) 100%); + + > .iconInner { + background: linear-gradient(0deg, #e1e1e1, #7c7c7c); + } + + &:before { + content: ""; + display: block; + position: absolute; + top: 30px; + width: 200px; + height: 8px; + rotate: -45deg; + translate: -30px; + background: #ffffffee; + animation: shine 2s infinite; + } +} + +.iconInner { + position: relative; + width: 100%; + height: 100%; + border-radius: 100%; + box-shadow: 0 1px 0px #ffffff88 inset; +} + +.iconImg { + width: calc(100% - 12px); + height: calc(100% - 12px); + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + margin: auto; + filter: drop-shadow(0px 1px 2px #000000aa); +} + +.body { + flex: 1; + min-width: 0; +} + +.header { + margin-bottom: 8px; + display: flex; +} + +.title { + font-weight: bold; +} + +.time { + margin-left: auto; + font-size: 85%; + opacity: 0.7; +} + +.description { + font-size: 85%; +} + +.flavor { + opacity: 0.7; + transform: skewX(-15deg); + font-size: 85%; + margin-top: 8px; +} +</style> diff --git a/packages/frontend/src/components/MkClickerGame.vue b/packages/frontend/src/components/MkClickerGame.vue index 03736ac5e4..68e0f8185d 100644 --- a/packages/frontend/src/components/MkClickerGame.vue +++ b/packages/frontend/src/components/MkClickerGame.vue @@ -20,6 +20,7 @@ import * as os from '@/os'; import { useInterval } from '@/scripts/use-interval'; import * as game from '@/scripts/clicker-game'; import number from '@/filters/number'; +import { claimAchievement } from '@/scripts/achievements'; defineProps<{ }>(); @@ -30,14 +31,18 @@ let cps = $ref(0); let prevCookies = $ref(0); function onClick(ev: MouseEvent) { + const x = ev.clientX; + const y = ev.clientY; + os.popup(MkPlusOneEffect, { x, y }, {}, 'end'); + saveData.value!.cookies++; saveData.value!.totalCookies++; saveData.value!.totalHandmadeCookies++; saveData.value!.clicked++; - const x = ev.clientX; - const y = ev.clientY; - os.popup(MkPlusOneEffect, { x, y }, {}, 'end'); + if (cookies.value === 1) { + claimAchievement('cookieClicked'); + } } useInterval(() => { diff --git a/packages/frontend/src/components/MkDrive.folder.vue b/packages/frontend/src/components/MkDrive.folder.vue index 82653ca0b4..156013b9aa 100644 --- a/packages/frontend/src/components/MkDrive.folder.vue +++ b/packages/frontend/src/components/MkDrive.folder.vue @@ -33,6 +33,7 @@ import * as Misskey from 'misskey-js'; import * as os from '@/os'; import { i18n } from '@/i18n'; import { defaultStore } from '@/store'; +import { claimAchievement } from '@/scripts/achievements'; const props = withDefaults(defineProps<{ folder: Misskey.entities.DriveFolder; @@ -159,9 +160,11 @@ function onDrop(ev: DragEvent) { }).then(() => { // noop }).catch(err => { - switch (err) { - case 'detected-circular-definition': + switch (err.code) { + case 'RECURSIVE_NESTING': + claimAchievement('driveFolderCircularReference'); os.alert({ + type: 'error', title: i18n.ts.unableToProcess, text: i18n.ts.circularReferenceFolder, }); diff --git a/packages/frontend/src/components/MkDrive.vue b/packages/frontend/src/components/MkDrive.vue index 112a64f52d..af7175e5cd 100644 --- a/packages/frontend/src/components/MkDrive.vue +++ b/packages/frontend/src/components/MkDrive.vue @@ -99,6 +99,7 @@ import { stream } from '@/stream'; import { defaultStore } from '@/store'; import { i18n } from '@/i18n'; import { uploadFile, uploads } from '@/scripts/upload'; +import { claimAchievement } from '@/scripts/achievements'; const props = withDefaults(defineProps<{ initialFolder?: Misskey.entities.DriveFolder; @@ -268,9 +269,11 @@ function onDrop(ev: DragEvent): any { }).then(() => { // noop }).catch(err => { - switch (err) { - case 'detected-circular-definition': + switch (err.code) { + case 'RECURSIVE_NESTING': + claimAchievement('driveFolderCircularReference'); os.alert({ + type: 'error', title: i18n.ts.unableToProcess, text: i18n.ts.circularReferenceFolder, }); diff --git a/packages/frontend/src/components/MkFollowButton.vue b/packages/frontend/src/components/MkFollowButton.vue index ee256d9263..de8db54bfa 100644 --- a/packages/frontend/src/components/MkFollowButton.vue +++ b/packages/frontend/src/components/MkFollowButton.vue @@ -35,6 +35,8 @@ import * as Misskey from 'misskey-js'; import * as os from '@/os'; import { stream } from '@/stream'; import { i18n } from '@/i18n'; +import { claimAchievement } from '@/scripts/achievements'; +import { $i } from '@/account'; const props = withDefaults(defineProps<{ user: Misskey.entities.UserDetailed, @@ -90,6 +92,21 @@ async function onClick() { userId: props.user.id, }); hasPendingFollowRequestFromYou = true; + + claimAchievement('following1'); + + if ($i.followingCount >= 10) { + claimAchievement('following10'); + } + if ($i.followingCount >= 50) { + claimAchievement('following50'); + } + if ($i.followingCount >= 100) { + claimAchievement('following100'); + } + if ($i.followingCount >= 300) { + claimAchievement('following300'); + } } } } catch (err) { diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue index 9b2501a2ed..1f6a2883d7 100644 --- a/packages/frontend/src/components/MkNote.vue +++ b/packages/frontend/src/components/MkNote.vue @@ -143,6 +143,7 @@ import { getNoteMenu } from '@/scripts/get-note-menu'; import { useNoteCapture } from '@/scripts/use-note-capture'; import { deepClone } from '@/scripts/clone'; import { useTooltip } from '@/scripts/use-tooltip'; +import { claimAchievement } from '@/scripts/achievements'; const props = defineProps<{ note: misskey.entities.Note; @@ -268,6 +269,9 @@ function react(viaKeyboard = false): void { noteId: appearNote.id, reaction: reaction, }); + if (appearNote.text && appearNote.text.length > 100 && (Date.now() - new Date(appearNote.createdAt).getTime() < 1000 * 3)) { + claimAchievement('reactWithoutRead'); + } }, () => { focus(); }); diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue index 56061e0e6f..48ace56d9c 100644 --- a/packages/frontend/src/components/MkNoteDetailed.vue +++ b/packages/frontend/src/components/MkNoteDetailed.vue @@ -159,6 +159,7 @@ import { getNoteMenu } from '@/scripts/get-note-menu'; import { useNoteCapture } from '@/scripts/use-note-capture'; import { deepClone } from '@/scripts/clone'; import { useTooltip } from '@/scripts/use-tooltip'; +import { claimAchievement } from '@/scripts/achievements'; const props = defineProps<{ note: misskey.entities.Note; @@ -279,6 +280,9 @@ function react(viaKeyboard = false): void { noteId: appearNote.id, reaction: reaction, }); + if (appearNote.text && appearNote.text.length > 100 && (Date.now() - new Date(appearNote.createdAt).getTime() < 1000 * 3)) { + claimAchievement('reactWithoutRead'); + } }, () => { focus(); }); diff --git a/packages/frontend/src/components/MkNotification.vue b/packages/frontend/src/components/MkNotification.vue index 5b8041c1d4..e992495a78 100644 --- a/packages/frontend/src/components/MkNotification.vue +++ b/packages/frontend/src/components/MkNotification.vue @@ -2,6 +2,7 @@ <div ref="elRef" :class="$style.root"> <div v-once :class="$style.head"> <MkAvatar v-if="notification.type === 'pollEnded'" :class="$style.icon" :user="notification.note.user" link preview/> + <MkAvatar v-else-if="notification.type === 'achievementEarned'" :class="$style.icon" :user="$i" link preview/> <MkAvatar v-else-if="notification.user" :class="$style.icon" :user="notification.user" link preview/> <img v-else-if="notification.icon" :class="$style.icon" :src="notification.icon" alt=""/> <div :class="[$style.subIcon, $style['t_' + notification.type]]"> @@ -14,6 +15,7 @@ <i v-else-if="notification.type === 'mention'" class="ti ti-at"></i> <i v-else-if="notification.type === 'quote'" class="ti ti-quote"></i> <i v-else-if="notification.type === 'pollEnded'" class="ti ti-chart-arrows"></i> + <i v-else-if="notification.type === 'achievementEarned'" class="ti ti-military-award"></i> <!-- notification.reaction が null になることはまずないが、ここでoptional chaining使うと一部ブラウザで刺さるので念の為 --> <MkReactionIcon v-else-if="notification.type === 'reaction'" @@ -28,6 +30,7 @@ <div :class="$style.tail"> <header :class="$style.header"> <span v-if="notification.type === 'pollEnded'">{{ i18n.ts._notification.pollEnded }}</span> + <span v-else-if="notification.type === 'achievementEarned'">{{ i18n.ts._notification.achievementEarned }}</span> <MkA v-else-if="notification.user" v-user-preview="notification.user.id" :class="$style.headerName" :to="userPage(notification.user)"><MkUserName :user="notification.user"/></MkA> <span v-else>{{ notification.header }}</span> <MkTime v-if="withTime" :time="notification.createdAt" :class="$style.headerTime"/> @@ -57,6 +60,9 @@ <Mfm :text="getNoteSummary(notification.note)" :plain="true" :nowrap="!full" :author="notification.note.user"/> <i class="ti ti-quote" :class="$style.quote"></i> </MkA> + <MkA v-else-if="notification.type === 'achievementEarned'" :class="$style.text" to="/my/achievements"> + {{ i18n.ts._achievements._types['_' + notification.achievement].title }} + </MkA> <span v-else-if="notification.type === 'follow'" :class="$style.text" style="opacity: 0.6;">{{ i18n.ts.youGotNewFollower }}<div v-if="full"><MkFollowButton :user="notification.user" :full="true"/></div></span> <span v-else-if="notification.type === 'followRequestAccepted'" :class="$style.text" style="opacity: 0.6;">{{ i18n.ts.followRequestAccepted }}</span> <span v-else-if="notification.type === 'receiveFollowRequest'" :class="$style.text" style="opacity: 0.6;">{{ i18n.ts.receiveFollowRequest }}<div v-if="full && !followRequestDone"><button class="_textButton" @click="acceptFollowRequest()">{{ i18n.ts.accept }}</button> | <button class="_textButton" @click="rejectFollowRequest()">{{ i18n.ts.reject }}</button></div></span> @@ -82,6 +88,7 @@ import { i18n } from '@/i18n'; import * as os from '@/os'; import { stream } from '@/stream'; import { useTooltip } from '@/scripts/use-tooltip'; +import { $i } from '@/account'; const props = withDefaults(defineProps<{ notification: misskey.entities.Notification; @@ -240,6 +247,12 @@ useTooltip(reactionRef, (showing) => { pointer-events: none; } +.t_achievementEarned { + padding: 3px; + background: #88a6b7; + pointer-events: none; +} + .tail { flex: 1; min-width: 0; diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index 6822caf4f4..c7e7e85b2e 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -93,11 +93,12 @@ import { defaultStore, notePostInterruptors, postFormActions } from '@/store'; import MkInfo from '@/components/MkInfo.vue'; import { i18n } from '@/i18n'; import { instance } from '@/instance'; -import { $i, getAccounts, openAccountMenu as openAccountMenu_ } from '@/account'; +import { $i, notesCount, incNotesCount, getAccounts, openAccountMenu as openAccountMenu_ } from '@/account'; import { uploadFile } from '@/scripts/upload'; import { deepClone } from '@/scripts/clone'; import MkRippleEffect from '@/components/MkRippleEffect.vue'; import { miLocalStorage } from '@/local-storage'; +import { claimAchievement } from '@/scripts/achievements'; const modal = inject('modal'); @@ -627,6 +628,34 @@ async function post(ev?: MouseEvent) { } posting = false; postAccount = null; + + incNotesCount(); + if (notesCount === 1) { + claimAchievement('notes1'); + } + + const text = postData.text?.toLowerCase() ?? ''; + if ((text.includes('love') || text.includes('❤')) && text.includes('misskey')) { + claimAchievement('iLoveMisskey'); + } + if (text.includes('Efrlqw8ytg4'.toLowerCase()) || text.includes('XVCwzwxdHuA'.toLowerCase())) { + claimAchievement('brainDiver'); + } + + if (props.renote && (props.renote.userId === $i.id) && text.length > 0) { + claimAchievement('selfQuote'); + } + + const date = new Date(); + const h = date.getHours(); + const m = date.getMinutes(); + const s = date.getSeconds(); + if (h >= 0 && h <= 3) { + claimAchievement('postedAtLateNight'); + } + if (m === 0 && s === 0) { + claimAchievement('postedAt0min0sec'); + } }); }).catch(err => { posting = false; diff --git a/packages/frontend/src/components/MkReactionsViewer.reaction.vue b/packages/frontend/src/components/MkReactionsViewer.reaction.vue index e90dd7ea69..ec4042d18c 100644 --- a/packages/frontend/src/components/MkReactionsViewer.reaction.vue +++ b/packages/frontend/src/components/MkReactionsViewer.reaction.vue @@ -20,6 +20,7 @@ import * as os from '@/os'; import { useTooltip } from '@/scripts/use-tooltip'; import { $i } from '@/account'; import MkReactionEffect from '@/components/MkReactionEffect.vue'; +import { claimAchievement } from '@/scripts/achievements'; const props = defineProps<{ reaction: string; @@ -52,6 +53,9 @@ const toggleReaction = () => { noteId: props.note.id, reaction: props.reaction, }); + if (props.note.text && props.note.text.length > 100 && (Date.now() - new Date(props.note.createdAt).getTime() < 1000 * 3)) { + claimAchievement('reactWithoutRead'); + } } }; diff --git a/packages/frontend/src/init.ts b/packages/frontend/src/init.ts index e10315e1ad..a2723d479c 100644 --- a/packages/frontend/src/init.ts +++ b/packages/frontend/src/init.ts @@ -44,6 +44,7 @@ import { reactionPicker } from '@/scripts/reaction-picker'; import { getUrlWithoutLoginId } from '@/scripts/login-id'; import { getAccountFromId } from '@/scripts/get-account-from-id'; import { miLocalStorage } from './local-storage'; +import { claimAchievement, claimedAchievements } from './scripts/achievements'; (async () => { console.info(`Misskey v${version}`); @@ -345,6 +346,82 @@ import { miLocalStorage } from './local-storage'; }); } + if ($i.birthday) { + const now = new Date(); + const m = now.getMonth() + 1; + const d = now.getDate(); + const bm = parseInt($i.birthday.split('-')[1]); + const bd = parseInt($i.birthday.split('-')[2]); + if (m === bm && d === bd) { + claimAchievement('loggedInOnBirthday'); + } + } + + if ($i.loggedInDays >= 3) claimAchievement('login3'); + if ($i.loggedInDays >= 7) claimAchievement('login7'); + if ($i.loggedInDays >= 15) claimAchievement('login15'); + if ($i.loggedInDays >= 30) claimAchievement('login30'); + if ($i.loggedInDays >= 60) claimAchievement('login60'); + if ($i.loggedInDays >= 100) claimAchievement('login100'); + if ($i.loggedInDays >= 200) claimAchievement('login200'); + if ($i.loggedInDays >= 300) claimAchievement('login300'); + if ($i.loggedInDays >= 400) claimAchievement('login400'); + if ($i.loggedInDays >= 500) claimAchievement('login500'); + if ($i.loggedInDays >= 600) claimAchievement('login600'); + if ($i.loggedInDays >= 700) claimAchievement('login700'); + if ($i.loggedInDays >= 800) claimAchievement('login800'); + if ($i.loggedInDays >= 900) claimAchievement('login900'); + if ($i.loggedInDays >= 1000) claimAchievement('login1000'); + + if ($i.notesCount > 0) claimAchievement('notes1'); + if ($i.notesCount >= 10) claimAchievement('notes10'); + if ($i.notesCount >= 100) claimAchievement('notes100'); + if ($i.notesCount >= 500) claimAchievement('notes500'); + if ($i.notesCount >= 1000) claimAchievement('notes1000'); + if ($i.notesCount >= 5000) claimAchievement('notes5000'); + if ($i.notesCount >= 10000) claimAchievement('notes10000'); + if ($i.notesCount >= 20000) claimAchievement('notes20000'); + if ($i.notesCount >= 30000) claimAchievement('notes30000'); + if ($i.notesCount >= 40000) claimAchievement('notes40000'); + if ($i.notesCount >= 50000) claimAchievement('notes50000'); + if ($i.notesCount >= 60000) claimAchievement('notes60000'); + if ($i.notesCount >= 70000) claimAchievement('notes70000'); + if ($i.notesCount >= 80000) claimAchievement('notes80000'); + if ($i.notesCount >= 90000) claimAchievement('notes90000'); + if ($i.notesCount >= 100000) claimAchievement('notes100000'); + + if ($i.followersCount > 0) claimAchievement('followers1'); + if ($i.followersCount >= 10) claimAchievement('followers10'); + if ($i.followersCount >= 50) claimAchievement('followers50'); + if ($i.followersCount >= 100) claimAchievement('followers100'); + if ($i.followersCount >= 300) claimAchievement('followers300'); + if ($i.followersCount >= 500) claimAchievement('followers500'); + if ($i.followersCount >= 1000) claimAchievement('followers1000'); + + if (Date.now() - new Date($i.createdAt).getTime() > 1000 * 60 * 60 * 24 * 365) { + claimAchievement('passedSinceAccountCreated1'); + } + if (Date.now() - new Date($i.createdAt).getTime() > 1000 * 60 * 60 * 24 * 365 * 2) { + claimAchievement('passedSinceAccountCreated2'); + } + if (Date.now() - new Date($i.createdAt).getTime() > 1000 * 60 * 60 * 24 * 365 * 3) { + claimAchievement('passedSinceAccountCreated3'); + } + + if (claimedAchievements.length >= 30) { + claimAchievement('collectAchievements30'); + } + + window.setInterval(() => { + if (Math.floor(Math.random() * 10000) === 0) { + claimAchievement('justPlainLucky'); + } + }, 1000 * 10); + + window.setTimeout(() => { + claimAchievement('client30min'); + }, 1000 * 60 * 30); + const lastUsed = miLocalStorage.getItem('lastUsed'); if (lastUsed) { const lastUsedDate = parseInt(lastUsed, 10); diff --git a/packages/frontend/src/navbar.ts b/packages/frontend/src/navbar.ts index 9ee78741dc..3d16a52e62 100644 --- a/packages/frontend/src/navbar.ts +++ b/packages/frontend/src/navbar.ts @@ -1,11 +1,11 @@ import { computed, ref, reactive } from 'vue'; import { $i } from './account'; +import { miLocalStorage } from './local-storage'; import { search } from '@/scripts/search'; import * as os from '@/os'; import { i18n } from '@/i18n'; import { ui } from '@/config'; import { unisonReload } from '@/scripts/unison-reload'; -import { miLocalStorage } from './local-storage'; export const navbarItemDef = reactive({ notifications: { @@ -103,6 +103,12 @@ export const navbarItemDef = reactive({ icon: 'ti ti-device-tv', to: '/channels', }, + achievements: { + title: i18n.ts.achievements, + icon: 'ti ti-military-award', + show: computed(() => $i != null), + to: '/my/achievements', + }, ui: { title: i18n.ts.switchUi, icon: 'ti ti-devices', diff --git a/packages/frontend/src/pages/achievements.vue b/packages/frontend/src/pages/achievements.vue new file mode 100644 index 0000000000..b6cd174b41 --- /dev/null +++ b/packages/frontend/src/pages/achievements.vue @@ -0,0 +1,25 @@ +<template> +<MkStickyContainer> + <template #header><MkPageHeader/></template> + <MkSpacer :content-max="1200"> + <MkAchievements :user="$i"/> + </MkSpacer> +</MkStickyContainer> +</template> + +<script lang="ts" setup> +import { ref } from 'vue'; +import MkAchievements from '@/components/MkAchievements.vue'; +import { i18n } from '@/i18n'; +import { definePageMetadata } from '@/scripts/page-metadata'; +import { $i } from '@/account'; + +definePageMetadata({ + title: i18n.ts.achievements, + icon: 'ti ti-military-award', +}); +</script> + +<style lang="scss" module> + +</style> diff --git a/packages/frontend/src/pages/settings/profile.vue b/packages/frontend/src/pages/settings/profile.vue index ae74224db6..da7d3d3703 100644 --- a/packages/frontend/src/pages/settings/profile.vue +++ b/packages/frontend/src/pages/settings/profile.vue @@ -85,6 +85,7 @@ import { i18n } from '@/i18n'; import { $i } from '@/account'; import { langmap } from '@/scripts/langmap'; import { definePageMetadata } from '@/scripts/page-metadata'; +import { claimAchievement } from '@/scripts/achievements'; const profile = reactive({ name: $i.name, @@ -133,6 +134,13 @@ function save() { isCat: !!profile.isCat, showTimelineReplies: !!profile.showTimelineReplies, }); + claimAchievement('profileFilled'); + if (profile.name === 'syuilo' || profile.name === 'しゅいろ') { + claimAchievement('setNameToSyuilo'); + } + if (profile.isCat) { + claimAchievement('markedAsCat'); + } } function changeAvatar(ev) { @@ -155,6 +163,7 @@ function changeAvatar(ev) { }); $i.avatarId = i.avatarId; $i.avatarUrl = i.avatarUrl; + claimAchievement('profileFilled'); }); } diff --git a/packages/frontend/src/router.ts b/packages/frontend/src/router.ts index 26c73c610f..22106e1595 100644 --- a/packages/frontend/src/router.ts +++ b/packages/frontend/src/router.ts @@ -427,6 +427,10 @@ export const routes = [{ path: '/my/favorites', component: page(() => import('./pages/favorites.vue')), loginRequired: true, +}, { + path: '/my/achievements', + component: page(() => import('./pages/achievements.vue')), + loginRequired: true, }, { name: 'messaging', path: '/my/messaging', diff --git a/packages/frontend/src/scripts/achievements.ts b/packages/frontend/src/scripts/achievements.ts new file mode 100644 index 0000000000..c8245ad3db --- /dev/null +++ b/packages/frontend/src/scripts/achievements.ts @@ -0,0 +1,425 @@ +import * as os from '@/os'; +import { $i } from '@/account'; + +export const ACHIEVEMENT_TYPES = [ + 'notes1', + 'notes10', + 'notes100', + 'notes500', + 'notes1000', + 'notes5000', + 'notes10000', + 'notes20000', + 'notes30000', + 'notes40000', + 'notes50000', + 'notes60000', + 'notes70000', + 'notes80000', + 'notes90000', + 'notes100000', + 'login3', + 'login7', + 'login15', + 'login30', + 'login60', + 'login100', + 'login200', + 'login300', + 'login400', + 'login500', + 'login600', + 'login700', + 'login800', + 'login900', + 'login1000', + 'passedSinceAccountCreated1', + 'passedSinceAccountCreated2', + 'passedSinceAccountCreated3', + 'loggedInOnBirthday', + 'noteClipped1', + 'noteFavorited1', + 'profileFilled', + 'markedAsCat', + 'following1', + 'following10', + 'following50', + 'following100', + 'following300', + 'followers1', + 'followers10', + 'followers50', + 'followers100', + 'followers300', + 'followers500', + 'followers1000', + 'collectAchievements30', + 'iLoveMisskey', + 'client30min', + 'noteDeletedWithin1min', + 'postedAtLateNight', + 'postedAt0min0sec', + 'selfQuote', + 'htl20npm', + 'driveFolderCircularReference', + 'reactWithoutRead', + 'clickedClickHere', + 'justPlainLucky', + 'setNameToSyuilo', + 'cookieClicked', + 'brainDiver', +] as const; + +export const ACHIEVEMENT_BADGES = { + 'notes1': { + img: '/fluent-emoji/1f4dd.png', + bg: 'linear-gradient(0deg, rgb(59 187 116), rgb(199 211 102))', + frame: 'bronze', + }, + 'notes10': { + img: '/fluent-emoji/1f4d1.png', + bg: null, + frame: 'bronze', + }, + 'notes100': { + img: '/fluent-emoji/1f4d2.png', + bg: 'linear-gradient(0deg, rgb(59 187 116), rgb(199 211 102))', + frame: 'bronze', + }, + 'notes500': { + img: '/fluent-emoji/1f4da.png', + bg: null, + frame: 'bronze', + }, + 'notes1000': { + img: '/fluent-emoji/1f5c3.png', + bg: 'linear-gradient(0deg, rgb(59 187 116), rgb(199 211 102))', + frame: 'bronze', + }, + 'notes5000': { + img: '/fluent-emoji/1f304.png', + bg: 'linear-gradient(0deg, rgb(59 187 116), rgb(199 211 102))', + frame: 'bronze', + }, + 'notes10000': { + img: '/fluent-emoji/1f3d9.png', + bg: 'linear-gradient(0deg, rgb(220 223 225), rgb(172 192 207))', + frame: 'silver', + }, + 'notes20000': { + img: '/fluent-emoji/1f307.png', + bg: 'linear-gradient(0deg, rgb(220 223 225), rgb(172 192 207))', + frame: 'silver', + }, + 'notes30000': { + img: '/fluent-emoji/1f306.png', + bg: 'linear-gradient(0deg, rgb(144 224 255), rgb(255 168 252))', + frame: 'silver', + }, + 'notes40000': { + img: '/fluent-emoji/1f303.png', + bg: 'linear-gradient(0deg, rgb(197 69 192), rgb(2 112 155))', + frame: 'silver', + }, + 'notes50000': { + img: '/fluent-emoji/1fa90.png', + bg: 'linear-gradient(0deg, rgb(144 224 255), rgb(255 168 252))', + frame: 'gold', + }, + 'notes60000': { + img: '/fluent-emoji/2604.png', + bg: 'linear-gradient(0deg, rgb(197 69 192), rgb(2 112 155))', + frame: 'gold', + }, + 'notes70000': { + img: '/fluent-emoji/1f30c.png', + bg: 'linear-gradient(0deg, rgb(144 224 255), rgb(255 168 252))', + frame: 'gold', + }, + 'notes80000': { + img: '/fluent-emoji/1f30c.png', + bg: 'linear-gradient(0deg, rgb(197 69 192), rgb(2 112 155))', + frame: 'gold', + }, + 'notes90000': { + img: '/fluent-emoji/1f30c.png', + bg: 'linear-gradient(0deg, rgb(255 232 119), rgb(255 140 41))', + frame: 'gold', + }, + 'notes100000': { + img: '/fluent-emoji/267e.png', + bg: 'linear-gradient(0deg, rgb(255 232 119), rgb(255 140 41))', + frame: 'platinum', + }, + 'login3': { + img: '/fluent-emoji/1f331.png', + bg: null, + frame: 'bronze', + }, + 'login7': { + img: '/fluent-emoji/1f331.png', + bg: 'linear-gradient(0deg, rgb(59 187 116), rgb(199 211 102))', + frame: 'bronze', + }, + 'login15': { + img: '/fluent-emoji/1f331.png', + bg: 'linear-gradient(0deg, rgb(144, 224, 255), rgb(255, 168, 252))', + frame: 'bronze', + }, + 'login30': { + img: '/fluent-emoji/1fab4.png', + bg: null, + frame: 'bronze', + }, + 'login60': { + img: '/fluent-emoji/1fab4.png', + bg: 'linear-gradient(0deg, rgb(59 187 116), rgb(199 211 102))', + frame: 'bronze', + }, + 'login100': { + img: '/fluent-emoji/1fab4.png', + bg: 'linear-gradient(0deg, rgb(144, 224, 255), rgb(255, 168, 252))', + frame: 'silver', + }, + 'login200': { + img: '/fluent-emoji/1f333.png', + bg: null, + frame: 'silver', + }, + 'login300': { + img: '/fluent-emoji/1f333.png', + bg: 'linear-gradient(0deg, rgb(59 187 116), rgb(199 211 102))', + frame: 'silver', + }, + 'login400': { + img: '/fluent-emoji/1f333.png', + bg: 'linear-gradient(0deg, rgb(144, 224, 255), rgb(255, 168, 252))', + frame: 'silver', + }, + 'login500': { + img: '/fluent-emoji/1f304.png', + bg: null, + frame: 'silver', + }, + 'login600': { + img: '/fluent-emoji/1f304.png', + bg: 'linear-gradient(0deg, rgb(59 187 116), rgb(199 211 102))', + frame: 'gold', + }, + 'login700': { + img: '/fluent-emoji/1f304.png', + bg: 'linear-gradient(0deg, rgb(144, 224, 255), rgb(255, 168, 252))', + frame: 'gold', + }, + 'login800': { + img: '/fluent-emoji/1f307.png', + bg: null, + frame: 'gold', + }, + 'login900': { + img: '/fluent-emoji/1f307.png', + bg: 'linear-gradient(0deg, rgb(59 187 116), rgb(199 211 102))', + frame: 'gold', + }, + 'login1000': { + img: '/fluent-emoji/1f307.png', + bg: 'linear-gradient(0deg, rgb(144, 224, 255), rgb(255, 168, 252))', + frame: 'platinum', + }, + 'noteClipped1': { + img: '/fluent-emoji/1f587.png', + bg: null, + frame: 'bronze', + }, + 'noteFavorited1': { + img: '/fluent-emoji/1f31f.png', + bg: null, + frame: 'bronze', + }, + 'profileFilled': { + img: '/fluent-emoji/1f44c.png', + bg: 'linear-gradient(0deg, rgb(187 183 59), rgb(255 143 77))', + frame: 'bronze', + }, + 'markedAsCat': { + img: '/fluent-emoji/1f408.png', + bg: 'linear-gradient(0deg, rgb(187 183 59), rgb(255 143 77))', + frame: 'bronze', + }, + 'following1': { + img: '/fluent-emoji/2618.png', + bg: 'linear-gradient(0deg, rgb(59 187 116), rgb(199 211 102))', + frame: 'bronze', + }, + 'following10': { + img: '/fluent-emoji/1f6b8.png', + bg: 'linear-gradient(0deg, rgb(59 187 116), rgb(199 211 102))', + frame: 'bronze', + }, + 'following50': { + img: '/fluent-emoji/1f91d.png', + bg: 'linear-gradient(0deg, rgb(59 187 116), rgb(199 211 102))', + frame: 'bronze', + }, + 'following100': { + img: '/fluent-emoji/1f4af.png', + bg: 'linear-gradient(0deg, rgb(255 53 184), rgb(255 206 69))', + frame: 'silver', + }, + 'following300': { + img: '/fluent-emoji/1f970.png', + bg: 'linear-gradient(0deg, rgb(144 224 255), rgb(255 168 252))', + frame: 'silver', + }, + 'followers1': { + img: '/fluent-emoji/2618.png', + bg: 'linear-gradient(0deg, rgb(59 187 116), rgb(199 211 102))', + frame: 'bronze', + }, + 'followers10': { + img: '/fluent-emoji/1f44b.png', + bg: 'linear-gradient(0deg, rgb(59 187 116), rgb(199 211 102))', + frame: 'bronze', + }, + 'followers50': { + img: '/fluent-emoji/1f411.png', + bg: 'linear-gradient(0deg, rgb(220 223 225), rgb(172 192 207))', + frame: 'bronze', + }, + 'followers100': { + img: '/fluent-emoji/1f396.png', + bg: 'linear-gradient(0deg, rgb(144 224 255), rgb(255 168 252))', + frame: 'silver', + }, + 'followers300': { + img: '/fluent-emoji/1f3c6.png', + bg: 'linear-gradient(0deg, rgb(144 224 255), rgb(255 168 252))', + frame: 'silver', + }, + 'followers500': { + img: '/fluent-emoji/1f4e1.png', + bg: 'linear-gradient(0deg, rgb(220 223 225), rgb(172 192 207))', + frame: 'gold', + }, + 'followers1000': { + img: '/fluent-emoji/1f451.png', + bg: 'linear-gradient(0deg, rgb(255 232 119), rgb(255 140 41))', + frame: 'platinum', + }, + 'collectAchievements30': { + img: '/fluent-emoji/1f3c5.png', + bg: 'linear-gradient(0deg, rgb(255 77 77), rgb(247 155 214))', + frame: 'silver', + }, + 'iLoveMisskey': { + img: '/fluent-emoji/2764.png', + bg: 'linear-gradient(0deg, rgb(255 77 77), rgb(247 155 214))', + frame: 'silver', + }, + 'client30min': { + img: '/fluent-emoji/1f552.png', + bg: 'linear-gradient(0deg, rgb(220 223 225), rgb(172 192 207))', + frame: 'bronze', + }, + 'noteDeletedWithin1min': { + img: '/fluent-emoji/1f5d1.png', + bg: 'linear-gradient(0deg, rgb(220 223 225), rgb(172 192 207))', + frame: 'bronze', + }, + 'postedAtLateNight': { + img: '/fluent-emoji/1f319.png', + bg: 'linear-gradient(0deg, rgb(197 69 192), rgb(2 112 155))', + frame: 'bronze', + }, + 'postedAt0min0sec': { + img: '/fluent-emoji/1f55b.png', + bg: 'linear-gradient(0deg, rgb(58 231 198), rgb(37 194 255))', + frame: 'bronze', + }, + 'selfQuote': { + img: '/fluent-emoji/1f4dd.png', + bg: null, + frame: 'bronze', + }, + 'htl20npm': { + img: '/fluent-emoji/1f30a.png', + bg: 'linear-gradient(0deg, rgb(220 223 225), rgb(172 192 207))', + frame: 'bronze', + }, + 'driveFolderCircularReference': { + img: '/fluent-emoji/1f4c2.png', + bg: 'linear-gradient(0deg, rgb(144 224 255), rgb(255 168 252))', + frame: 'bronze', + }, + 'reactWithoutRead': { + img: '/fluent-emoji/2753.png', + bg: 'linear-gradient(0deg, rgb(144 224 255), rgb(255 168 252))', + frame: 'bronze', + }, + 'clickedClickHere': { + img: '/fluent-emoji/2757.png', + bg: 'linear-gradient(0deg, rgb(144 224 255), rgb(255 168 252))', + frame: 'bronze', + }, + 'justPlainLucky': { + img: '/fluent-emoji/1f340.png', + bg: 'linear-gradient(0deg, rgb(187 183 59), rgb(255 143 77))', + frame: 'silver', + }, + 'setNameToSyuilo': { + img: '/fluent-emoji/1f36e.png', + bg: 'linear-gradient(0deg, rgb(187 183 59), rgb(255 143 77))', + frame: 'bronze', + }, + 'passedSinceAccountCreated1': { + img: '/fluent-emoji/0031-20e3.png', + bg: null, + frame: 'bronze', + }, + 'passedSinceAccountCreated2': { + img: '/fluent-emoji/0032-20e3.png', + bg: null, + frame: 'silver', + }, + 'passedSinceAccountCreated3': { + img: '/fluent-emoji/0033-20e3.png', + bg: null, + frame: 'gold', + }, + 'loggedInOnBirthday': { + img: '/fluent-emoji/1f382.png', + bg: 'linear-gradient(0deg, rgb(144 224 255), rgb(255 168 252))', + frame: 'silver', + }, + 'cookieClicked': { + img: '/fluent-emoji/1f36a.png', + bg: 'linear-gradient(0deg, rgb(187 183 59), rgb(255 143 77))', + frame: 'bronze', + }, + 'brainDiver': { + img: '/fluent-emoji/1f9e0.png', + bg: 'linear-gradient(0deg, rgb(144, 224, 255), rgb(255, 168, 252))', + frame: 'bronze', + }, +} as const satisfies Record<typeof ACHIEVEMENT_TYPES[number], { + img: string; + bg: string | null; + frame: 'bronze' | 'silver' | 'gold' | 'platinum'; +}>; + +export const claimedAchievements = ($i && $i.achievements) ? $i.achievements.map(x => x.name) : []; + +export function claimAchievement(type: typeof ACHIEVEMENT_TYPES[number]) { + if (claimedAchievements.includes(type)) return; + os.api('i/claim-achievement', { name: type }); + claimedAchievements.push(type); +} + +if (_DEV_) { + (window as any).unlockAllAchievements = async () => { + for (const t of ACHIEVEMENT_TYPES) { + await new Promise(resolve => setTimeout(resolve, 100)); + claimAchievement(t); + } + }; +} diff --git a/packages/frontend/src/scripts/get-note-menu.ts b/packages/frontend/src/scripts/get-note-menu.ts index 7a426ec722..da7f2a5c20 100644 --- a/packages/frontend/src/scripts/get-note-menu.ts +++ b/packages/frontend/src/scripts/get-note-menu.ts @@ -1,6 +1,7 @@ import { defineAsyncComponent, Ref, inject } from 'vue'; import * as misskey from 'misskey-js'; import { pleaseLogin } from './please-login'; +import { claimAchievement } from './achievements'; import { $i } from '@/account'; import { i18n } from '@/i18n'; import { instance } from '@/instance'; @@ -38,6 +39,10 @@ export function getNoteMenu(props: { os.api('notes/delete', { noteId: appearNote.id, }); + + if (Date.now() - new Date(appearNote.createdAt).getTime() < 1000 * 60) { + claimAchievement('noteDeletedWithin1min'); + } }); } @@ -53,10 +58,15 @@ export function getNoteMenu(props: { }); os.post({ initialNote: appearNote, renote: appearNote.renote, reply: appearNote.reply, channel: appearNote.channel }); + + if (Date.now() - new Date(appearNote.createdAt).getTime() < 1000 * 60) { + claimAchievement('noteDeletedWithin1min'); + } }); } function toggleFavorite(favorite: boolean): void { + claimAchievement('noteFavorited1'); os.apiWithDialog(favorite ? 'notes/favorites/create' : 'notes/favorites/delete', { noteId: appearNote.id, }); @@ -118,11 +128,13 @@ export function getNoteMenu(props: { const clip = await os.apiWithDialog('clips/create', result); + claimAchievement('noteClipped1'); os.apiWithDialog('clips/add-note', { clipId: clip.id, noteId: appearNote.id }); }, }, null, ...clips.map(clip => ({ text: clip.name, action: () => { + claimAchievement('noteClipped1'); os.promiseDialog( os.api('clips/add-note', { clipId: clip.id, noteId: appearNote.id }), null, From 59748f07d1183179b7d54773f714a1fc77c63b61 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 21 Jan 2023 13:18:17 +0900 Subject: [PATCH 27/70] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 21fecb6817..1c136995d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ You should also include the user name that made the change. ## 13.x.x (unreleased) ### Improvements +- 実績機能 - Playのプリセットを追加 - Playのscriptの文字数制限を緩和 - AiScript GUIの強化 From 0b4a7e8166318329883ebeb61d8376948e557cbc Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 21 Jan 2023 13:20:09 +0900 Subject: [PATCH 28/70] enhance(server): set Cache-Control header for some routes --- packages/backend/src/server/ServerService.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/server/ServerService.ts b/packages/backend/src/server/ServerService.ts index 47d11ed0ae..eb6a3795eb 100644 --- a/packages/backend/src/server/ServerService.ts +++ b/packages/backend/src/server/ServerService.ts @@ -82,13 +82,13 @@ export class ServerService { fastify.get<{ Params: { path: string }; Querystring: { static?: any; }; }>('/emoji/:path(.*)', async (request, reply) => { const path = request.params.path; + reply.header('Cache-Control', 'public, max-age=86400'); + if (!path.match(/^[a-zA-Z0-9\-_@\.]+?\.webp$/)) { reply.code(404); return; } - reply.header('Cache-Control', 'public, max-age=86400'); - const name = path.split('@')[0].replace('.webp', ''); const host = path.split('@')[1]?.replace('.webp', ''); @@ -132,6 +132,8 @@ export class ServerService { relations: ['avatar'], }); + reply.header('Cache-Control', 'public, max-age=86400'); + if (user) { reply.redirect(this.userEntityService.getAvatarUrlSync(user)); } else { @@ -143,6 +145,7 @@ export class ServerService { const [temp, cleanup] = await createTemp(); await genIdenticon(request.params.x, fs.createWriteStream(temp)); reply.header('Content-Type', 'image/png'); + reply.header('Cache-Control', 'public, max-age=86400'); return fs.createReadStream(temp).on('close', () => cleanup()); }); From 7e89e709346b466f311f9556c592919f2358b1e7 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 21 Jan 2023 14:09:01 +0900 Subject: [PATCH 29/70] enhance(server): separate job queue process (#9662) * enhance(server): separate job queue process * add commands for build with swc --- .../src/{RootModule.ts => MainModule.ts} | 6 ++--- packages/backend/src/boot/master.ts | 25 ++++++++++++------- packages/backend/src/boot/worker.ts | 19 ++++---------- .../src/core/chart/ChartManagementService.ts | 2 +- .../backend/src/queue/QueueProcessorModule.ts | 2 ++ 5 files changed, 27 insertions(+), 27 deletions(-) rename packages/backend/src/{RootModule.ts => MainModule.ts} (63%) diff --git a/packages/backend/src/RootModule.ts b/packages/backend/src/MainModule.ts similarity index 63% rename from packages/backend/src/RootModule.ts rename to packages/backend/src/MainModule.ts index 3fc3927768..fc568e883e 100644 --- a/packages/backend/src/RootModule.ts +++ b/packages/backend/src/MainModule.ts @@ -1,13 +1,13 @@ import { Module } from '@nestjs/common'; import { ServerModule } from '@/server/ServerModule.js'; import { GlobalModule } from '@/GlobalModule.js'; -import { QueueProcessorModule } from '@/queue/QueueProcessorModule.js'; +import { DaemonModule } from '@/daemons/DaemonModule.js'; @Module({ imports: [ GlobalModule, ServerModule, - QueueProcessorModule, + DaemonModule, ], }) -export class RootModule {} +export class MainModule {} diff --git a/packages/backend/src/boot/master.ts b/packages/backend/src/boot/master.ts index 4630217c4c..93cb3131ba 100644 --- a/packages/backend/src/boot/master.ts +++ b/packages/backend/src/boot/master.ts @@ -17,6 +17,9 @@ import { JanitorService } from '@/daemons/JanitorService.js'; import { QueueStatsService } from '@/daemons/QueueStatsService.js'; import { ServerStatsService } from '@/daemons/ServerStatsService.js'; import { NestLogger } from '@/NestLogger.js'; +import { ChartManagementService } from '@/core/chart/ChartManagementService.js'; +import { ServerService } from '@/server/ServerService.js'; +import { MainModule } from '@/MainModule.js'; import { envOption } from '../env.js'; const _filename = fileURLToPath(import.meta.url); @@ -70,6 +73,15 @@ export async function masterMain() { process.exit(1); } + const app = await NestFactory.createApplicationContext(MainModule, { + logger: new NestLogger(), + }); + app.enableShutdownHooks(); + + // start server + const serverService = app.get(ServerService); + serverService.launch(); + bootLogger.succ('Misskey initialized'); if (!envOption.disableClustering) { @@ -78,15 +90,10 @@ export async function masterMain() { bootLogger.succ(`Now listening on port ${config.port} on ${config.url}`, null, true); - if (!envOption.noDaemons) { - const daemons = await NestFactory.createApplicationContext(DaemonModule, { - logger: new NestLogger(), - }); - daemons.enableShutdownHooks(); - daemons.get(JanitorService).start(); - daemons.get(QueueStatsService).start(); - daemons.get(ServerStatsService).start(); - } + app.get(ChartManagementService).start(); + app.get(JanitorService).start(); + app.get(QueueStatsService).start(); + app.get(ServerStatsService).start(); } function showEnvironment(): void { diff --git a/packages/backend/src/boot/worker.ts b/packages/backend/src/boot/worker.ts index f29e37de78..e0574643b7 100644 --- a/packages/backend/src/boot/worker.ts +++ b/packages/backend/src/boot/worker.ts @@ -1,32 +1,23 @@ import cluster from 'node:cluster'; import { NestFactory } from '@nestjs/core'; -import { envOption } from '@/env.js'; import { ChartManagementService } from '@/core/chart/ChartManagementService.js'; -import { ServerService } from '@/server/ServerService.js'; import { QueueProcessorService } from '@/queue/QueueProcessorService.js'; import { NestLogger } from '@/NestLogger.js'; -import { RootModule } from '../RootModule.js'; +import { QueueProcessorModule } from '@/queue/QueueProcessorModule.js'; /** * Init worker process */ export async function workerMain() { - const app = await NestFactory.createApplicationContext(RootModule, { + const jobQueue = await NestFactory.createApplicationContext(QueueProcessorModule, { logger: new NestLogger(), }); - app.enableShutdownHooks(); - - // start server - const serverService = app.get(ServerService); - serverService.launch(); + jobQueue.enableShutdownHooks(); // start job queue - if (!envOption.onlyServer) { - const queueProcessorService = app.get(QueueProcessorService); - queueProcessorService.start(); - } + jobQueue.get(QueueProcessorService).start(); - app.get(ChartManagementService).run(); + jobQueue.get(ChartManagementService).start(); if (cluster.isWorker) { // Send a 'ready' message to parent process diff --git a/packages/backend/src/core/chart/ChartManagementService.ts b/packages/backend/src/core/chart/ChartManagementService.ts index 37de30b71c..4fba1b57d0 100644 --- a/packages/backend/src/core/chart/ChartManagementService.ts +++ b/packages/backend/src/core/chart/ChartManagementService.ts @@ -54,7 +54,7 @@ export class ChartManagementService implements OnApplicationShutdown { } @bindThis - public async run() { + public async start() { // 20分おきにメモリ情報をDBに書き込み this.saveIntervalId = setInterval(() => { for (const chart of this.charts) { diff --git a/packages/backend/src/queue/QueueProcessorModule.ts b/packages/backend/src/queue/QueueProcessorModule.ts index 034e9cc5a5..6a8f35cdda 100644 --- a/packages/backend/src/queue/QueueProcessorModule.ts +++ b/packages/backend/src/queue/QueueProcessorModule.ts @@ -1,5 +1,6 @@ import { Module } from '@nestjs/common'; import { CoreModule } from '@/core/CoreModule.js'; +import { GlobalModule } from '@/GlobalModule.js'; import { QueueLoggerService } from './QueueLoggerService.js'; import { QueueProcessorService } from './QueueProcessorService.js'; import { DbQueueProcessorsService } from './DbQueueProcessorsService.js'; @@ -34,6 +35,7 @@ import { ExportFavoritesProcessorService } from './processors/ExportFavoritesPro @Module({ imports: [ + GlobalModule, CoreModule, ], providers: [ From 951ab90b1afff4e7ba61496cdd00dc1a94113491 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 21 Jan 2023 14:10:09 +0900 Subject: [PATCH 30/70] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c136995d7..ef8238ab23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ You should also include the user name that made the change. - リアクション一覧詳細ダイアログを表示できるように - 存在しないカスタム絵文字をテキストで表示するように - Alt text in image viewer +- ジョブキューのプロセスとWebサーバーのプロセスを分離 ### Bugfixes - playを削除する手段がなかったのを修正 From 9d367882fb7e5fc375b7d14e6409a735e68fca8a Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 21 Jan 2023 15:30:29 +0900 Subject: [PATCH 31/70] add some achievements --- locales/ja-JP.yml | 6 ++++++ packages/backend/src/core/AchievementService.ts | 2 ++ packages/frontend/src/components/MkPageWindow.vue | 15 ++++++++++++++- packages/frontend/src/os.ts | 4 +++- packages/frontend/src/pages/scratchpad.vue | 4 ++++ packages/frontend/src/scripts/achievements.ts | 12 ++++++++++++ 6 files changed, 41 insertions(+), 2 deletions(-) diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index e46e6ab9de..aac5d5e833 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1119,6 +1119,12 @@ _achievements: _htl20npm: title: "流れるTL" description: "ホームタイムラインの流速が20npmを越す" + _outputHelloWorldOnScratchpad: + title: "Hello, world!" + description: "スクラッチパッドで hello world を出力した" + _open3windows: + title: "マルチウィンドウ" + description: "ウィンドウを3つ以上開いた状態にした" _driveFolderCircularReference: title: "循環参照" description: "ドライブのフォルダを再帰的な入れ子にしようとした" diff --git a/packages/backend/src/core/AchievementService.ts b/packages/backend/src/core/AchievementService.ts index 4ed75308eb..149974452c 100644 --- a/packages/backend/src/core/AchievementService.ts +++ b/packages/backend/src/core/AchievementService.ts @@ -65,6 +65,8 @@ const ACHIEVEMENT_TYPES = [ 'postedAt0min0sec', 'selfQuote', 'htl20npm', + 'outputHelloWorldOnScratchpad', + 'open3windows', 'driveFolderCircularReference', 'reactWithoutRead', 'clickedClickHere', diff --git a/packages/frontend/src/components/MkPageWindow.vue b/packages/frontend/src/components/MkPageWindow.vue index 25b9da2d0b..d12aafd06d 100644 --- a/packages/frontend/src/components/MkPageWindow.vue +++ b/packages/frontend/src/components/MkPageWindow.vue @@ -24,7 +24,7 @@ </template> <script lang="ts" setup> -import { ComputedRef, inject, provide } from 'vue'; +import { ComputedRef, inject, onMounted, onUnmounted, provide } from 'vue'; import RouterView from '@/components/global/RouterView.vue'; import MkWindow from '@/components/MkWindow.vue'; import { popout as _popout } from '@/scripts/popout'; @@ -35,6 +35,8 @@ import { mainRouter, routes } from '@/router'; import { Router } from '@/nirax'; import { i18n } from '@/i18n'; import { PageMetadata, provideMetadataReceiver, setPageMetadata } from '@/scripts/page-metadata'; +import { openingWindowsCount } from '@/os'; +import { claimAchievement } from '@/scripts/achievements'; const props = defineProps<{ initialPath: string; @@ -128,6 +130,17 @@ function popout() { windowEl.close(); } +onMounted(() => { + openingWindowsCount.value++; + if (openingWindowsCount.value >= 3) { + claimAchievement('open3windows'); + } +}); + +onUnmounted(() => { + openingWindowsCount.value--; +}); + defineExpose({ close, }); diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts index 111bd0cd8d..01f8244060 100644 --- a/packages/frontend/src/os.ts +++ b/packages/frontend/src/os.ts @@ -9,7 +9,7 @@ import * as Misskey from 'misskey-js'; import { i18n } from './i18n'; import MkPostFormDialog from '@/components/MkPostFormDialog.vue'; import MkWaitingDialog from '@/components/MkWaitingDialog.vue'; -import MkPageWindow from '@/components/MkPageWindow.vue' +import MkPageWindow from '@/components/MkPageWindow.vue'; import MkToast from '@/components/MkToast.vue'; import MkDialog from '@/components/MkDialog.vue'; import MkEmojiPickerDialog from '@/components/MkEmojiPickerDialog.vue'; @@ -18,6 +18,8 @@ import MkPopupMenu from '@/components/MkPopupMenu.vue'; import MkContextMenu from '@/components/MkContextMenu.vue'; import { MenuItem } from '@/types/menu'; +export const openingWindowsCount = ref(0); + export const apiWithDialog = (( endpoint: string, data: Record<string, any> = {}, diff --git a/packages/frontend/src/pages/scratchpad.vue b/packages/frontend/src/pages/scratchpad.vue index bd68df724e..0d52850b5d 100644 --- a/packages/frontend/src/pages/scratchpad.vue +++ b/packages/frontend/src/pages/scratchpad.vue @@ -47,6 +47,7 @@ import { definePageMetadata } from '@/scripts/page-metadata'; import { AsUiComponent, AsUiRoot, patch, registerAsUiLib, render } from '@/scripts/aiscript/ui'; import MkAsUi from '@/components/MkAsUi.vue'; import { miLocalStorage } from '@/local-storage'; +import { claimAchievement } from '@/scripts/achievements'; const parser = new Parser(); let aiscript: Interpreter; @@ -90,6 +91,9 @@ async function run() { }); }, out: (value) => { + if (value.type === 'str' && value.value.toLowerCase().replace(',', '').includes('hello world')) { + claimAchievement('outputHelloWorldOnScratchpad'); + } logs.value.push({ id: Math.random(), text: value.type === 'str' ? value.value : utils.valToString(value), diff --git a/packages/frontend/src/scripts/achievements.ts b/packages/frontend/src/scripts/achievements.ts index c8245ad3db..70ea0ffbd4 100644 --- a/packages/frontend/src/scripts/achievements.ts +++ b/packages/frontend/src/scripts/achievements.ts @@ -61,6 +61,8 @@ export const ACHIEVEMENT_TYPES = [ 'postedAt0min0sec', 'selfQuote', 'htl20npm', + 'outputHelloWorldOnScratchpad', + 'open3windows', 'driveFolderCircularReference', 'reactWithoutRead', 'clickedClickHere', @@ -346,6 +348,16 @@ export const ACHIEVEMENT_BADGES = { bg: 'linear-gradient(0deg, rgb(220 223 225), rgb(172 192 207))', frame: 'bronze', }, + 'outputHelloWorldOnScratchpad': { + img: '/fluent-emoji/1f530.png', + bg: 'linear-gradient(0deg, rgb(58 231 198), rgb(37 194 255))', + frame: 'bronze', + }, + 'open3windows': { + img: '/fluent-emoji/1f5a5.png', + bg: 'linear-gradient(0deg, rgb(144 224 255), rgb(255 168 252))', + frame: 'bronze', + }, 'driveFolderCircularReference': { img: '/fluent-emoji/1f4c2.png', bg: 'linear-gradient(0deg, rgb(144 224 255), rgb(255 168 252))', From 2b377a3dc5f0beccf36d4b37237adb9c39e92652 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 21 Jan 2023 16:06:49 +0900 Subject: [PATCH 32/70] add some achievements --- locales/ja-JP.yml | 6 +++++ .../backend/src/core/AchievementService.ts | 2 ++ packages/frontend/src/init.ts | 11 +++++--- packages/frontend/src/pages/achievements.vue | 27 ++++++++++++++++++- packages/frontend/src/scripts/achievements.ts | 16 +++++++++-- 5 files changed, 56 insertions(+), 6 deletions(-) diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index aac5d5e833..fb9a3435a6 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1095,6 +1095,9 @@ _achievements: _collectAchievements30: title: "実績コレクター" description: "実績を30個以上獲得した" + _viewAchievements3min: + title: "実績好き" + description: "実績一覧を3分以上眺め続けた" _iLoveMisskey: title: "I Love Misskey" description: "\"I ❤ #Misskey\"を投稿した" @@ -1152,6 +1155,9 @@ _achievements: _loggedInOnBirthday: title: "ハッピーバースデー" description: "誕生日にログインした" + _loggedInOnNewYearsDay: + title: "あけましておめでとうございます" + description: "元日にログインした" _cookieClicked: title: "クッキーをクリックするゲーム" description: "クッキーをクリックした" diff --git a/packages/backend/src/core/AchievementService.ts b/packages/backend/src/core/AchievementService.ts index 149974452c..26dd356d36 100644 --- a/packages/backend/src/core/AchievementService.ts +++ b/packages/backend/src/core/AchievementService.ts @@ -41,6 +41,7 @@ const ACHIEVEMENT_TYPES = [ 'passedSinceAccountCreated2', 'passedSinceAccountCreated3', 'loggedInOnBirthday', + 'loggedInOnNewYearsDay', 'noteClipped1', 'noteFavorited1', 'profileFilled', @@ -58,6 +59,7 @@ const ACHIEVEMENT_TYPES = [ 'followers500', 'followers1000', 'collectAchievements30', + 'viewAchievements3min', 'iLoveMisskey', 'client30min', 'noteDeletedWithin1min', diff --git a/packages/frontend/src/init.ts b/packages/frontend/src/init.ts index a2723d479c..09fb7caf14 100644 --- a/packages/frontend/src/init.ts +++ b/packages/frontend/src/init.ts @@ -346,10 +346,11 @@ import { claimAchievement, claimedAchievements } from './scripts/achievements'; }); } + const now = new Date(); + const m = now.getMonth() + 1; + const d = now.getDate(); + if ($i.birthday) { - const now = new Date(); - const m = now.getMonth() + 1; - const d = now.getDate(); const bm = parseInt($i.birthday.split('-')[1]); const bd = parseInt($i.birthday.split('-')[2]); if (m === bm && d === bd) { @@ -357,6 +358,10 @@ import { claimAchievement, claimedAchievements } from './scripts/achievements'; } } + if (m === 1 && d === 1) { + claimAchievement('loggedInOnNewYearsDay'); + } + if ($i.loggedInDays >= 3) claimAchievement('login3'); if ($i.loggedInDays >= 7) claimAchievement('login7'); if ($i.loggedInDays >= 15) claimAchievement('login15'); diff --git a/packages/frontend/src/pages/achievements.vue b/packages/frontend/src/pages/achievements.vue index b6cd174b41..3cec8f630f 100644 --- a/packages/frontend/src/pages/achievements.vue +++ b/packages/frontend/src/pages/achievements.vue @@ -8,11 +8,36 @@ </template> <script lang="ts" setup> -import { ref } from 'vue'; +import { onActivated, onDeactivated, onMounted, onUnmounted, ref } from 'vue'; import MkAchievements from '@/components/MkAchievements.vue'; import { i18n } from '@/i18n'; import { definePageMetadata } from '@/scripts/page-metadata'; import { $i } from '@/account'; +import { claimAchievement } from '@/scripts/achievements'; + +let timer; + +function viewAchievements3min() { + claimAchievement('viewAchievements3min'); +} + +onMounted(() => { + if (timer == null) timer = window.setTimeout(viewAchievements3min, 1000 * 60 * 3); +}); + +onUnmounted(() => { + window.clearTimeout(timer); + timer = null; +}); + +onActivated(() => { + if (timer == null) timer = window.setTimeout(viewAchievements3min, 1000 * 60 * 3); +}); + +onDeactivated(() => { + window.clearTimeout(timer); + timer = null; +}); definePageMetadata({ title: i18n.ts.achievements, diff --git a/packages/frontend/src/scripts/achievements.ts b/packages/frontend/src/scripts/achievements.ts index 70ea0ffbd4..8f484f8925 100644 --- a/packages/frontend/src/scripts/achievements.ts +++ b/packages/frontend/src/scripts/achievements.ts @@ -37,6 +37,7 @@ export const ACHIEVEMENT_TYPES = [ 'passedSinceAccountCreated2', 'passedSinceAccountCreated3', 'loggedInOnBirthday', + 'loggedInOnNewYearsDay', 'noteClipped1', 'noteFavorited1', 'profileFilled', @@ -54,6 +55,7 @@ export const ACHIEVEMENT_TYPES = [ 'followers500', 'followers1000', 'collectAchievements30', + 'viewAchievements3min', 'iLoveMisskey', 'client30min', 'noteDeletedWithin1min', @@ -289,7 +291,7 @@ export const ACHIEVEMENT_BADGES = { frame: 'bronze', }, 'followers100': { - img: '/fluent-emoji/1f396.png', + img: '/fluent-emoji/1f60e.png', bg: 'linear-gradient(0deg, rgb(144 224 255), rgb(255 168 252))', frame: 'silver', }, @@ -313,6 +315,11 @@ export const ACHIEVEMENT_BADGES = { bg: 'linear-gradient(0deg, rgb(255 77 77), rgb(247 155 214))', frame: 'silver', }, + 'viewAchievements3min': { + img: '/fluent-emoji/1f3c5.png', + bg: 'linear-gradient(0deg, rgb(144 224 255), rgb(255 168 252))', + frame: 'bronze', + }, 'iLoveMisskey': { img: '/fluent-emoji/2764.png', bg: 'linear-gradient(0deg, rgb(255 77 77), rgb(247 155 214))', @@ -400,7 +407,12 @@ export const ACHIEVEMENT_BADGES = { }, 'loggedInOnBirthday': { img: '/fluent-emoji/1f382.png', - bg: 'linear-gradient(0deg, rgb(144 224 255), rgb(255 168 252))', + bg: 'linear-gradient(0deg, rgb(255 77 77), rgb(247 155 214))', + frame: 'silver', + }, + 'loggedInOnNewYearsDay': { + img: '/fluent-emoji/1f38d.png', + bg: 'linear-gradient(0deg, rgb(255 144 144), rgb(255 232 168))', frame: 'silver', }, 'cookieClicked': { From 8dc0e0abbbfb480994b47db86705d2faca76006e Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 21 Jan 2023 16:08:42 +0900 Subject: [PATCH 33/70] 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 fb9a3435a6..898ae01e72 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1158,6 +1158,7 @@ _achievements: _loggedInOnNewYearsDay: title: "あけましておめでとうございます" description: "元日にログインした" + flavor: "今年も弊インスタンスをよろしくお願いします" _cookieClicked: title: "クッキーをクリックするゲーム" description: "クッキーをクリックした" From 3bf775c9a8c9aadf8e8c5e7e8a1cbae5e168f2d9 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 21 Jan 2023 16:57:23 +0900 Subject: [PATCH 34/70] =?UTF-8?q?=E3=83=A6=E3=83=BC=E3=82=B6=E3=83=BC?= =?UTF-8?q?=E3=81=AE=E5=AE=9F=E7=B8=BE=E4=B8=80=E8=A6=A7=E3=82=92=E8=A6=8B?= =?UTF-8?q?=E3=82=8C=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 --- packages/frontend/src/pages/achievements.vue | 14 +++-- .../frontend/src/pages/user/achievements.vue | 52 +++++++++++++++++++ packages/frontend/src/pages/user/index.vue | 8 ++- 3 files changed, 68 insertions(+), 6 deletions(-) create mode 100644 packages/frontend/src/pages/user/achievements.vue diff --git a/packages/frontend/src/pages/achievements.vue b/packages/frontend/src/pages/achievements.vue index 3cec8f630f..14b8520696 100644 --- a/packages/frontend/src/pages/achievements.vue +++ b/packages/frontend/src/pages/achievements.vue @@ -15,7 +15,7 @@ import { definePageMetadata } from '@/scripts/page-metadata'; import { $i } from '@/account'; import { claimAchievement } from '@/scripts/achievements'; -let timer; +let timer: number | null; function viewAchievements3min() { claimAchievement('viewAchievements3min'); @@ -26,8 +26,10 @@ onMounted(() => { }); onUnmounted(() => { - window.clearTimeout(timer); - timer = null; + if (timer != null) { + window.clearTimeout(timer); + timer = null; + } }); onActivated(() => { @@ -35,8 +37,10 @@ onActivated(() => { }); onDeactivated(() => { - window.clearTimeout(timer); - timer = null; + if (timer != null) { + window.clearTimeout(timer); + timer = null; + } }); definePageMetadata({ diff --git a/packages/frontend/src/pages/user/achievements.vue b/packages/frontend/src/pages/user/achievements.vue new file mode 100644 index 0000000000..eaea26db4a --- /dev/null +++ b/packages/frontend/src/pages/user/achievements.vue @@ -0,0 +1,52 @@ +<template> +<MkSpacer :content-max="1200"> + <MkAchievements :user="user" :with-locked="false"/> +</MkSpacer> +</template> + +<script lang="ts" setup> +import { onActivated, onDeactivated, onMounted, onUnmounted, ref } from 'vue'; +import * as misskey from 'misskey-js'; +import MkAchievements from '@/components/MkAchievements.vue'; +import { i18n } from '@/i18n'; +import { claimAchievement } from '@/scripts/achievements'; +import { $i } from '@/account'; + +const props = defineProps<{ + user: misskey.entities.User; +}>(); + +let timer: number | null; + +function viewAchievements3min() { + if ($i && (props.user.id === $i.id)) { + claimAchievement('viewAchievements3min'); + } +} + +onMounted(() => { + if (timer == null) timer = window.setTimeout(viewAchievements3min, 1000 * 60 * 3); +}); + +onUnmounted(() => { + if (timer != null) { + window.clearTimeout(timer); + timer = null; + } +}); + +onActivated(() => { + if (timer == null) timer = window.setTimeout(viewAchievements3min, 1000 * 60 * 3); +}); + +onDeactivated(() => { + if (timer != null) { + window.clearTimeout(timer); + timer = null; + } +}); +</script> + +<style lang="scss" module> + +</style> diff --git a/packages/frontend/src/pages/user/index.vue b/packages/frontend/src/pages/user/index.vue index 7abae1ea30..d63aa3a3a5 100644 --- a/packages/frontend/src/pages/user/index.vue +++ b/packages/frontend/src/pages/user/index.vue @@ -6,6 +6,7 @@ <div v-if="user"> <XHome v-if="tab === 'home'" :user="user"/> <XActivity v-else-if="tab === 'activity'" :user="user"/> + <XAchievements v-else-if="tab === 'achievements'" :user="user"/> <XReactions v-else-if="tab === 'reactions'" :user="user"/> <XClips v-else-if="tab === 'clips'" :user="user"/> <XPages v-else-if="tab === 'pages'" :user="user"/> @@ -34,6 +35,7 @@ import { $i } from '@/account'; const XHome = defineAsyncComponent(() => import('./home.vue')); const XActivity = defineAsyncComponent(() => import('./activity.vue')); +const XAchievements = defineAsyncComponent(() => import('./achievements.vue')); const XReactions = defineAsyncComponent(() => import('./reactions.vue')); const XClips = defineAsyncComponent(() => import('./clips.vue')); const XPages = defineAsyncComponent(() => import('./pages.vue')); @@ -76,7 +78,11 @@ const headerTabs = $computed(() => user ? [{ key: 'activity', title: i18n.ts.activity, icon: 'ti ti-chart-line', -}, ...($i && ($i.id === user.id)) || user.publicReactions ? [{ +}, ...(user.host == null ? [{ + key: 'achievements', + title: i18n.ts.achievements, + icon: 'ti ti-military-award', +}] : []), ...($i && ($i.id === user.id)) || user.publicReactions ? [{ key: 'reactions', title: i18n.ts.reaction, icon: 'ti ti-mood-happy', From 4b75c68753deab3b445c8bea2d1b75f84357aac8 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 21 Jan 2023 16:59:58 +0900 Subject: [PATCH 35/70] =?UTF-8?q?fix(client):=20The=20=E2=80=A6=20button?= =?UTF-8?q?=20on=20notes=20does=20nothing=20when=20not=20logged=20in?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix #9659 --- CHANGELOG.md | 1 + packages/frontend/src/scripts/get-note-menu.ts | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef8238ab23..26632b8399 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ You should also include the user name that made the change. ### Bugfixes - playを削除する手段がなかったのを修正 +- The … button on notes does nothing when not logged in ## 13.0.0 (2023/01/16) diff --git a/packages/frontend/src/scripts/get-note-menu.ts b/packages/frontend/src/scripts/get-note-menu.ts index da7f2a5c20..b5d2251d28 100644 --- a/packages/frontend/src/scripts/get-note-menu.ts +++ b/packages/frontend/src/scripts/get-note-menu.ts @@ -187,7 +187,7 @@ export function getNoteMenu(props: { }); } - function notedetails(): void { + function openDetail(): void { os.pageWindow(`/notes/${appearNote.id}`); } @@ -225,7 +225,7 @@ export function getNoteMenu(props: { ), { icon: 'ti ti-info-circle', text: i18n.ts.details, - action: notedetails, + action: openDetail, }, { icon: 'ti ti-users', text: i18n.ts.reactions, From 8631740ca44b0e03d67ac2dd3c21ecf9ca9dcaa4 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 21 Jan 2023 17:01:02 +0900 Subject: [PATCH 36/70] =?UTF-8?q?fix(server):=20twitter=E3=81=A8=E9=80=A3?= =?UTF-8?q?=E6=90=BA=E3=81=99=E3=82=8B=E3=81=A8=E3=81=8D=E3=81=AB=20autwh?= =?UTF-8?q?=20is=20not=20a=20function=20=E3=81=AB=E3=81=AA=E3=82=8B?= =?UTF-8?q?=E3=81=AE=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 #9658 --- CHANGELOG.md | 1 + .../backend/src/server/api/integration/TwitterServerService.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 26632b8399..85246595ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ You should also include the user name that made the change. ### Bugfixes - playを削除する手段がなかったのを修正 - The … button on notes does nothing when not logged in +- twitterと連携するときに autwh is not a function になるのを修正 ## 13.0.0 (2023/01/16) diff --git a/packages/backend/src/server/api/integration/TwitterServerService.ts b/packages/backend/src/server/api/integration/TwitterServerService.ts index 9cfadbfa1a..f31a788d31 100644 --- a/packages/backend/src/server/api/integration/TwitterServerService.ts +++ b/packages/backend/src/server/api/integration/TwitterServerService.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import Redis from 'ioredis'; import { v4 as uuid } from 'uuid'; import { IsNull } from 'typeorm'; -import autwh from 'autwh'; +import * as autwh from 'autwh'; import type { Config } from '@/config.js'; import type { UserProfilesRepository, UsersRepository } from '@/models/index.js'; import { DI } from '@/di-symbols.js'; From 80bfa028311952fff02ddd0e5896347156f93132 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 21 Jan 2023 17:01:43 +0900 Subject: [PATCH 37/70] New Crowdin updates (#9666) * New translations ja-JP.yml (Thai) * New translations ja-JP.yml (Japanese, Kansai) * New translations ja-JP.yml (Japanese, Kansai) * New translations ja-JP.yml (Romanian) * New translations ja-JP.yml (French) * New translations ja-JP.yml (Spanish) * New translations ja-JP.yml (Arabic) * New translations ja-JP.yml (Catalan) * New translations ja-JP.yml (Czech) * New translations ja-JP.yml (German) * New translations ja-JP.yml (Greek) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Korean) * New translations ja-JP.yml (Dutch) * New translations ja-JP.yml (Polish) * New translations ja-JP.yml (Portuguese) * New translations ja-JP.yml (Russian) * New translations ja-JP.yml (Slovak) * New translations ja-JP.yml (Swedish) * New translations ja-JP.yml (Ukrainian) * New translations ja-JP.yml (Chinese Simplified) * New translations ja-JP.yml (Chinese Traditional) * New translations ja-JP.yml (English) * New translations ja-JP.yml (Vietnamese) * New translations ja-JP.yml (Indonesian) * New translations ja-JP.yml (Bengali) * New translations ja-JP.yml (Thai) * New translations ja-JP.yml (Japanese, Kansai) * New translations ja-JP.yml (Chinese Traditional) * New translations ja-JP.yml (Thai) * New translations ja-JP.yml (Korean) * New translations ja-JP.yml (Chinese Traditional) * New translations ja-JP.yml (Korean) --- locales/ar-SA.yml | 1 + locales/bn-BD.yml | 1 + locales/ca-ES.yml | 1 + locales/cs-CZ.yml | 1 + locales/de-DE.yml | 1 + locales/el-GR.yml | 1 + locales/en-US.yml | 1 + locales/es-ES.yml | 1 + locales/fr-FR.yml | 1 + locales/id-ID.yml | 1 + locales/it-IT.yml | 1 + locales/ja-KS.yml | 7 +- locales/ko-KR.yml | 217 ++++++++++++++++++++++++++++++++++++++++++++++ locales/nl-NL.yml | 1 + locales/pl-PL.yml | 1 + locales/pt-PT.yml | 1 + locales/ro-RO.yml | 1 + locales/ru-RU.yml | 1 + locales/sk-SK.yml | 1 + locales/sv-SE.yml | 1 + locales/th-TH.yml | 30 +++++++ locales/uk-UA.yml | 1 + locales/vi-VN.yml | 1 + locales/zh-CN.yml | 1 + locales/zh-TW.yml | 57 ++++++++++++ 25 files changed, 329 insertions(+), 3 deletions(-) diff --git a/locales/ar-SA.yml b/locales/ar-SA.yml index 7f465c61bb..1ff72668eb 100644 --- a/locales/ar-SA.yml +++ b/locales/ar-SA.yml @@ -108,6 +108,7 @@ clickToShow: "اضغط للعرض" sensitive: "محتوى حساس" add: "إضافة" reaction: "التفاعلات" +reactions: "التفاعلات" reactionSetting: "التفاعلات المراد عرضها في منتقي التفاعلات." reactionSettingDescription2: "اسحب لترتيب ، انقر للحذف ، استخدم \"+\" للإضافة." rememberNoteVisibility: "تذكر إعدادت مدى رؤية الملاحظات" diff --git a/locales/bn-BD.yml b/locales/bn-BD.yml index 28a01e657a..c6e94896e1 100644 --- a/locales/bn-BD.yml +++ b/locales/bn-BD.yml @@ -107,6 +107,7 @@ clickToShow: "দেখার জন্য ক্লিক করুন" sensitive: "সংবেদনশীল বিষয়বস্তু" add: "যুক্ত করুন" reaction: "প্রতিক্রিয়া" +reactions: "প্রতিক্রিয়া" reactionSetting: "রিঅ্যাকশন পিকারে যেসকল প্রতিক্রিয়া দেখানো হবে" reactionSettingDescription2: "পুনরায় সাজাতে টেনে আনুন, মুছতে ক্লিক করুন, যোগ করতে + টিপুন।" rememberNoteVisibility: "নোটের দৃশ্যমান্যতার সেটিংস মনে রাখুন" diff --git a/locales/ca-ES.yml b/locales/ca-ES.yml index be97b7f60a..8bc5bf0366 100644 --- a/locales/ca-ES.yml +++ b/locales/ca-ES.yml @@ -108,6 +108,7 @@ clickToShow: "Fes clic per mostrar" sensitive: "NSFW" add: "Afegir" reaction: "Reaccions" +reactions: "Reaccions" reactionSetting: "Reaccions a mostrar al selector de reaccions" reactionSettingDescription2: "Arrossega per reordenar, fes clic per suprimir, prem \"+\" per afegir." rememberNoteVisibility: "Recorda la configuració de visibilitat de les notes" diff --git a/locales/cs-CZ.yml b/locales/cs-CZ.yml index 4d7c0168fc..eb9ae6f87b 100644 --- a/locales/cs-CZ.yml +++ b/locales/cs-CZ.yml @@ -105,6 +105,7 @@ clickToShow: "Klikněte pro zobrazení" sensitive: "NSFW" add: "Přidat" reaction: "Reakce" +reactions: "Reakce" reactionSettingDescription2: "Přetažením změníte pořadí, kliknutím smažete, zmáčkněte \"+\" k přidání" rememberNoteVisibility: "Zapamatovat nastavení zobrazení poznámky" attachCancel: "Odstranit přílohu" diff --git a/locales/de-DE.yml b/locales/de-DE.yml index 71ed02cd43..f6095e4db6 100644 --- a/locales/de-DE.yml +++ b/locales/de-DE.yml @@ -110,6 +110,7 @@ clickToShow: "Zum Anzeigen anklicken" sensitive: "NSFW" add: "Hinzufügen" reaction: "Reaktionen" +reactions: "Reaktionen" reactionSetting: "In der Reaktionsauswahl anzuzeigende Reaktionen" reactionSettingDescription2: "Ziehe um Anzuordnen, klicke um zu löschen, drücke „+“ um hinzuzufügen" rememberNoteVisibility: "Notizsichtbarkeit merken" diff --git a/locales/el-GR.yml b/locales/el-GR.yml index 974e66c036..c711683ffc 100644 --- a/locales/el-GR.yml +++ b/locales/el-GR.yml @@ -103,6 +103,7 @@ you: "Εσύ" clickToShow: "Κάντε κλικ για εμφάνιση" add: "Προσθέστε" reaction: "Αντιδράσεις" +reactions: "Αντιδράσεις" reactionSetting: "Αντιδράσεις για εμφάνιση στην επιλογή αντίδρασης" reactionSettingDescription2: "Σύρετε για να αλλάξετε τη σειρά, κάντε κλικ για να διαγράψετε, πατήστε \"+\" για να προσθέσετε." rememberNoteVisibility: "Θυμήσου τις ρυθμίσεις ορατότητας σημειώματος" diff --git a/locales/en-US.yml b/locales/en-US.yml index 4cecca84f0..964c79caaf 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -110,6 +110,7 @@ clickToShow: "Click to show" sensitive: "NSFW" add: "Add" reaction: "Reactions" +reactions: "Reactions" reactionSetting: "Reactions to show in the reaction picker" reactionSettingDescription2: "Drag to reorder, click to delete, press \"+\" to add." rememberNoteVisibility: "Remember note visibility settings" diff --git a/locales/es-ES.yml b/locales/es-ES.yml index 8be0edc29c..47799a0917 100644 --- a/locales/es-ES.yml +++ b/locales/es-ES.yml @@ -110,6 +110,7 @@ clickToShow: "Click para ver" sensitive: "Marcado como sensible" add: "Agregar" reaction: "Reacción" +reactions: "Reacción" reactionSetting: "Reacciones para mostrar en el menú de reacciones" reactionSettingDescription2: "Arrastre para reordenar, click para borrar, apriete la tecla + para añadir." rememberNoteVisibility: "Recordar visibilidad" diff --git a/locales/fr-FR.yml b/locales/fr-FR.yml index eacff8c438..462b561e43 100644 --- a/locales/fr-FR.yml +++ b/locales/fr-FR.yml @@ -110,6 +110,7 @@ clickToShow: "Cliquer pour afficher" sensitive: "Contenu sensible" add: "Ajouter" reaction: "Réactions" +reactions: "Réactions" reactionSetting: "Réactions à afficher dans le sélecteur de réactions" reactionSettingDescription2: "Déplacer pour réorganiser, cliquer pour effacer, utiliser « + » pour ajouter." rememberNoteVisibility: "Activer l'option \" se souvenir de la visibilité des notes \" vous permet de réutiliser automatiquement la visibilité utilisée lors de la publication de votre note précédente." diff --git a/locales/id-ID.yml b/locales/id-ID.yml index 7f7d582ee3..87e37518f8 100644 --- a/locales/id-ID.yml +++ b/locales/id-ID.yml @@ -109,6 +109,7 @@ clickToShow: "Klik untuk melihat" sensitive: "Konten sensitif" add: "Tambahkan" reaction: "Reaksi" +reactions: "Reaksi" reactionSetting: "Reaksi untuk dimunculkan di bilah reaksi" reactionSettingDescription2: "Geser untuk memindah urutkan, klik untuk menghapus, tekan \"+\" untuk menambahkan" rememberNoteVisibility: "Ingat pengaturan visibilitas catatan" diff --git a/locales/it-IT.yml b/locales/it-IT.yml index 3879932e20..e215b9f79e 100644 --- a/locales/it-IT.yml +++ b/locales/it-IT.yml @@ -110,6 +110,7 @@ clickToShow: "Clicca per visualizzare" sensitive: "Contenuto sensibile" add: "Aggiungi" reaction: "Reazioni" +reactions: "Reazioni" reactionSetting: "Reazioni visualizzate sul pannello" reactionSettingDescription2: "Trascina per riorganizzare, clicca per cancellare, usa il pulsante \"+\" per aggiungere." rememberNoteVisibility: "Ricordare le impostazioni di visibilità delle note" diff --git a/locales/ja-KS.yml b/locales/ja-KS.yml index e95b068b0b..40d28b1961 100644 --- a/locales/ja-KS.yml +++ b/locales/ja-KS.yml @@ -8,9 +8,9 @@ search: "探す" notifications: "通知" username: "ユーザー名" password: "パスワード" -forgotPassword: "パスワード忘れてん" +forgotPassword: "パスワード忘れてもうた" fetchingAsApObject: "今ちと連合に照会しとるで" -ok: "OKや" +ok: "ええで" gotIt: "ほい" cancel: "やめとく" noThankYou: "やめとく" @@ -110,6 +110,7 @@ clickToShow: "押したら見えるで" sensitive: "ちょっとアカンやつやで" add: "増やす" reaction: "リアクション" +reactions: "リアクション" reactionSetting: "Reaction that will be displayed in Picker. " reactionSettingDescription2: "ドラッグで並び替え、クリックで削除、+を押して追加やで。" rememberNoteVisibility: "公開範囲覚えといて" @@ -607,7 +608,7 @@ wordMute: "ワードミュート" regexpError: "正規表現エラー" regexpErrorDescription: "{tab}ワードミュートの{line}行目の正規表現にエラーが出てきたで:" instanceMute: "インスタンスミュート" -userSaysSomething: "{name}が何か言ったようやで" +userSaysSomething: "{name}が何か言うとるわ" makeActive: "使うで" display: "表示" copy: "コピー" diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml index 4605e82960..3e6ab5a2f5 100644 --- a/locales/ko-KR.yml +++ b/locales/ko-KR.yml @@ -110,6 +110,7 @@ clickToShow: "클릭하여 보기" sensitive: "열람주의" add: "추가" reaction: "리액션" +reactions: "리액션" reactionSetting: "선택기에 표시할 리액션" reactionSettingDescription2: "끌어서 순서 변경, 클릭해서 삭제, +를 눌러서 추가할 수 있습니다." rememberNoteVisibility: "공개 범위를 기억하기" @@ -937,6 +938,221 @@ cannotPerformTemporary: "일시적으로 사용할 수 없음" cannotPerformTemporaryDescription: "조작 횟수 제한을 초과하여 일시적으로 사용이 불가합니다. 잠시 후 다시 시도해 주세요." preset: "프리셋" selectFromPresets: "프리셋에서 선택" +achievements: "도전과제" +_achievements: + earnedAt: "달성 일시" + _types: + _notes1: + title: "미스키 설정하고 있었는데요" + description: "첫 노트를 포스트했습니다" + flavor: "Misskey에 오신 것을 환영합니다!" + _notes10: + title: "노트 조금" + description: "10개의 노트를 작성했습니다" + _notes100: + title: "노트 많이" + description: "100개의 노트를 작성했습니다" + _notes500: + title: "노트로 뒤덮여버렸어" + description: "500개의 노트를 작성했습니다" + _notes1000: + title: "노트만 산더미" + description: "1,000개의 노트를 작성했습니다" + _notes5000: + title: "노트가 어디서 솟아?" + description: "5,000개의 노트를 작성했습니다" + _notes10000: + title: "슈퍼-노트" + description: "10,000개의 노트를 작성했습니다" + _notes20000: + title: "노트 더 없어?" + description: "20,000개의 노트를 작성했습니다" + _notes30000: + title: "노트노트노트" + description: "30,000개의 노트를 작성했습니다" + _notes40000: + title: "노트 공장" + description: "40,000개의 노트를 작성했습니다" + _notes50000: + title: "노트 행성" + description: "50,000개의 노트를 작성했습니다" + _notes60000: + title: "노트 퀘이사" + description: "60,000개의 노트를 작성했습니다" + _notes70000: + title: "노트 블랙홀" + description: "70,000개의 노트를 작성했습니다" + _notes80000: + title: "노트 은하" + description: "80,000개의 노트를 작성했습니다" + _notes90000: + title: "노트 우주" + description: "90,000개의 노트를 작성했습니다" + _notes100000: + title: "네 모든 노트는 내 거야" + description: "100,000개의 노트를 작성했습니다" + flavor: "이만큼 쓸 일도 없겠지만... 다른 할 일이 있진 않으신가요?" + _login3: + title: "비기너 I" + description: "총 3일간 로그인했습니다" + flavor: "오늘부터 여러분도 미스키스트에요!" + _login7: + title: "비기너 II" + description: "총 7일간 로그인했습니다" + flavor: "슬슬 익숙해지셨나요?" + _login15: + title: "비기너 III" + description: "총 15일간 로그인했습니다" + _login30: + title: "미스키스트 I" + description: "총 30일간 로그인했습니다" + _login60: + title: "미스키스트 II" + description: "총 60일간 로그인했습니다" + _login100: + title: "미스키스트 III" + description: "총 100일간 로그인했습니다" + flavor: "그 유저, 미스키스트를 위하여" + _login200: + title: "단골 I" + description: "총 200일간 로그인했습니다" + _login300: + title: "단골 II" + description: "총 300일간 로그인했습니다" + _login400: + title: "단골 III" + description: "총 400일간 로그인했습니다" + _login500: + title: "베테랑 I" + description: "총 500일간 로그인했습니다" + flavor: "여러분, 저 이 노트들 좋아해요" + _login600: + title: "베테랑 II" + description: "총 600일간 로그인했습니다" + _login700: + title: "베테랑 III" + description: "총 700일간 로그인했습니다" + _login800: + title: "노트 마스터 I" + description: "총 800일간 로그인했습니다" + _login900: + title: "노트 마스터 II" + description: "총 900일간 로그인했습니다" + _login1000: + title: "노트 마스터 III" + description: "총 1,000일간 로그인했습니다" + flavor: "미스키를 사용해 주셔서 감사합니다!" + _noteClipped1: + title: "클립할 수밖에 없었어" + description: "처음으로 노트를 클립했습니다" + _noteFavorited1: + title: "별을 바라보는 자" + description: "처음으로 노트를 즐겨찾기했습니다" + _profileFilled: + title: "준비 완료" + description: "프로필 설정을 완료했습니다" + _markedAsCat: + title: "나는 고양이다냥!" + description: "계정을 고양이로 설정했습니다냥" + flavor: "냐냐냐냐냐냐아아아아앙!" + _following1: + title: "첫 팔로우" + description: "사용자를 처음으로 팔로우했습니다" + _following10: + title: "팔로우, 팔로우" + description: "10명의 사용자를 팔로우했습니다" + _following50: + title: "친구 잔뜩" + description: "50명의 사용자를 팔로우했습니다" + _following100: + title: "주소록 한 권으론 부족해" + description: "100명의 사용자를 팔로우했습니다" + _following300: + title: "친구가 넘쳐나" + description: "300명의 사용자를 팔로우했습니다" + _followers1: + title: "첫 팔로워" + description: "사용자가 처음으로 팔로잉했습니다" + _followers10: + title: "날 따라와!" + description: "10명의 사용자가 팔로우했습니다" + _followers50: + title: "이곳저곳" + description: "50명의 사용자가 팔로우했습니다" + _followers100: + title: "인기왕" + description: "100명의 사용자가 팔로우했습니다" + _followers300: + title: "줄 좀 서봐요" + description: "100명의 사용자가 팔로우했습니다" + _followers500: + title: "기지국" + description: "500명의 사용자가 팔로우했습니다" + _followers1000: + title: "유명인사" + description: "1,000명의 사용자가 팔로우했습니다" + _collectAchievements30: + title: "도전과제 콜렉터" + description: "30개의 도전과제를 획득했습니다" + _iLoveMisskey: + title: "I Love Misskey" + description: "\"I ❤ #Misskey\"를 포스트했습니다" + flavor: "Misskey를 이용해주셔서 감사합니다! - 개발팀 일동" + _client30min: + title: "잠깐 쉬어" + description: "클라이언트를 시작하고 30분이 경과하였습니다" + _noteDeletedWithin1min: + title: "있었는데요 없었습니다" + description: "노트를 포스트한 후 1분 이내에 삭제했습니다" + _postedAtLateNight: + title: "올빼미" + description: "한밤중에 노트를 포스트했습니다" + flavor: "잠 좀 자세요. 걱정돼요." + _postedAt0min0sec: + title: "정각" + description: "1초도 어긋나지 않은 정각에 노트를 포스트했습니다" + flavor: "째깍 째깍 째깍 땡!" + _selfQuote: + title: "혼잣말" + description: "자기 노트를 인용했습니다" + _htl20npm: + title: "타임라인 폭주 중" + description: "1분 사이에 홈 타임라인에 노트가 20개 넘게 생성되었습니다" + _driveFolderCircularReference: + title: "순환 참조" + description: "드라이브 폴더를 자신을 가리키도록 만드려 시도했습니다" + _reactWithoutRead: + title: "읽고 답하긴 하시는 건가요?" + description: "100자가 넘는 포스트에 3초 안에 포스트했습니다" + _clickedClickHere: + title: "여길 눌러보세요" + description: "이 곳을 눌러봤습니다" + _justPlainLucky: + title: "그냥 운이 좋았어" + description: "매 10초마다 0.01%의 확률로 달성됩니다" + _setNameToSyuilo: + title: "신 콤플렉스" + description: "이름을 syuilo로 설정했습니다" + _passedSinceAccountCreated1: + title: "1년" + description: "계정을 생성하고 1년이 지났습니다" + _passedSinceAccountCreated2: + title: "2년" + description: "계정을 생성하고 2년이 지났습니다" + _passedSinceAccountCreated3: + title: "3년" + description: "계정을 생성하고 3년이 지났습니다" + _loggedInOnBirthday: + title: "생일 축하합니다!" + description: "설정한 생일에 로그인했습니다" + _cookieClicked: + title: "쿠키 클리커 게임" + description: "쿠키를 클릭했습니다" + flavor: "뭔가 문제가 있나요?" + _brainDiver: + title: "Brain Diver" + description: "Brain Diver로의 링크를 첨부했습니다" + flavor: "Misskey-Misskey La-Tu-Ma" _role: new: "새 역할 생성" edit: "역할 수정" @@ -1586,6 +1802,7 @@ _notification: pollEnded: "투표 결과가 발표되었습니다" unreadAntennaNote: "안테나 {name}" emptyPushNotificationMessage: "푸시 알림이 갱신되었습니다" + achievementEarned: "도전 과제를 달성했습니다" _types: all: "전부" follow: "팔로잉" diff --git a/locales/nl-NL.yml b/locales/nl-NL.yml index 5735c6322a..e99d49710d 100644 --- a/locales/nl-NL.yml +++ b/locales/nl-NL.yml @@ -109,6 +109,7 @@ clickToShow: "Klik om te bekijken" sensitive: "NSFW" add: "Toevoegen" reaction: "Reacties" +reactions: "Reacties" reactionSetting: "Reacties die in de reactie-selector worden getoond" reactionSettingDescription2: "Sleep om opnieuw te ordenen, Klik om te verwijderen, Druk op \"+\" om toe te voegen" rememberNoteVisibility: "Vergeet niet de notitie zichtbaarheidsinstellingen" diff --git a/locales/pl-PL.yml b/locales/pl-PL.yml index 1bdfcd9675..d78185be82 100644 --- a/locales/pl-PL.yml +++ b/locales/pl-PL.yml @@ -110,6 +110,7 @@ clickToShow: "Kliknij, aby wyświetlić" sensitive: "NSFW" add: "Dodaj" reaction: "Reakcja" +reactions: "Reakcja" reactionSetting: "Reakcje do pokazania w wyborniku reakcji" reactionSettingDescription2: "Przeciągnij aby zmienić kolejność, naciśnij aby usunąć, naciśnij „+” aby dodać" rememberNoteVisibility: "Zapamiętuj ustawienia widoczności wpisu" diff --git a/locales/pt-PT.yml b/locales/pt-PT.yml index c8dc097236..8eac5fee64 100644 --- a/locales/pt-PT.yml +++ b/locales/pt-PT.yml @@ -107,6 +107,7 @@ clickToShow: "Clique para ver" sensitive: "Conteúdo sensível" add: "Adicionar" reaction: "Reações" +reactions: "Reações" reactionSetting: "Quais reações a mostrar no selecionador de reações" reactionSettingDescription2: "Arraste para reordenar, clique para excluir, pressione + para adicionar." rememberNoteVisibility: "Lembrar das configurações de visibilidade de notas" diff --git a/locales/ro-RO.yml b/locales/ro-RO.yml index 7c01aa725c..b1ec5426ad 100644 --- a/locales/ro-RO.yml +++ b/locales/ro-RO.yml @@ -107,6 +107,7 @@ clickToShow: "Click pentru a afișa" sensitive: "NSFW" add: "Adaugă" reaction: "Reacție" +reactions: "Reacție" reactionSetting: "Reacții care să apară in selectorul de reacții" reactionSettingDescription2: "Trage pentru a rearanja, apasă pe \"+\" pentru a adăuga." rememberNoteVisibility: "Amintește setarea de vizibilitate a notelor" diff --git a/locales/ru-RU.yml b/locales/ru-RU.yml index 9d836f17ba..d7aca1c9fc 100644 --- a/locales/ru-RU.yml +++ b/locales/ru-RU.yml @@ -107,6 +107,7 @@ clickToShow: "Нажмите для просмотра" sensitive: "Содержимое не для всех" add: "Добавить" reaction: "Реакции" +reactions: "Реакции" reactionSetting: "Реакции, отображаемые в палитре" reactionSettingDescription2: "Расставляйте перетаскиванием, удаляйте нажатием, добавляйте кнопкой «+»." rememberNoteVisibility: "Запоминать видимость заметок" diff --git a/locales/sk-SK.yml b/locales/sk-SK.yml index 60945593b9..ee2ca11fa7 100644 --- a/locales/sk-SK.yml +++ b/locales/sk-SK.yml @@ -110,6 +110,7 @@ clickToShow: "Kliknutím zobrazíte" sensitive: "NSFW" add: "Pridať" reaction: "Reakcie" +reactions: "Reakcie" reactionSetting: "Reakcie zobrazené vo výbere reakcií" reactionSettingDescription2: "Ťahaním preusporiadate, kliknutím odstránite, Stlačením \"+\" pridáte" rememberNoteVisibility: "Zapamätať nastavenia viditeľnosti poznámky" diff --git a/locales/sv-SE.yml b/locales/sv-SE.yml index b2647c9689..1abd0d194d 100644 --- a/locales/sv-SE.yml +++ b/locales/sv-SE.yml @@ -110,6 +110,7 @@ clickToShow: "Klicka för att visa" sensitive: "Känsligt innehåll" add: "Lägg till" reaction: "Reaktioner" +reactions: "Reaktioner" reactionSetting: "Reaktioner som ska visas i reaktionsväljaren" reactionSettingDescription2: "Dra för att omordna, klicka för att radera, tryck \"+\" för att lägga till." rememberNoteVisibility: "Komihåg notvisningsinställningar" diff --git a/locales/th-TH.yml b/locales/th-TH.yml index b77bc55b20..08737fb1b7 100644 --- a/locales/th-TH.yml +++ b/locales/th-TH.yml @@ -110,6 +110,7 @@ clickToShow: "คลิกเพื่อแสดง" sensitive: "เนื้อหาที่ละเอียดอ่อน NSFW" add: "เพิ่ม" reaction: "รีแอคชั่น" +reactions: "รีแอคชั่น" reactionSetting: "รีแอคชั่นไปยังแสดงผลในตัวเลือกการรีแอคชั่น" reactionSettingDescription2: "กดลากเพื่อจัดลำดับใหม่ กดคลิกเพื่อลบ กด \"+\" เพื่อเพิ่ม" rememberNoteVisibility: "จดจำการตั้งค่าการมองเห็นตัวโน้ต" @@ -932,6 +933,23 @@ assign: "กำหนด" unassign: "ยังไม่มอบหมาย" color: "สี" manageCustomEmojis: "จัดการอีโมจิแบบกำหนดเอง" +youCannotCreateAnymore: "คุณถึงขีดจํากัดการสร้างแล้วนะ" +cannotPerformTemporary: "ไม่สามารถใช้การได้ชั่วคราว" +cannotPerformTemporaryDescription: "การดําเนินการนี้ไม่สามารถดําเนินการได้ชั่วคราว เนื่องจากเกินขีดจํากัดการดําเนินการ กรุณารอสักครู่แล้วลองใหม่อีกครั้งนะค่ะ" +preset: "พรีเซ็ต" +selectFromPresets: "เลือกจากการพรีเซ็ต" +achievements: "ความสำเร็จ" +_achievements: + earnedAt: "ได้รับเมื่อ" + _types: + _followers100: + title: "บุคคลที่เป็นที่นิยม" + _followers500: + title: "เสาสัญญาณ" + _iLoveMisskey: + title: "ฉันรัก Misskey" + _driveFolderCircularReference: + title: "อ้างอิงวงจร" _role: new: "บทบาทใหม่" edit: "แก้ไขบทบาท" @@ -948,6 +966,7 @@ _role: isPublic: "บทบาทสาธารณะ" descriptionOfIsPublic: "ทุกคนสามารถดูได้ว่าผู้ใช้งานนั้นได้รับมอบหมายบทบาทด้วยหรือไม่ \n\nบทบาทจะแสดงในโปรไฟล์ของผู้ใช้ด้วย" options: "ตัวเลือกบทบาท" + policies: "นโยบาย" baseRole: "บทบาทพื้นฐาน" useBaseValue: "ใช้บทบาทพื้นฐานเริ่มต้น" chooseRoleToAssign: "เลือกบทบาทที่ต้องการกำหนด" @@ -965,7 +984,17 @@ _role: canInvite: "สร้างรหัสเชิญอินสแตนซ์" canManageCustomEmojis: "จัดการอีโมจิแบบกำหนดเอง" driveCapacity: "ความจุของไดรฟ์" + pinMax: "จํานวนสูงสุดของโน้ตที่ปักหมุดไว้" antennaMax: "จำนวนสูงสุดของเสาอากาศ" + wordMuteMax: "จำนวนอักขระสูงสุดที่อนุญาตในการปิดเสียงคำ" + webhookMax: "จำนวนเว็บฮุคสูงสุด" + clipMax: "จำนวนคลิปสูงสุด" + noteEachClipsMax: "จำนวนโน้ตสูงสุดภายในคลิป" + userListMax: "จำนวนรายชื่อผู้ใช้สูงสุด" + userEachUserListsMax: "จำนวนผู้ใช้สูงสุดภายในรายการผู้ใช้" + rateLimitFactor: "ขีดจำกัดอัตรา" + descriptionOfRateLimitFactor: "ขีดจํากัดอัตราที่ต่ำกว่ามีข้อจํากัดน้อยกว่าข้อจํากัดที่สูงกว่า" + canHideAds: "ซ่อนโฆษณา" _condition: isLocal: "ผู้ใช้ภายใน" isRemote: "ผู้ใช้ระยะไกล" @@ -1570,6 +1599,7 @@ _notification: pollEnded: "โพลสำรวจความคิดเห็นผลลัพธ์มีพร้อมใช้งาน" unreadAntennaNote: "เสาอากาศ {name}" emptyPushNotificationMessage: "การแจ้งเตือนแบบพุชได้รับการอัพเดทแล้ว" + achievementEarned: "รับความสำเร็จ" _types: all: "ทั้งหมด" follow: "กำลังติดตาม" diff --git a/locales/uk-UA.yml b/locales/uk-UA.yml index 72397fb068..6ca8d7059d 100644 --- a/locales/uk-UA.yml +++ b/locales/uk-UA.yml @@ -109,6 +109,7 @@ clickToShow: "Натисніть для перегляду" sensitive: "NSFW" add: "Додати" reaction: "Реакції" +reactions: "Реакції" reactionSetting: "Налаштування реакцій" reactionSettingDescription2: "Перемістити щоб змінити порядок, Клацнути мишою щоб видалити, Натиснути \"+\" щоб додати." rememberNoteVisibility: "Пам’ятати параметри видимісті" diff --git a/locales/vi-VN.yml b/locales/vi-VN.yml index 92931de6f2..b460b5e837 100644 --- a/locales/vi-VN.yml +++ b/locales/vi-VN.yml @@ -107,6 +107,7 @@ clickToShow: "Nhấn để xem" sensitive: "Nhạy cảm" add: "Thêm" reaction: "Biểu cảm" +reactions: "Biểu cảm" reactionSetting: "Chọn những biểu cảm hiển thị" reactionSettingDescription2: "Kéo để sắp xếp, nhấn để xóa, nhấn \"+\" để thêm." rememberNoteVisibility: "Lưu kiểu tút mặc định" diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index d026422ca7..817fc69462 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -110,6 +110,7 @@ clickToShow: "点击以显示" sensitive: "敏感内容" add: "添加" reaction: "回应" +reactions: "回应" reactionSetting: "在选择器中显示的回应" reactionSettingDescription2: "拖动重新排序,单击删除,点击 + 添加。" rememberNoteVisibility: "保存上次设置的可见性" diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml index 2a2cdfbc72..025a4c3e5d 100644 --- a/locales/zh-TW.yml +++ b/locales/zh-TW.yml @@ -110,6 +110,7 @@ clickToShow: "按一下以顯示" sensitive: "敏感內容" add: "新增" reaction: "情感" +reactions: "情感" reactionSetting: "在選擇器中顯示反應" reactionSettingDescription2: "拖動以重新列序,點擊以刪除,按下 + 添加。" rememberNoteVisibility: "記住貼文可見性" @@ -937,6 +938,62 @@ cannotPerformTemporary: "暫時無法進行" cannotPerformTemporaryDescription: "由於超過操作次數限制,暫時無法進行。請過一段時間之後再嘗試。" preset: "預設值" selectFromPresets: "從預設值中選擇" +achievements: "成就" +_achievements: + earnedAt: "獲得日期" + _types: + _notes1: + title: "just setting up my msky" + description: "發出了第一則貼文" + flavor: "祝您的Misskey生活愉快!" + _notes10: + title: "若干貼文" + description: "發表了10則貼文" + _notes100: + title: "許多的貼文" + description: "發表了100則貼文" + _notes500: + title: "滿滿的貼文" + description: "發表了500則貼文" + _notes1000: + title: "一堆貼文" + description: "發表了1000則貼文" + _notes5000: + title: "滔滔不絕的貼文" + description: "發表了5000則貼文" + _notes10000: + title: "超級貼文" + description: "發表了10000則貼文" + _notes20000: + title: "需要更多的貼文" + description: "發表了20000則貼文" + _notes30000: + title: "貼文貼文貼文" + description: "發表了30000則貼文" + _notes40000: + title: "貼文工廠" + description: "發表了40000則貼文" + _notes50000: + title: "貼文星球" + description: "發表了50000則貼文" + _notes60000: + title: "貼文類星體" + description: "發表了60000則貼文" + _notes70000: + title: "貼文黑洞" + description: "發表了70000則貼文" + _notes80000: + title: "貼文銀河" + description: "發表了80000則貼文" + _notes90000: + title: "貼文宇宙" + description: "發表了90000則貼文" + _notes100000: + description: "發表了100,000則貼文" + flavor: "有這麼多東西要寫嗎?" + _login3: + title: "初學者 I" + description: "總登入天數為3天" _role: new: "建立角色" edit: "編輯角色" From 69bb377cb1ae2315846eddaa5475cd25122bafb3 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 21 Jan 2023 17:02:44 +0900 Subject: [PATCH 38/70] 13.1.0-beta.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index eafcab0307..9ff6cf1254 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "13.0.0", + "version": "13.1.0-beta.2", "codename": "nasubi", "repository": { "type": "git", From bd3d75df6bab819bc83fd2c2c52d4e10fa58f9fe Mon Sep 17 00:00:00 2001 From: CyberRex <hspwinx86@gmail.com> Date: Sat, 21 Jan 2023 18:41:30 +0900 Subject: [PATCH 39/70] Update chokidar and use pnpm exec (#9640) * Fix for dev mode * Update packages/frontend/package.json Co-authored-by: tamaina <tamaina@hotmail.co.jp> Co-authored-by: tamaina <tamaina@hotmail.co.jp> --- package.json | 2 +- pnpm-lock.yaml | 4 ++-- scripts/dev.js | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 9ff6cf1254..3a8b8471bd 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "cleanall": "pnpm clean-all" }, "resolutions": { - "chokidar": "^3.3.1", + "chokidar": "^3.5.3", "lodash": "^4.17.21" }, "dependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 993efd1746..e0ffafd129 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,7 +1,7 @@ lockfileVersion: 5.4 overrides: - chokidar: ^3.3.1 + chokidar: ^3.5.3 lodash: ^4.17.21 importers: @@ -126,7 +126,7 @@ importers: cbor: 8.1.0 chalk: 5.2.0 chalk-template: 0.4.0 - chokidar: ^3.3.1 + chokidar: ^3.5.3 cli-highlight: 2.1.11 color-convert: 2.0.1 content-disposition: 0.5.4 diff --git a/scripts/dev.js b/scripts/dev.js index 943190f8b4..b1970ebc54 100644 --- a/scripts/dev.js +++ b/scripts/dev.js @@ -14,7 +14,7 @@ const fs = require('fs'); stderr: process.stderr, }); - execa('pnpm', ['dlx', 'gulp', 'watch'], { + execa('pnpm', ['exec', 'gulp', 'watch'], { cwd: __dirname + '/../', stdout: process.stdout, stderr: process.stderr, From 4dc00ee72aba7d725d8e78fe9b4cccb046eeb999 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 21 Jan 2023 18:41:53 +0900 Subject: [PATCH 40/70] New translations ja-JP.yml (English) (#9673) --- locales/en-US.yml | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/locales/en-US.yml b/locales/en-US.yml index 964c79caaf..b9f1603d26 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -938,6 +938,39 @@ cannotPerformTemporary: "Temporarily unavailable" cannotPerformTemporaryDescription: "This action cannot be performed temporarily due to exceeding the execution limit. Please wait for a while and then try again." preset: "Preset" selectFromPresets: "Choose from presets" +_achievements: + _types: + _reactWithoutRead: + title: "Did you really read that?" + description: "React on a note that's over 100 characters long within 3 seconds of it being posted" + _clickedClickHere: + title: "Click here" + description: "You've clicked here" + _justPlainLucky: + title: "Just Plain Lucky" + description: "Has a chance to be obtained with a probability of 0.01% every 10 seconds" + _setNameToSyuilo: + title: "God Complex" + description: "Set your name to \"syuilo\"" + _passedSinceAccountCreated1: + title: "One Year Anniversary" + description: "One year has passed since your account was created" + _passedSinceAccountCreated2: + title: "Two Year Anniversary" + description: "Two years have passed since your account was created" + _passedSinceAccountCreated3: + title: "Three Year Anniversary" + description: "Three years have passed since your account was created" + _loggedInOnBirthday: + title: "Happy Birthday" + description: "Logged in on your birthday" + _cookieClicked: + title: "A game in which you click cookies" + description: "Clicked the cookie" + _brainDiver: + title: "Brain Diver" + description: "Post the link to Brain Diver" + flavor: "Misskey-Misskey La-Tu-Ma" _role: new: "New role" edit: "Edit role" @@ -1587,6 +1620,7 @@ _notification: pollEnded: "Poll results have become available" unreadAntennaNote: "Antenna {name}" emptyPushNotificationMessage: "Push notifications have been updated" + achievementEarned: "Achievement unlocked" _types: all: "All" follow: "New followers" From dc4fd3e505ab0ebd199ee38ffec930e0aac7c7bc Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 21 Jan 2023 18:45:41 +0900 Subject: [PATCH 41/70] 13.1.0 --- CHANGELOG.md | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 85246595ca..ca8379bcfd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ You should also include the user name that made the change. --> -## 13.x.x (unreleased) +## 13.1.0 (2023/01/21) ### Improvements - 実績機能 diff --git a/package.json b/package.json index 3a8b8471bd..25a74d4040 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "13.1.0-beta.2", + "version": "13.1.0", "codename": "nasubi", "repository": { "type": "git", From bd469420fa6554db0caff17406f356761dbe1f00 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 21 Jan 2023 20:24:15 +0900 Subject: [PATCH 42/70] =?UTF-8?q?fix(client):=20=E3=82=AF=E3=83=A9?= =?UTF-8?q?=E3=82=A4=E3=82=A2=E3=83=B3=E3=83=88=E8=B5=B7=E5=8B=95=E6=99=82?= =?UTF-8?q?=E3=81=AB=E3=82=82=E8=A8=80=E8=AA=9E=E3=83=95=E3=82=A1=E3=82=A4?= =?UTF-8?q?=E3=83=AB=E3=81=AE=E3=83=81=E3=82=A7=E3=83=83=E3=82=AF=E3=82=92?= =?UTF-8?q?=E8=A1=8C=E3=81=86=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix #9005 --- packages/frontend/src/init.ts | 13 +++++++++++++ packages/frontend/src/local-storage.ts | 1 + 2 files changed, 14 insertions(+) diff --git a/packages/frontend/src/init.ts b/packages/frontend/src/init.ts index 09fb7caf14..d90d3b5532 100644 --- a/packages/frontend/src/init.ts +++ b/packages/frontend/src/init.ts @@ -80,6 +80,19 @@ import { claimAchievement, claimedAchievements } from './scripts/achievements'; }); } + //#region Detect language & fetch translations + const localeVersion = miLocalStorage.getItem('localeVersion'); + const localeOutdated = (localeVersion == null || localeVersion !== version); + if (localeOutdated) { + const res = await window.fetch(`/assets/locales/${lang}.${version}.json`); + if (res.status === 200) { + miLocalStorage.setItem('locale', await res.text()); + miLocalStorage.setItem('localeVersion', version); + location.reload(); + } + } + //#endregion + // タッチデバイスでCSSの:hoverを機能させる document.addEventListener('touchend', () => {}, { passive: true }); diff --git a/packages/frontend/src/local-storage.ts b/packages/frontend/src/local-storage.ts index bb8192e980..68dc9ebe41 100644 --- a/packages/frontend/src/local-storage.ts +++ b/packages/frontend/src/local-storage.ts @@ -19,6 +19,7 @@ type Keys = 'fontSize' | 'ui' | 'locale' | + 'localeVersion' | 'theme' | 'customCss' | 'message_drafts' | From 3e112da486e59d48c415a5bd3a251148ed7312b3 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 21 Jan 2023 20:40:09 +0900 Subject: [PATCH 43/70] =?UTF-8?q?=E3=83=AD=E3=83=BC=E3=82=AB=E3=83=AB?= =?UTF-8?q?=E3=81=AE=E3=82=AB=E3=82=B9=E3=82=BF=E3=83=A0=E7=B5=B5=E6=96=87?= =?UTF-8?q?=E5=AD=97=E3=81=AB=E3=81=A4=E3=81=84=E3=81=A6=E3=81=AF=E7=9B=B4?= =?UTF-8?q?=E6=8E=A5=E3=82=AA=E3=83=AA=E3=82=B8=E3=83=8A=E3=83=ABURL?= =?UTF-8?q?=E3=81=AB=E3=83=AA=E3=82=AF=E3=82=A8=E3=82=B9=E3=83=88=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 --- packages/backend/src/core/entities/EmojiEntityService.ts | 6 ++++-- packages/backend/src/models/schema/emoji.ts | 4 ++++ packages/backend/src/server/api/endpoints/emojis.ts | 1 + packages/frontend/src/components/global/MkEmoji.vue | 6 +++++- packages/frontend/src/custom-emojis.ts | 9 +++++++-- 5 files changed, 21 insertions(+), 5 deletions(-) diff --git a/packages/backend/src/core/entities/EmojiEntityService.ts b/packages/backend/src/core/entities/EmojiEntityService.ts index 2a4e09519f..cee85a5688 100644 --- a/packages/backend/src/core/entities/EmojiEntityService.ts +++ b/packages/backend/src/core/entities/EmojiEntityService.ts @@ -22,7 +22,7 @@ export class EmojiEntityService { @bindThis public async pack( src: Emoji['id'] | Emoji, - opts: { omitHost?: boolean; omitId?: boolean; } = {}, + opts: { omitHost?: boolean; omitId?: boolean; withUrl?: boolean; } = {}, ): Promise<Packed<'Emoji'>> { const emoji = typeof src === 'object' ? src : await this.emojisRepository.findOneByOrFail({ id: src }); @@ -32,13 +32,15 @@ export class EmojiEntityService { name: emoji.name, category: emoji.category, host: opts.omitHost ? undefined : emoji.host, + // ?? emoji.originalUrl してるのは後方互換性のため + url: opts.withUrl ? (emoji.publicUrl ?? emoji.originalUrl) : undefined, }; } @bindThis public packMany( emojis: any[], - opts: { omitHost?: boolean; omitId?: boolean; } = {}, + opts: { omitHost?: boolean; omitId?: boolean; withUrl?: boolean; } = {}, ) { return Promise.all(emojis.map(x => this.pack(x, opts))); } diff --git a/packages/backend/src/models/schema/emoji.ts b/packages/backend/src/models/schema/emoji.ts index d897a0fc05..143f25373c 100644 --- a/packages/backend/src/models/schema/emoji.ts +++ b/packages/backend/src/models/schema/emoji.ts @@ -29,5 +29,9 @@ export const packedEmojiSchema = { optional: true, nullable: true, description: 'The local host is represented with `null`.', }, + url: { + type: 'string', + optional: true, nullable: false, + }, }, } as const; diff --git a/packages/backend/src/server/api/endpoints/emojis.ts b/packages/backend/src/server/api/endpoints/emojis.ts index 97dcfde596..db1eddc80a 100644 --- a/packages/backend/src/server/api/endpoints/emojis.ts +++ b/packages/backend/src/server/api/endpoints/emojis.ts @@ -83,6 +83,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { emojis: await this.emojiEntityService.packMany(emojis, { omitId: true, omitHost: true, + withUrl: true, }), }; }); diff --git a/packages/frontend/src/components/global/MkEmoji.vue b/packages/frontend/src/components/global/MkEmoji.vue index b7dd0296cd..aaad81c656 100644 --- a/packages/frontend/src/components/global/MkEmoji.vue +++ b/packages/frontend/src/components/global/MkEmoji.vue @@ -12,6 +12,7 @@ import { getStaticImageUrl } from '@/scripts/media-proxy'; import { char2twemojiFilePath, char2fluentEmojiFilePath } from '@/scripts/emoji-base'; import { defaultStore } from '@/store'; import { getEmojiName } from '@/scripts/emojilist'; +import { customEmojis } from '@/custom-emojis'; const props = defineProps<{ emoji: string; @@ -30,6 +31,9 @@ const useOsNativeEmojis = computed(() => defaultStore.state.emojiStyle === 'nati const url = computed(() => { if (char.value) { return char2path(char.value); + } else if (props.host == null) { + const found = customEmojis.find(x => x.name === customEmojiName); + return found ? found.url : null; } else { const rawUrl = props.host ? `/emoji/${customEmojiName}@${props.host}.webp` : `/emoji/${customEmojiName}.webp`; return defaultStore.state.disableShowingAnimatedImages @@ -38,7 +42,7 @@ const url = computed(() => { } }); const alt = computed(() => isCustom.value ? `:${customEmojiName}:` : char.value); -let errored = $ref(false); +let errored = $ref(isCustom.value && url.value == null); // Searching from an array with 2000 items for every emoji felt like too energy-consuming, so I decided to do it lazily on pointerenter function computeTitle(event: PointerEvent): void { diff --git a/packages/frontend/src/custom-emojis.ts b/packages/frontend/src/custom-emojis.ts index 19469999b6..637ee9c06e 100644 --- a/packages/frontend/src/custom-emojis.ts +++ b/packages/frontend/src/custom-emojis.ts @@ -2,14 +2,19 @@ import { api } from './os'; import { miLocalStorage } from './local-storage'; const storageCache = miLocalStorage.getItem('emojis'); -export let customEmojis = storageCache ? JSON.parse(storageCache) : []; +export let customEmojis: { + name: string; + aliases: string[]; + category: string; + url: string; +}[] = storageCache ? JSON.parse(storageCache) : []; fetchCustomEmojis(); export async function fetchCustomEmojis() { const now = Date.now(); const lastFetchedAt = miLocalStorage.getItem('lastEmojisFetchedAt'); - if (lastFetchedAt && (now - parseInt(lastFetchedAt)) < 1000 * 60 * 60) return; + if (lastFetchedAt && (now - parseInt(lastFetchedAt)) < 1000 * 60 * 60 * 24) return; const res = await api('emojis', {}); From 307a882649691f2d816e8178a6df4a142f4932af Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sat, 21 Jan 2023 20:53:11 +0900 Subject: [PATCH 44/70] tweak fetchCustomEmojis timing --- packages/frontend/src/custom-emojis.ts | 2 -- packages/frontend/src/init.ts | 5 +++++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/frontend/src/custom-emojis.ts b/packages/frontend/src/custom-emojis.ts index 637ee9c06e..a7ac4e03ca 100644 --- a/packages/frontend/src/custom-emojis.ts +++ b/packages/frontend/src/custom-emojis.ts @@ -9,8 +9,6 @@ export let customEmojis: { url: string; }[] = storageCache ? JSON.parse(storageCache) : []; -fetchCustomEmojis(); - export async function fetchCustomEmojis() { const now = Date.now(); const lastFetchedAt = miLocalStorage.getItem('lastEmojisFetchedAt'); diff --git a/packages/frontend/src/init.ts b/packages/frontend/src/init.ts index d90d3b5532..079003ee83 100644 --- a/packages/frontend/src/init.ts +++ b/packages/frontend/src/init.ts @@ -45,6 +45,7 @@ import { getUrlWithoutLoginId } from '@/scripts/login-id'; import { getAccountFromId } from '@/scripts/get-account-from-id'; import { miLocalStorage } from './local-storage'; import { claimAchievement, claimedAchievements } from './scripts/achievements'; +import { fetchCustomEmojis } from './custom-emojis'; (async () => { console.info(`Misskey v${version}`); @@ -178,6 +179,10 @@ import { claimAchievement, claimedAchievements } from './scripts/achievements'; initializeSw(); }); + try { + await fetchCustomEmojis(); + } catch (err) {} + const app = createApp( window.location.search === '?zen' ? defineAsyncComponent(() => import('@/ui/zen.vue')) : !$i ? defineAsyncComponent(() => import('@/ui/visitor.vue')) : From a3aafa03ada6cd3fa62cff68929edcc6c23ee664 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 22 Jan 2023 04:17:58 +0900 Subject: [PATCH 45/70] update deps --- package.json | 6 +- packages/backend/package.json | 20 +- packages/frontend/package.json | 20 +- pnpm-lock.yaml | 516 ++++++++++++++++++++++----------- 4 files changed, 375 insertions(+), 187 deletions(-) diff --git a/package.json b/package.json index 25a74d4040..d30ea8eea3 100644 --- a/package.json +++ b/package.json @@ -54,11 +54,11 @@ "devDependencies": { "@types/gulp": "4.0.10", "@types/gulp-rename": "2.0.1", - "@typescript-eslint/eslint-plugin": "5.48.1", - "@typescript-eslint/parser": "5.48.1", + "@typescript-eslint/eslint-plugin": "5.48.2", + "@typescript-eslint/parser": "5.48.2", "cross-env": "7.0.3", "cypress": "12.3.0", - "eslint": "^8.31.0", + "eslint": "^8.32.0", "start-server-and-test": "1.15.2" }, "optionalDependencies": { diff --git a/packages/backend/package.json b/packages/backend/package.json index 68cfbb05ad..be9012021f 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -58,9 +58,9 @@ "date-fns": "2.29.3", "deep-email-validator": "0.1.21", "escape-regexp": "0.0.1", - "fastify": "4.11.0", + "fastify": "4.12.0", "feed": "4.2.2", - "file-type": "18.1.0", + "file-type": "18.2.0", "fluent-ffmpeg": "2.1.2", "form-data": "^4.0.0", "got": "12.5.3", @@ -89,7 +89,7 @@ "probe-image-size": "7.2.3", "promise-limit": "2.7.0", "pug": "3.0.2", - "punycode": "2.2.0", + "punycode": "2.3.0", "pureimage": "0.3.15", "qrcode": "1.5.1", "random-seed": "0.3.0", @@ -120,7 +120,7 @@ "typeorm": "0.3.11", "typescript": "4.9.4", "ulid": "2.3.0", - "undici": "^5.15.0", + "undici": "^5.15.1", "unzipper": "0.10.11", "uuid": "9.0.0", "vary": "1.1.2", @@ -132,7 +132,7 @@ "devDependencies": { "@redocly/openapi-core": "1.0.0-beta.120", "@swc/cli": "^0.1.59", - "@swc/core": "1.3.26", + "@swc/core": "1.3.27", "@swc/jest": "0.2.24", "@types/accepts": "1.3.5", "@types/archiver": "5.3.1", @@ -144,7 +144,7 @@ "@types/escape-regexp": "0.0.1", "@types/fluent-ffmpeg": "2.1.20", "@types/ioredis": "4.28.10", - "@types/jest": "29.2.5", + "@types/jest": "29.2.6", "@types/js-yaml": "4.0.5", "@types/jsdom": "20.0.1", "@types/jsonld": "1.5.8", @@ -176,11 +176,11 @@ "@types/web-push": "3.3.2", "@types/websocket": "1.0.5", "@types/ws": "8.5.4", - "@typescript-eslint/eslint-plugin": "5.48.1", - "@typescript-eslint/parser": "5.48.1", + "@typescript-eslint/eslint-plugin": "5.48.2", + "@typescript-eslint/parser": "5.48.2", "cross-env": "7.0.3", - "eslint": "8.31.0", - "eslint-plugin-import": "2.27.4", + "eslint": "8.32.0", + "eslint-plugin-import": "2.27.5", "execa": "6.1.0", "jest": "29.3.1", "jest-mock": "^29.3.1", diff --git a/packages/frontend/package.json b/packages/frontend/package.json index 730389a2e6..cdfa96ea82 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -8,7 +8,7 @@ }, "dependencies": { "@discordapp/twemoji": "14.0.2", - "@rollup/plugin-alias": "4.0.2", + "@rollup/plugin-alias": "4.0.3", "@rollup/plugin-json": "6.0.0", "@rollup/pluginutils": "5.0.2", "@syuilo/aiscript": "0.12.2", @@ -18,10 +18,10 @@ "autobind-decorator": "2.4.0", "autosize": "5.0.2", "blurhash": "2.0.4", - "broadcast-channel": "4.20.1", + "broadcast-channel": "4.20.2", "browser-image-resizer": "git+https://github.com/misskey-dev/browser-image-resizer#v2.2.1-misskey.3", "canvas-confetti": "^1.6.0", - "chart.js": "4.1.2", + "chart.js": "4.2.0", "chartjs-adapter-date-fns": "3.0.0", "chartjs-chart-matrix": "^1.3.0", "chartjs-plugin-gradient": "0.6.1", @@ -41,10 +41,10 @@ "misskey-js": "0.0.14", "photoswipe": "5.3.4", "prismjs": "1.29.0", - "punycode": "2.2.0", + "punycode": "2.3.0", "querystring": "0.2.1", "rndstr": "1.0.0", - "rollup": "3.10.0", + "rollup": "3.10.1", "s-age": "1.1.2", "sanitize-html": "^2.8.1", "sass": "1.57.1", @@ -69,7 +69,7 @@ }, "devDependencies": { "@types/escape-regexp": "0.0.1", - "@types/glob": "8.0.0", + "@types/glob": "8.0.1", "@types/gulp": "4.0.10", "@types/gulp-rename": "2.0.1", "@types/matter-js": "0.18.2", @@ -82,13 +82,13 @@ "@types/uuid": "9.0.0", "@types/websocket": "1.0.5", "@types/ws": "8.5.4", - "@typescript-eslint/eslint-plugin": "5.48.1", - "@typescript-eslint/parser": "5.48.1", + "@typescript-eslint/eslint-plugin": "5.48.2", + "@typescript-eslint/parser": "5.48.2", "@vue/runtime-core": "3.2.45", "cross-env": "7.0.3", "cypress": "12.3.0", - "eslint": "8.31.0", - "eslint-plugin-import": "2.27.4", + "eslint": "8.32.0", + "eslint-plugin-import": "2.27.5", "eslint-plugin-vue": "9.9.0", "start-server-and-test": "1.15.2", "vue-eslint-parser": "^9.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e0ffafd129..13b709bb24 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,11 +11,11 @@ importers: '@tensorflow/tfjs-core': ^4.2.0 '@types/gulp': 4.0.10 '@types/gulp-rename': 2.0.1 - '@typescript-eslint/eslint-plugin': 5.48.1 - '@typescript-eslint/parser': 5.48.1 + '@typescript-eslint/eslint-plugin': 5.48.2 + '@typescript-eslint/parser': 5.48.2 cross-env: 7.0.3 cypress: 12.3.0 - eslint: ^8.31.0 + eslint: ^8.32.0 execa: 5.1.1 gulp: 4.0.2 gulp-cssnano: 2.1.3 @@ -39,11 +39,11 @@ importers: devDependencies: '@types/gulp': 4.0.10 '@types/gulp-rename': 2.0.1 - '@typescript-eslint/eslint-plugin': 5.48.1_3jon24igvnqaqexgwtxk6nkpse - '@typescript-eslint/parser': 5.48.1_iukboom6ndih5an6iafl45j2fe + '@typescript-eslint/eslint-plugin': 5.48.2_caon6io6stgpr7lz2rtbhekxqy + '@typescript-eslint/parser': 5.48.2_7uibuqfxkfaozanbtbziikiqje cross-env: 7.0.3 cypress: 12.3.0 - eslint: 8.31.0 + eslint: 8.32.0 start-server-and-test: 1.15.2 packages/backend: @@ -66,7 +66,7 @@ importers: '@redocly/openapi-core': 1.0.0-beta.120 '@sinonjs/fake-timers': 10.0.2 '@swc/cli': ^0.1.59 - '@swc/core': 1.3.26 + '@swc/core': 1.3.27 '@swc/jest': 0.2.24 '@tensorflow/tfjs': ^4.1.0 '@tensorflow/tfjs-node': 4.1.0 @@ -80,7 +80,7 @@ importers: '@types/escape-regexp': 0.0.1 '@types/fluent-ffmpeg': 2.1.20 '@types/ioredis': 4.28.10 - '@types/jest': 29.2.5 + '@types/jest': 29.2.6 '@types/js-yaml': 4.0.5 '@types/jsdom': 20.0.1 '@types/jsonld': 1.5.8 @@ -112,8 +112,8 @@ importers: '@types/web-push': 3.3.2 '@types/websocket': 1.0.5 '@types/ws': 8.5.4 - '@typescript-eslint/eslint-plugin': 5.48.1 - '@typescript-eslint/parser': 5.48.1 + '@typescript-eslint/eslint-plugin': 5.48.2 + '@typescript-eslint/parser': 5.48.2 accepts: ^1.3.8 ajv: 8.12.0 archiver: 5.3.1 @@ -134,12 +134,12 @@ importers: date-fns: 2.29.3 deep-email-validator: 0.1.21 escape-regexp: 0.0.1 - eslint: 8.31.0 - eslint-plugin-import: 2.27.4 + eslint: 8.32.0 + eslint-plugin-import: 2.27.5 execa: 6.1.0 - fastify: 4.11.0 + fastify: 4.12.0 feed: 4.2.2 - file-type: 18.1.0 + file-type: 18.2.0 fluent-ffmpeg: 2.1.2 form-data: ^4.0.0 got: 12.5.3 @@ -171,7 +171,7 @@ importers: probe-image-size: 7.2.3 promise-limit: 2.7.0 pug: 3.0.2 - punycode: 2.2.0 + punycode: 2.3.0 pureimage: 0.3.15 qrcode: 1.5.1 random-seed: 0.3.0 @@ -202,7 +202,7 @@ importers: typeorm: 0.3.11 typescript: 4.9.4 ulid: 2.3.0 - undici: ^5.15.0 + undici: ^5.15.1 unzipper: 0.10.11 uuid: 9.0.0 vary: 1.1.2 @@ -246,9 +246,9 @@ importers: date-fns: 2.29.3 deep-email-validator: 0.1.21 escape-regexp: 0.0.1 - fastify: 4.11.0 + fastify: 4.12.0 feed: 4.2.2 - file-type: 18.1.0 + file-type: 18.2.0 fluent-ffmpeg: 2.1.2 form-data: 4.0.0 got: 12.5.3 @@ -277,7 +277,7 @@ importers: probe-image-size: 7.2.3 promise-limit: 2.7.0 pug: 3.0.2 - punycode: 2.2.0 + punycode: 2.3.0 pureimage: 0.3.15 qrcode: 1.5.1 random-seed: 0.3.0 @@ -308,7 +308,7 @@ importers: typeorm: 0.3.11_ioredis@4.28.5+pg@8.8.0 typescript: 4.9.4 ulid: 2.3.0 - undici: 5.15.0 + undici: 5.15.1 unzipper: 0.10.11 uuid: 9.0.0 vary: 1.1.2 @@ -321,9 +321,9 @@ importers: '@tensorflow/tfjs-node': 4.1.0_seedrandom@3.0.5 devDependencies: '@redocly/openapi-core': 1.0.0-beta.120 - '@swc/cli': 0.1.59_cr4os3zuq4gmhe2qzzjtw2pxeu - '@swc/core': 1.3.26 - '@swc/jest': 0.2.24_@swc+core@1.3.26 + '@swc/cli': 0.1.59_2w2rsb5d2wh3txrlxuiknf4vra + '@swc/core': 1.3.27 + '@swc/jest': 0.2.24_@swc+core@1.3.27 '@types/accepts': 1.3.5 '@types/archiver': 5.3.1 '@types/bcryptjs': 2.4.2 @@ -334,7 +334,7 @@ importers: '@types/escape-regexp': 0.0.1 '@types/fluent-ffmpeg': 2.1.20 '@types/ioredis': 4.28.10 - '@types/jest': 29.2.5 + '@types/jest': 29.2.6 '@types/js-yaml': 4.0.5 '@types/jsdom': 20.0.1 '@types/jsonld': 1.5.8 @@ -366,11 +366,11 @@ importers: '@types/web-push': 3.3.2 '@types/websocket': 1.0.5 '@types/ws': 8.5.4 - '@typescript-eslint/eslint-plugin': 5.48.1_3jon24igvnqaqexgwtxk6nkpse - '@typescript-eslint/parser': 5.48.1_iukboom6ndih5an6iafl45j2fe + '@typescript-eslint/eslint-plugin': 5.48.2_caon6io6stgpr7lz2rtbhekxqy + '@typescript-eslint/parser': 5.48.2_7uibuqfxkfaozanbtbziikiqje cross-env: 7.0.3 - eslint: 8.31.0 - eslint-plugin-import: 2.27.4_qdjeohovcytra7xto5vgmxssaq + eslint: 8.32.0 + eslint-plugin-import: 2.27.5_2l6piu6guil2f63lj3qmhzbnn4 execa: 6.1.0 jest: 29.3.1_@types+node@18.11.18 jest-mock: 29.3.1 @@ -379,13 +379,13 @@ importers: packages/frontend: specifiers: '@discordapp/twemoji': 14.0.2 - '@rollup/plugin-alias': 4.0.2 + '@rollup/plugin-alias': 4.0.3 '@rollup/plugin-json': 6.0.0 '@rollup/pluginutils': 5.0.2 '@syuilo/aiscript': 0.12.2 '@tabler/icons': ^1.118.0 '@types/escape-regexp': 0.0.1 - '@types/glob': 8.0.0 + '@types/glob': 8.0.1 '@types/gulp': 4.0.10 '@types/gulp-rename': 2.0.1 '@types/matter-js': 0.18.2 @@ -398,18 +398,18 @@ importers: '@types/uuid': 9.0.0 '@types/websocket': 1.0.5 '@types/ws': 8.5.4 - '@typescript-eslint/eslint-plugin': 5.48.1 - '@typescript-eslint/parser': 5.48.1 + '@typescript-eslint/eslint-plugin': 5.48.2 + '@typescript-eslint/parser': 5.48.2 '@vitejs/plugin-vue': 4.0.0 '@vue/compiler-sfc': 3.2.45 '@vue/runtime-core': 3.2.45 autobind-decorator: 2.4.0 autosize: 5.0.2 blurhash: 2.0.4 - broadcast-channel: 4.20.1 + broadcast-channel: 4.20.2 browser-image-resizer: git+https://github.com/misskey-dev/browser-image-resizer#v2.2.1-misskey.3 canvas-confetti: ^1.6.0 - chart.js: 4.1.2 + chart.js: 4.2.0 chartjs-adapter-date-fns: 3.0.0 chartjs-chart-matrix: ^1.3.0 chartjs-plugin-gradient: 0.6.1 @@ -420,8 +420,8 @@ importers: cypress: 12.3.0 date-fns: 2.29.3 escape-regexp: 0.0.1 - eslint: 8.31.0 - eslint-plugin-import: 2.27.4 + eslint: 8.32.0 + eslint-plugin-import: 2.27.5 eslint-plugin-vue: 9.9.0 eventemitter3: 5.0.0 gsap: ^3.11.4 @@ -434,10 +434,10 @@ importers: misskey-js: 0.0.14 photoswipe: 5.3.4 prismjs: 1.29.0 - punycode: 2.2.0 + punycode: 2.3.0 querystring: 0.2.1 rndstr: 1.0.0 - rollup: 3.10.0 + rollup: 3.10.1 s-age: 1.1.2 sanitize-html: ^2.8.1 sass: 1.57.1 @@ -464,9 +464,9 @@ importers: vuedraggable: next dependencies: '@discordapp/twemoji': 14.0.2 - '@rollup/plugin-alias': 4.0.2_rollup@3.10.0 - '@rollup/plugin-json': 6.0.0_rollup@3.10.0 - '@rollup/pluginutils': 5.0.2_rollup@3.10.0 + '@rollup/plugin-alias': 4.0.3_rollup@3.10.1 + '@rollup/plugin-json': 6.0.0_rollup@3.10.1 + '@rollup/pluginutils': 5.0.2_rollup@3.10.1 '@syuilo/aiscript': 0.12.2 '@tabler/icons': 1.119.0 '@vitejs/plugin-vue': 4.0.0_vite@4.0.4+vue@3.2.45 @@ -474,14 +474,14 @@ importers: autobind-decorator: 2.4.0 autosize: 5.0.2 blurhash: 2.0.4 - broadcast-channel: 4.20.1 + broadcast-channel: 4.20.2 browser-image-resizer: github.com/misskey-dev/browser-image-resizer/0227e860621e55cbed0aabe6dc601096a7748c4a canvas-confetti: 1.6.0 - chart.js: 4.1.2 - chartjs-adapter-date-fns: 3.0.0_kluv2ktejb2igagp3yc56zuwiy - chartjs-chart-matrix: 1.3.0_chart.js@4.1.2 - chartjs-plugin-gradient: 0.6.1_chart.js@4.1.2 - chartjs-plugin-zoom: 2.0.0_chart.js@4.1.2 + chart.js: 4.2.0 + chartjs-adapter-date-fns: 3.0.0_n6szoxj4ax2zhp2sxsxxj6zdla + chartjs-chart-matrix: 1.3.0_chart.js@4.2.0 + chartjs-plugin-gradient: 0.6.1_chart.js@4.2.0 + chartjs-plugin-zoom: 2.0.0_chart.js@4.2.0 compare-versions: 5.0.1 cropperjs: 2.0.0-beta.2 date-fns: 2.29.3 @@ -497,10 +497,10 @@ importers: misskey-js: 0.0.14 photoswipe: 5.3.4 prismjs: 1.29.0 - punycode: 2.2.0 + punycode: 2.3.0 querystring: 0.2.1 rndstr: 1.0.0 - rollup: 3.10.0 + rollup: 3.10.1 s-age: 1.1.2 sanitize-html: 2.8.1 sass: 1.57.1 @@ -524,7 +524,7 @@ importers: vuedraggable: 4.1.0_vue@3.2.45 devDependencies: '@types/escape-regexp': 0.0.1 - '@types/glob': 8.0.0 + '@types/glob': 8.0.1 '@types/gulp': 4.0.10 '@types/gulp-rename': 2.0.1 '@types/matter-js': 0.18.2 @@ -537,16 +537,16 @@ importers: '@types/uuid': 9.0.0 '@types/websocket': 1.0.5 '@types/ws': 8.5.4 - '@typescript-eslint/eslint-plugin': 5.48.1_3jon24igvnqaqexgwtxk6nkpse - '@typescript-eslint/parser': 5.48.1_iukboom6ndih5an6iafl45j2fe + '@typescript-eslint/eslint-plugin': 5.48.2_caon6io6stgpr7lz2rtbhekxqy + '@typescript-eslint/parser': 5.48.2_7uibuqfxkfaozanbtbziikiqje '@vue/runtime-core': 3.2.45 cross-env: 7.0.3 cypress: 12.3.0 - eslint: 8.31.0 - eslint-plugin-import: 2.27.4_qdjeohovcytra7xto5vgmxssaq - eslint-plugin-vue: 9.9.0_eslint@8.31.0 + eslint: 8.32.0 + eslint-plugin-import: 2.27.5_2l6piu6guil2f63lj3qmhzbnn4 + eslint-plugin-vue: 9.9.0_eslint@8.32.0 start-server-and-test: 1.15.2 - vue-eslint-parser: 9.1.0_eslint@8.31.0 + vue-eslint-parser: 9.1.0_eslint@8.32.0 vue-tsc: 1.0.24_typescript@4.9.4 packages/sw: @@ -1077,7 +1077,7 @@ packages: dependencies: ky: 0.30.0 ky-universal: 0.10.1_ky@0.30.0 - undici: 5.15.0 + undici: 5.15.1 transitivePeerDependencies: - web-streams-polyfill dev: false @@ -1402,7 +1402,7 @@ packages: fastify-plugin: 4.5.0 pump: 3.0.0 tiny-lru: 10.0.1 - undici: 5.15.0 + undici: 5.15.1 dev: false /@fastify/send/1.0.0: @@ -2059,8 +2059,8 @@ packages: - encoding dev: true - /@rollup/plugin-alias/4.0.2_rollup@3.10.0: - resolution: {integrity: sha512-1hv7dBOZZwo3SEupxn4UA2N0EDThqSSS+wI1St1TNTBtOZvUchyIClyHcnDcjjrReTPZ47Faedrhblv4n+T5UQ==} + /@rollup/plugin-alias/4.0.3_rollup@3.10.1: + resolution: {integrity: sha512-ZuDWE1q4PQDhvm/zc5Prun8sBpLJy41DMptYrS6MhAy9s9kL/doN1613BWfEchGVfKxzliJ3BjbOPizXX38DbQ==} engines: {node: '>=14.0.0'} peerDependencies: rollup: ^1.20.0||^2.0.0||^3.0.0 @@ -2068,11 +2068,11 @@ packages: rollup: optional: true dependencies: - rollup: 3.10.0 + rollup: 3.10.1 slash: 4.0.0 dev: false - /@rollup/plugin-json/6.0.0_rollup@3.10.0: + /@rollup/plugin-json/6.0.0_rollup@3.10.1: resolution: {integrity: sha512-i/4C5Jrdr1XUarRhVu27EEwjt4GObltD7c+MkCIpO2QIbojw8MUs+CCTqOphQi3Qtg1FLmYt+l+6YeoIf51J7w==} engines: {node: '>=14.0.0'} peerDependencies: @@ -2081,11 +2081,11 @@ packages: rollup: optional: true dependencies: - '@rollup/pluginutils': 5.0.2_rollup@3.10.0 - rollup: 3.10.0 + '@rollup/pluginutils': 5.0.2_rollup@3.10.1 + rollup: 3.10.1 dev: false - /@rollup/pluginutils/5.0.2_rollup@3.10.0: + /@rollup/pluginutils/5.0.2_rollup@3.10.1: resolution: {integrity: sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==} engines: {node: '>=14.0.0'} peerDependencies: @@ -2097,7 +2097,7 @@ packages: '@types/estree': 1.0.0 estree-walker: 2.0.2 picomatch: 2.3.1 - rollup: 3.10.0 + rollup: 3.10.1 dev: false /@sideway/address/4.1.4: @@ -2161,7 +2161,7 @@ packages: resolution: {integrity: sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==} dev: false - /@swc/cli/0.1.59_cr4os3zuq4gmhe2qzzjtw2pxeu: + /@swc/cli/0.1.59_2w2rsb5d2wh3txrlxuiknf4vra: resolution: {integrity: sha512-BlX3wIxYTwdtR22dIqZ3FEIOJPqnlByAp4JY46OMZi2UXMB3ZbOzefawD2ZlLafRUWyy5NtiZZty5waKzaYRnA==} engines: {node: '>= 12.13'} hasBin: true @@ -2172,7 +2172,7 @@ packages: chokidar: optional: true dependencies: - '@swc/core': 1.3.26 + '@swc/core': 1.3.27 bin-wrapper: 4.1.0 chokidar: 3.5.3 commander: 7.2.0 @@ -2182,8 +2182,8 @@ packages: source-map: 0.7.4 dev: true - /@swc/core-darwin-arm64/1.3.26: - resolution: {integrity: sha512-FWWflBfKRYrUJtko2xiedC5XCa31O75IZZqnTWuLpe9g3C5tnUuF3M8LSXZS/dn6wprome1MhtG9GMPkSYkhkg==} + /@swc/core-darwin-arm64/1.3.27: + resolution: {integrity: sha512-IKlxkhEy99CnP9nduaf5IJWIFcr6D5cZCjYmCs7nWkjMV+aAieyDO9AX4LT8AcHy6CF7ByOX7SKoqk+gVMAaKw==} engines: {node: '>=10'} cpu: [arm64] os: [darwin] @@ -2191,8 +2191,8 @@ packages: dev: true optional: true - /@swc/core-darwin-x64/1.3.26: - resolution: {integrity: sha512-0uQeebAtsewqJ2b35aPZstGrylwd6oJjUyAJOfVJNbremFSJ5JzytB3NoDCIw7CT5UQrSRpvD3mU95gfdQjDGA==} + /@swc/core-darwin-x64/1.3.27: + resolution: {integrity: sha512-MtabZIhFf/dL3vs6UMbd+vJsjIkm2NaFqulGV0Jofy2bfVZPTj/b5pXeOlUsTWy7JcH1uixjdx4RvJRyvqJxQA==} engines: {node: '>=10'} cpu: [x64] os: [darwin] @@ -2200,8 +2200,8 @@ packages: dev: true optional: true - /@swc/core-linux-arm-gnueabihf/1.3.26: - resolution: {integrity: sha512-06T+LbVFlyciQtwrUB5/a16A1ju1jFoYvd/hq9TWhf7GrtL43U7oJIgqMOPHx2j0+Ps2R3S6R/UUN5YXu618zA==} + /@swc/core-linux-arm-gnueabihf/1.3.27: + resolution: {integrity: sha512-XELMoGcUTAkk+G4buwIIhu6AIr1U418Odt22HUW8+ZvV+Wty2ICgR/myOIhM3xMb6U2L8ay+evMqoVNMQ0RRTg==} engines: {node: '>=10'} cpu: [arm] os: [linux] @@ -2209,8 +2209,8 @@ packages: dev: true optional: true - /@swc/core-linux-arm64-gnu/1.3.26: - resolution: {integrity: sha512-2NT/0xALPfK+U01qIlHxjkGdIj6F0txhu1U2v6B0YP2+k0whL2gCgYeg9QUvkYEXSD5r1Yx+vcb2R/vaSCSClg==} + /@swc/core-linux-arm64-gnu/1.3.27: + resolution: {integrity: sha512-O6vtT6bnrVR9PzEIuA5U7tIfYo7bv97H9K9Vqy2oyHNeGN0H36DKwS4UqPreHtziXNF5+7ubdUYUkrG/j8UnUQ==} engines: {node: '>=10'} cpu: [arm64] os: [linux] @@ -2218,8 +2218,8 @@ packages: dev: true optional: true - /@swc/core-linux-arm64-musl/1.3.26: - resolution: {integrity: sha512-64KrTay9hC0mTvZ1AmEFmNEwV5QDjw9U7PJU5riotSc28I+Q/ZoM0qcSFW9JRRa6F2Tr+IfMtyv8+eB2//BQ5g==} + /@swc/core-linux-arm64-musl/1.3.27: + resolution: {integrity: sha512-Oa0E1i7dOTWpaEZumKoNbTE/Ap+da6nlhqKVUdYrFDrOBi25tz76SdxZIyvAszzmgY89b5yd1naourKmkPXpww==} engines: {node: '>=10'} cpu: [arm64] os: [linux] @@ -2227,8 +2227,8 @@ packages: dev: true optional: true - /@swc/core-linux-x64-gnu/1.3.26: - resolution: {integrity: sha512-Te8G13l3dcRM1Mf3J4JzGUngzNXLKnMYlUmBOYN/ORsx7e+VNelR3zsTLHC0+0jGqELDgqvMyzDfk+dux/C/bQ==} + /@swc/core-linux-x64-gnu/1.3.27: + resolution: {integrity: sha512-S3v9H8oL2a8Ur6AjQyhkC6HfBVPOxKMdBhcZmdNuVgEUHbHdbf/Lka85F9IOYXEarMn0FtQw3ywowS22O9L5Uw==} engines: {node: '>=10'} cpu: [x64] os: [linux] @@ -2236,8 +2236,8 @@ packages: dev: true optional: true - /@swc/core-linux-x64-musl/1.3.26: - resolution: {integrity: sha512-nqQWuSM6OTKepUiQ9+rXgERq/JiO72RBOpXKO2afYppsL96sngjIRewV74v5f6IAfyzw+k+AhC5pgRA4Xu/Jkg==} + /@swc/core-linux-x64-musl/1.3.27: + resolution: {integrity: sha512-6DDkdXlOADpwICFZTRphCR+cIeS8aEYh4NlyzBito0mOWwIIdfCgALzhkTQOzTOkcD42bP97CIoZ97hqV/puOg==} engines: {node: '>=10'} cpu: [x64] os: [linux] @@ -2245,8 +2245,8 @@ packages: dev: true optional: true - /@swc/core-win32-arm64-msvc/1.3.26: - resolution: {integrity: sha512-xx34mx+9IBV1sun7sxoNFiqNom9wiOuvsQFJUyQptCnZHgYwOr9OI204LBF95dCcBCZsTm2hT1wBnySJOeimYw==} + /@swc/core-win32-arm64-msvc/1.3.27: + resolution: {integrity: sha512-baxfH4AbEcaTNo08wxV0W6hiMXwVCxPS4qc0amHpXPti92unvSqeDR1W3C9GjHqzXlWtmCRsq8Ww1pal6ZVLrw==} engines: {node: '>=10'} cpu: [arm64] os: [win32] @@ -2254,8 +2254,8 @@ packages: dev: true optional: true - /@swc/core-win32-ia32-msvc/1.3.26: - resolution: {integrity: sha512-48LZ/HKNuU9zl8c7qG6IQKb5rBCwmJgysGOmEGzTRBYxAf/x6Scmt0aqxCoV4J02HOs2WduCBDnhUKsSQ2kcXQ==} + /@swc/core-win32-ia32-msvc/1.3.27: + resolution: {integrity: sha512-7iLJnH71k5qCwxv9NcM/P7nIEzTsC7r1sIiQW6bu+CpC8qZvwl0PS+XvQRlLly2gCZM+Le98tksYG14MEh+Hrw==} engines: {node: '>=10'} cpu: [ia32] os: [win32] @@ -2263,8 +2263,8 @@ packages: dev: true optional: true - /@swc/core-win32-x64-msvc/1.3.26: - resolution: {integrity: sha512-UPe7S+MezD/S6cKBIc50TduGzmw6PBz1Ms5p+5wDLOKYNS/LSEM4iRmLwvePzP5X8mOyesXrsbwxLy8KHP65Yw==} + /@swc/core-win32-x64-msvc/1.3.27: + resolution: {integrity: sha512-mFM907PDw/jrQ44+TRjIVGEOy2Mu06mMMz0HPMFuRsBzl5t0Kajp3vmn8FkkpS9wH5982VPi6hPYVTb7QJo5Qg==} engines: {node: '>=10'} cpu: [x64] os: [win32] @@ -2272,31 +2272,31 @@ packages: dev: true optional: true - /@swc/core/1.3.26: - resolution: {integrity: sha512-U7vEsaLn3IGg0XCRLJX/GTkK9WIfFHUX5USdrp1L2QD29sWPe25HqNndXmUR9KytzKmpDMNoUuHyiuhpVrnNeQ==} + /@swc/core/1.3.27: + resolution: {integrity: sha512-praRNgpeYGvwDIm/Cl6JU+yHMvwVraL0U6ejMgGyzvpcm1FVsZd1/EYXGqzbBJ0ALv7Gx4eK56h4GnwV6d4L0w==} engines: {node: '>=10'} requiresBuild: true optionalDependencies: - '@swc/core-darwin-arm64': 1.3.26 - '@swc/core-darwin-x64': 1.3.26 - '@swc/core-linux-arm-gnueabihf': 1.3.26 - '@swc/core-linux-arm64-gnu': 1.3.26 - '@swc/core-linux-arm64-musl': 1.3.26 - '@swc/core-linux-x64-gnu': 1.3.26 - '@swc/core-linux-x64-musl': 1.3.26 - '@swc/core-win32-arm64-msvc': 1.3.26 - '@swc/core-win32-ia32-msvc': 1.3.26 - '@swc/core-win32-x64-msvc': 1.3.26 + '@swc/core-darwin-arm64': 1.3.27 + '@swc/core-darwin-x64': 1.3.27 + '@swc/core-linux-arm-gnueabihf': 1.3.27 + '@swc/core-linux-arm64-gnu': 1.3.27 + '@swc/core-linux-arm64-musl': 1.3.27 + '@swc/core-linux-x64-gnu': 1.3.27 + '@swc/core-linux-x64-musl': 1.3.27 + '@swc/core-win32-arm64-msvc': 1.3.27 + '@swc/core-win32-ia32-msvc': 1.3.27 + '@swc/core-win32-x64-msvc': 1.3.27 dev: true - /@swc/jest/0.2.24_@swc+core@1.3.26: + /@swc/jest/0.2.24_@swc+core@1.3.27: resolution: {integrity: sha512-fwgxQbM1wXzyKzl1+IW0aGrRvAA8k0Y3NxFhKigbPjOJ4mCKnWEcNX9HQS3gshflcxq8YKhadabGUVfdwjCr6Q==} engines: {npm: '>= 7.0.0'} peerDependencies: '@swc/core': '*' dependencies: '@jest/create-cache-key-function': 27.5.1 - '@swc/core': 1.3.26 + '@swc/core': 1.3.27 jsonc-parser: 3.2.0 dev: true @@ -2583,7 +2583,7 @@ packages: /@types/glob-stream/6.1.1: resolution: {integrity: sha512-AGOUTsTdbPkRS0qDeyeS+6KypmfVpbT5j23SN8UPG63qjKXNKjXn6V9wZUr8Fin0m9l8oGYaPK8b2WUMF8xI1A==} dependencies: - '@types/glob': 8.0.0 + '@types/glob': 8.0.1 '@types/node': 18.11.18 dev: true @@ -2594,6 +2594,13 @@ packages: '@types/node': 18.11.18 dev: true + /@types/glob/8.0.1: + resolution: {integrity: sha512-8bVUjXZvJacUFkJXHdyZ9iH1Eaj5V7I8c4NdH5sQJsdXkqT4CA5Dhb4yb4VE/3asyx4L9ayZr1NIhTsWHczmMw==} + dependencies: + '@types/minimatch': 5.1.2 + '@types/node': 18.11.18 + dev: true + /@types/graceful-fs/4.1.6: resolution: {integrity: sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==} dependencies: @@ -2641,8 +2648,8 @@ packages: '@types/istanbul-lib-report': 3.0.0 dev: true - /@types/jest/29.2.5: - resolution: {integrity: sha512-H2cSxkKgVmqNHXP7TC2L/WUorrZu8ZigyRywfVzv6EyBlxj39n4C00hjXYQWsbwqgElaj/CiAeSRmk5GoaKTgw==} + /@types/jest/29.2.6: + resolution: {integrity: sha512-XEUC/Tgw3uMh6Ho8GkUtQ2lPhY5Fmgyp3TdlkTJs1W9VgNxs+Ow/x3Elh8lHQKqCbZL0AubQuqWjHVT033Hhrw==} dependencies: expect: 29.3.1 pretty-format: 29.3.1 @@ -2949,8 +2956,8 @@ packages: dev: true optional: true - /@typescript-eslint/eslint-plugin/5.48.1_3jon24igvnqaqexgwtxk6nkpse: - resolution: {integrity: sha512-9nY5K1Rp2ppmpb9s9S2aBiF3xo5uExCehMDmYmmFqqyxgenbHJ3qbarcLt4ITgaD6r/2ypdlcFRdcuVPnks+fQ==} + /@typescript-eslint/eslint-plugin/5.48.2_caon6io6stgpr7lz2rtbhekxqy: + resolution: {integrity: sha512-sR0Gja9Ky1teIq4qJOl0nC+Tk64/uYdX+mi+5iB//MH8gwyx8e3SOyhEzeLZEFEEfCaLf8KJq+Bd/6je1t+CAg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: '@typescript-eslint/parser': ^5.0.0 @@ -2960,13 +2967,13 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/parser': 5.48.1_iukboom6ndih5an6iafl45j2fe - '@typescript-eslint/scope-manager': 5.48.1 - '@typescript-eslint/type-utils': 5.48.1_iukboom6ndih5an6iafl45j2fe - '@typescript-eslint/utils': 5.48.1_iukboom6ndih5an6iafl45j2fe + '@typescript-eslint/parser': 5.48.2_7uibuqfxkfaozanbtbziikiqje + '@typescript-eslint/scope-manager': 5.48.2 + '@typescript-eslint/type-utils': 5.48.2_7uibuqfxkfaozanbtbziikiqje + '@typescript-eslint/utils': 5.48.2_7uibuqfxkfaozanbtbziikiqje debug: 4.3.4 - eslint: 8.31.0 - ignore: 5.2.1 + eslint: 8.32.0 + ignore: 5.2.4 natural-compare-lite: 1.4.0 regexpp: 3.2.0 semver: 7.3.8 @@ -2996,6 +3003,26 @@ packages: - supports-color dev: true + /@typescript-eslint/parser/5.48.2_7uibuqfxkfaozanbtbziikiqje: + resolution: {integrity: sha512-38zMsKsG2sIuM5Oi/olurGwYJXzmtdsHhn5mI/pQogP+BjYVkK5iRazCQ8RGS0V+YLk282uWElN70zAAUmaYHw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/scope-manager': 5.48.2 + '@typescript-eslint/types': 5.48.2 + '@typescript-eslint/typescript-estree': 5.48.2_typescript@4.9.4 + debug: 4.3.4 + eslint: 8.32.0 + typescript: 4.9.4 + transitivePeerDependencies: + - supports-color + dev: true + /@typescript-eslint/scope-manager/5.48.1: resolution: {integrity: sha512-S035ueRrbxRMKvSTv9vJKIWgr86BD8s3RqoRZmsSh/s8HhIs90g6UlK8ZabUSjUZQkhVxt7nmZ63VJ9dcZhtDQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -3004,8 +3031,16 @@ packages: '@typescript-eslint/visitor-keys': 5.48.1 dev: true - /@typescript-eslint/type-utils/5.48.1_iukboom6ndih5an6iafl45j2fe: - resolution: {integrity: sha512-Hyr8HU8Alcuva1ppmqSYtM/Gp0q4JOp1F+/JH5D1IZm/bUBrV0edoewQZiEc1r6I8L4JL21broddxK8HAcZiqQ==} + /@typescript-eslint/scope-manager/5.48.2: + resolution: {integrity: sha512-zEUFfonQid5KRDKoI3O+uP1GnrFd4tIHlvs+sTJXiWuypUWMuDaottkJuR612wQfOkjYbsaskSIURV9xo4f+Fw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + '@typescript-eslint/types': 5.48.2 + '@typescript-eslint/visitor-keys': 5.48.2 + dev: true + + /@typescript-eslint/type-utils/5.48.2_7uibuqfxkfaozanbtbziikiqje: + resolution: {integrity: sha512-QVWx7J5sPMRiOMJp5dYshPxABRoZV1xbRirqSk8yuIIsu0nvMTZesKErEA3Oix1k+uvsk8Cs8TGJ6kQ0ndAcew==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: '*' @@ -3014,10 +3049,10 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 5.48.1_typescript@4.9.4 - '@typescript-eslint/utils': 5.48.1_iukboom6ndih5an6iafl45j2fe + '@typescript-eslint/typescript-estree': 5.48.2_typescript@4.9.4 + '@typescript-eslint/utils': 5.48.2_7uibuqfxkfaozanbtbziikiqje debug: 4.3.4 - eslint: 8.31.0 + eslint: 8.32.0 tsutils: 3.21.0_typescript@4.9.4 typescript: 4.9.4 transitivePeerDependencies: @@ -3029,6 +3064,11 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true + /@typescript-eslint/types/5.48.2: + resolution: {integrity: sha512-hE7dA77xxu7ByBc6KCzikgfRyBCTst6dZQpwaTy25iMYOnbNljDT4hjhrGEJJ0QoMjrfqrx+j1l1B9/LtKeuqA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + /@typescript-eslint/typescript-estree/5.48.1_typescript@4.9.4: resolution: {integrity: sha512-Hut+Osk5FYr+sgFh8J/FHjqX6HFcDzTlWLrFqGoK5kVUN3VBHF/QzZmAsIXCQ8T/W9nQNBTqalxi1P3LSqWnRA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -3050,20 +3090,41 @@ packages: - supports-color dev: true - /@typescript-eslint/utils/5.48.1_iukboom6ndih5an6iafl45j2fe: - resolution: {integrity: sha512-SmQuSrCGUOdmGMwivW14Z0Lj8dxG1mOFZ7soeJ0TQZEJcs3n5Ndgkg0A4bcMFzBELqLJ6GTHnEU+iIoaD6hFGA==} + /@typescript-eslint/typescript-estree/5.48.2_typescript@4.9.4: + resolution: {integrity: sha512-bibvD3z6ilnoVxUBFEgkO0k0aFvUc4Cttt0dAreEr+nrAHhWzkO83PEVVuieK3DqcgL6VAK5dkzK8XUVja5Zcg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/types': 5.48.2 + '@typescript-eslint/visitor-keys': 5.48.2 + debug: 4.3.4 + globby: 11.1.0 + is-glob: 4.0.3 + semver: 7.3.8 + tsutils: 3.21.0_typescript@4.9.4 + typescript: 4.9.4 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/utils/5.48.2_7uibuqfxkfaozanbtbziikiqje: + resolution: {integrity: sha512-2h18c0d7jgkw6tdKTlNaM7wyopbLRBiit8oAxoP89YnuBOzCZ8g8aBCaCqq7h208qUTroL7Whgzam7UY3HVLow==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: '@types/json-schema': 7.0.11 '@types/semver': 7.3.13 - '@typescript-eslint/scope-manager': 5.48.1 - '@typescript-eslint/types': 5.48.1 - '@typescript-eslint/typescript-estree': 5.48.1_typescript@4.9.4 - eslint: 8.31.0 + '@typescript-eslint/scope-manager': 5.48.2 + '@typescript-eslint/types': 5.48.2 + '@typescript-eslint/typescript-estree': 5.48.2_typescript@4.9.4 + eslint: 8.32.0 eslint-scope: 5.1.1 - eslint-utils: 3.0.0_eslint@8.31.0 + eslint-utils: 3.0.0_eslint@8.32.0 semver: 7.3.8 transitivePeerDependencies: - supports-color @@ -3078,6 +3139,14 @@ packages: eslint-visitor-keys: 3.3.0 dev: true + /@typescript-eslint/visitor-keys/5.48.2: + resolution: {integrity: sha512-z9njZLSkwmjFWUelGEwEbdf4NwKvfHxvGC0OcGN1Hp/XNDIcJ7D5DpPNPv6x6/mFvc1tQHsaWmpD/a4gOvvCJQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + '@typescript-eslint/types': 5.48.2 + eslint-visitor-keys: 3.3.0 + dev: true + /@vitejs/plugin-vue/4.0.0_vite@4.0.4+vue@3.2.45: resolution: {integrity: sha512-e0X4jErIxAB5oLtDqbHvHpJe/uWNkdpYV83AOG2xo2tEVSzCzewgJMtREZM30wXnM5ls90hxiOtAuVU6H5JgbA==} engines: {node: ^14.18.0 || >=16.0.0} @@ -4039,8 +4108,8 @@ packages: dependencies: fill-range: 7.0.1 - /broadcast-channel/4.20.1: - resolution: {integrity: sha512-ob5xyUEMWJRBOggwVGEZpKCXK/Pkfe3LkrokNTwgywhKXFNETRkD5IanLqmpY/roy7bqBsijb7lMEDyc1qlnHQ==} + /broadcast-channel/4.20.2: + resolution: {integrity: sha512-v0lJgMzC+MX4e2KCFWYXChZ2mKTqm5mnJGId6tqJp3NfylggbNd8c2uKeP4MQxD2ucKOesY68aN98zwl9d6Tvg==} dependencies: '@babel/runtime': 7.20.7 oblivious-set: 1.1.1 @@ -4403,45 +4472,45 @@ packages: is-regex: 1.1.4 dev: false - /chart.js/4.1.2: - resolution: {integrity: sha512-9L1w6WLPq6ztiWVVOYtDtpo0CUsBKDWPrUEdwChAyzczaikqeSwNKEv3QpJ7EO4ICcLSi6UDVhgvcnUhRJidRA==} + /chart.js/4.2.0: + resolution: {integrity: sha512-wbtcV+QKeH0F7gQZaCJEIpsNriFheacouJQTVIjITi3eQA8bTlIBoknz0+dgV79aeKLNMAX+nDslIVE/nJ3rzA==} engines: {pnpm: ^7.0.0} dependencies: '@kurkle/color': 0.3.2 dev: false - /chartjs-adapter-date-fns/3.0.0_kluv2ktejb2igagp3yc56zuwiy: + /chartjs-adapter-date-fns/3.0.0_n6szoxj4ax2zhp2sxsxxj6zdla: resolution: {integrity: sha512-Rs3iEB3Q5pJ973J93OBTpnP7qoGwvq3nUnoMdtxO+9aoJof7UFcRbWcIDteXuYd1fgAvct/32T9qaLyLuZVwCg==} peerDependencies: chart.js: '>=2.8.0' date-fns: '>=2.0.0' dependencies: - chart.js: 4.1.2 + chart.js: 4.2.0 date-fns: 2.29.3 dev: false - /chartjs-chart-matrix/1.3.0_chart.js@4.1.2: + /chartjs-chart-matrix/1.3.0_chart.js@4.2.0: resolution: {integrity: sha512-oPmyxY60tJDBFbnhXcmcJujs+F1a2uMvb9HOhSeV2/5k8L9LApWtyRJzwNWawMl8vDbWdhAfobq06b4AcdwE3Q==} peerDependencies: chart.js: '>=3.0.0' dependencies: - chart.js: 4.1.2 + chart.js: 4.2.0 dev: false - /chartjs-plugin-gradient/0.6.1_chart.js@4.1.2: + /chartjs-plugin-gradient/0.6.1_chart.js@4.2.0: resolution: {integrity: sha512-TGHNIh8KqQMLdb+UfY80cBHYRyOC47eeokmgkeajRdKGbFt462lJiyiq4ZJ25fiM7BGsmzoBLhmVyEw4B3gQxw==} peerDependencies: chart.js: '>=2.6.0' dependencies: - chart.js: 4.1.2 + chart.js: 4.2.0 dev: false - /chartjs-plugin-zoom/2.0.0_chart.js@4.1.2: + /chartjs-plugin-zoom/2.0.0_chart.js@4.2.0: resolution: {integrity: sha512-bqpi7DGy9a5hX7ThKl/xQaLzXvneSwhS0w/lNimZ8AJaoRVMKz5JfUoqwciJYV5ixKXJbgyvwC9HcJnyVsYmjg==} peerDependencies: chart.js: '>=3.2.0' dependencies: - chart.js: 4.1.2 + chart.js: 4.2.0 hammerjs: 2.0.8 dev: false @@ -6040,6 +6109,35 @@ packages: - supports-color dev: true + /eslint-module-utils/2.7.4_kvyj4idustix6trhy5lyssy2sq: + resolution: {integrity: sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + dependencies: + '@typescript-eslint/parser': 5.48.2_7uibuqfxkfaozanbtbziikiqje + debug: 3.2.7 + eslint: 8.32.0 + eslint-import-resolver-node: 0.3.7 + transitivePeerDependencies: + - supports-color + dev: true + /eslint-module-utils/2.7.4_sqt5xxn4ciiurbqrzlaarm6ama: resolution: {integrity: sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==} engines: {node: '>=4'} @@ -6102,19 +6200,52 @@ packages: - supports-color dev: true - /eslint-plugin-vue/9.9.0_eslint@8.31.0: + /eslint-plugin-import/2.27.5_2l6piu6guil2f63lj3qmhzbnn4: + resolution: {integrity: sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + dependencies: + '@typescript-eslint/parser': 5.48.2_7uibuqfxkfaozanbtbziikiqje + array-includes: 3.1.6 + array.prototype.flat: 1.3.1 + array.prototype.flatmap: 1.3.1 + debug: 3.2.7 + doctrine: 2.1.0 + eslint: 8.32.0 + eslint-import-resolver-node: 0.3.7 + eslint-module-utils: 2.7.4_kvyj4idustix6trhy5lyssy2sq + has: 1.0.3 + is-core-module: 2.11.0 + is-glob: 4.0.3 + minimatch: 3.1.2 + object.values: 1.1.6 + resolve: 1.22.1 + semver: 6.3.0 + tsconfig-paths: 3.14.1 + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + dev: true + + /eslint-plugin-vue/9.9.0_eslint@8.32.0: resolution: {integrity: sha512-YbubS7eK0J7DCf0U2LxvVP7LMfs6rC6UltihIgval3azO3gyDwEGVgsCMe1TmDiEkl6GdMKfRpaME6QxIYtzDQ==} engines: {node: ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.2.0 || ^7.0.0 || ^8.0.0 dependencies: - eslint: 8.31.0 - eslint-utils: 3.0.0_eslint@8.31.0 + eslint: 8.32.0 + eslint-utils: 3.0.0_eslint@8.32.0 natural-compare: 1.4.0 nth-check: 2.1.1 postcss-selector-parser: 6.0.11 semver: 7.3.8 - vue-eslint-parser: 9.1.0_eslint@8.31.0 + vue-eslint-parser: 9.1.0_eslint@8.32.0 xml-name-validator: 4.0.0 transitivePeerDependencies: - supports-color @@ -6146,6 +6277,16 @@ packages: eslint-visitor-keys: 2.1.0 dev: true + /eslint-utils/3.0.0_eslint@8.32.0: + resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} + engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} + peerDependencies: + eslint: '>=5' + dependencies: + eslint: 8.32.0 + eslint-visitor-keys: 2.1.0 + dev: true + /eslint-visitor-keys/2.1.0: resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==} engines: {node: '>=10'} @@ -6204,6 +6345,54 @@ packages: - supports-color dev: true + /eslint/8.32.0: + resolution: {integrity: sha512-nETVXpnthqKPFyuY2FNjz/bEd6nbosRgKbkgS/y1C7LJop96gYHWpiguLecMHQ2XCPxn77DS0P+68WzG6vkZSQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + hasBin: true + dependencies: + '@eslint/eslintrc': 1.4.1 + '@humanwhocodes/config-array': 0.11.8 + '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.3 + debug: 4.3.4 + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.1.1 + eslint-utils: 3.0.0_eslint@8.32.0 + eslint-visitor-keys: 3.3.0 + espree: 9.4.1 + esquery: 1.4.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.19.0 + grapheme-splitter: 1.0.4 + ignore: 5.2.4 + import-fresh: 3.3.0 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + js-sdsl: 4.2.0 + js-yaml: 4.1.0 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.1 + regexpp: 3.2.0 + strip-ansi: 6.0.1 + strip-json-comments: 3.1.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + dev: true + /espree/9.4.1: resolution: {integrity: sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -6501,6 +6690,10 @@ packages: time-stamp: 1.1.0 dev: false + /fast-content-type-parse/1.0.0: + resolution: {integrity: sha512-Xbc4XcysUXcsP5aHUU7Nq3OwvHq97C+WnbkeIefpeYLX+ryzFJlU6OStFJhs6Ol0LkUGpcK+wL0JwfM+FCU5IA==} + dev: false + /fast-decode-uri-component/1.0.1: resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==} dev: false @@ -6569,15 +6762,15 @@ packages: resolution: {integrity: sha512-79ak0JxddO0utAXAQ5ccKhvs6vX2MGyHHMMsmZkBANrq3hXc1CHzvNPHOcvTsVMEPl5I+NT+RO4YKMGehOfSIg==} dev: false - /fastify/4.11.0: - resolution: {integrity: sha512-JteZ8pjEqd+6n+azQnQfSJV8MUMxAmxbvC2Dx/Mybj039Lf/u3kda9Kq84uy/huCpqCzZoyHIZS5JFGF3wLztw==} + /fastify/4.12.0: + resolution: {integrity: sha512-Hh2GCsOCqnOuewWSvqXlpq5V/9VA+/JkVoooQWUhrU6gryO9+/UGOoF/dprGcKSDxkM/9TkMXSffYp8eA/YhYQ==} dependencies: '@fastify/ajv-compiler': 3.5.0 '@fastify/error': 3.2.0 '@fastify/fast-json-stringify-compiler': 4.2.0 abstract-logging: 2.0.1 avvio: 8.2.0 - content-type: 1.0.4 + fast-content-type-parse: 1.0.0 find-my-way: 7.4.0 light-my-request: 5.8.0 pino: 8.8.0 @@ -6636,8 +6829,8 @@ packages: flat-cache: 3.0.4 dev: true - /file-type/18.1.0: - resolution: {integrity: sha512-FqjmVvHjX5C/EnibCENAsCMIg7HgUYO0vDypt5V8RmtKDk7eUa+/6mEWSrY4PStFhUt0K3CoE8stjLJCcMsJFQ==} + /file-type/18.2.0: + resolution: {integrity: sha512-M3RQMWY3F2ykyWZ+IHwNCjpnUmukYhtdkGGC1ZVEUb0ve5REGF7NNJ4Q9ehCUabtQKtSVFOMbFTXgJlFb0DQIg==} engines: {node: '>=14.16'} dependencies: readable-web-to-node-stream: 3.0.2 @@ -7778,11 +7971,6 @@ packages: /ieee754/1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - /ignore/5.2.1: - resolution: {integrity: sha512-d2qQLzTJ9WxQftPAuEQpSPmKqzxePjzVbpAVv62AQ64NTL+wR4JkrVqR/LqFsFEUsHDAiId52mJteHDFuDkElA==} - engines: {node: '>= 4'} - dev: true - /ignore/5.2.4: resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} engines: {node: '>= 4'} @@ -11415,8 +11603,8 @@ packages: resolution: {integrity: sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==} dev: false - /punycode/2.2.0: - resolution: {integrity: sha512-LN6QV1IJ9ZhxWTNdktaPClrNfp8xdSAYS0Zk2ddX7XsXZAxckMHPCBcHRo0cTcEIgYPRiGEkmji3Idkh2yFtYw==} + /punycode/2.3.0: + resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} engines: {node: '>=6'} /pureimage/0.3.15: @@ -11971,8 +12159,8 @@ packages: seedrandom: 2.4.2 dev: false - /rollup/3.10.0: - resolution: {integrity: sha512-JmRYz44NjC1MjVF2VKxc0M1a97vn+cDxeqWmnwyAF4FvpjK8YFdHpaqvQB+3IxCvX05vJxKZkoMDU8TShhmJVA==} + /rollup/3.10.1: + resolution: {integrity: sha512-3Er+yel3bZbZX1g2kjVM+FW+RUWDxbG87fcqFM5/9HbPCTpbVp6JOLn7jlxnNlbu7s/N/uDA4EV/91E2gWnxzw==} engines: {node: '>=14.18.0', npm: '>=8.0.0'} hasBin: true optionalDependencies: @@ -13065,14 +13253,14 @@ packages: engines: {node: '>=0.8'} dependencies: psl: 1.9.0 - punycode: 2.2.0 + punycode: 2.3.0 /tough-cookie/4.1.2: resolution: {integrity: sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==} engines: {node: '>=6'} dependencies: psl: 1.9.0 - punycode: 2.2.0 + punycode: 2.3.0 universalify: 0.2.0 url-parse: 1.5.10 dev: false @@ -13084,7 +13272,7 @@ packages: resolution: {integrity: sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==} engines: {node: '>=12'} dependencies: - punycode: 2.2.0 + punycode: 2.3.0 dev: false /trace-redirect/1.0.6: @@ -13353,8 +13541,8 @@ packages: undertaker-registry: 1.0.1 dev: false - /undici/5.15.0: - resolution: {integrity: sha512-wCAZJDyjw9Myv+Ay62LAoB+hZLPW9SmKbQkbHIhMw/acKSlpn7WohdMUc/Vd4j1iSMBO0hWwU8mjB7a5p5bl8g==} + /undici/5.15.1: + resolution: {integrity: sha512-XLk8g0WAngdvFqTI+VKfBtM4YWXgdxkf1WezC771Es0Dd+Pm1KmNx8t93WTC+Hh9tnghmVxkclU1HN+j+CvIUA==} engines: {node: '>=12.18'} dependencies: busboy: 1.6.0 @@ -13460,7 +13648,7 @@ packages: /uri-js/4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} dependencies: - punycode: 2.2.0 + punycode: 2.3.0 /urix/0.1.0: resolution: {integrity: sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==} @@ -13675,7 +13863,7 @@ packages: esbuild: 0.16.17 postcss: 8.4.21 resolve: 1.22.1 - rollup: 3.10.0 + rollup: 3.10.1 sass: 1.57.1 optionalDependencies: fsevents: 2.3.2 @@ -13686,14 +13874,14 @@ packages: engines: {node: '>=0.10.0'} dev: false - /vue-eslint-parser/9.1.0_eslint@8.31.0: + /vue-eslint-parser/9.1.0_eslint@8.32.0: resolution: {integrity: sha512-NGn/iQy8/Wb7RrRa4aRkokyCZfOUWk19OP5HP6JEozQFX5AoS/t+Z0ZN7FY4LlmWc4FNI922V7cvX28zctN8dQ==} engines: {node: ^14.17.0 || >=16.0.0} peerDependencies: eslint: '>=6.0.0' dependencies: debug: 4.3.4 - eslint: 8.31.0 + eslint: 8.32.0 eslint-scope: 7.1.1 eslint-visitor-keys: 3.3.0 espree: 9.4.1 From ead931211c6783975d01928fa4636e42208689a1 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 22 Jan 2023 04:24:38 +0900 Subject: [PATCH 46/70] =?UTF-8?q?fix(client):=20=E5=AE=9F=E7=B8=BE?= =?UTF-8?q?=E8=A7=A3=E9=99=A4=E3=83=AA=E3=82=AF=E3=82=A8=E3=82=B9=E3=83=88?= =?UTF-8?q?=E3=81=AE=E9=96=93=E9=9A=94=E3=82=92=E3=81=82=E3=81=91=E3=82=8B?= =?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 Fix #9674 --- packages/frontend/src/scripts/achievements.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/frontend/src/scripts/achievements.ts b/packages/frontend/src/scripts/achievements.ts index 8f484f8925..c97358e880 100644 --- a/packages/frontend/src/scripts/achievements.ts +++ b/packages/frontend/src/scripts/achievements.ts @@ -433,16 +433,22 @@ export const ACHIEVEMENT_BADGES = { export const claimedAchievements = ($i && $i.achievements) ? $i.achievements.map(x => x.name) : []; -export function claimAchievement(type: typeof ACHIEVEMENT_TYPES[number]) { +const claimingQueue = new Set<string>(); + +export async function claimAchievement(type: typeof ACHIEVEMENT_TYPES[number]) { if (claimedAchievements.includes(type)) return; - os.api('i/claim-achievement', { name: type }); + claimingQueue.add(type); claimedAchievements.push(type); + await new Promise(resolve => setTimeout(resolve, (claimingQueue.size - 1) * 500)); + window.setTimeout(() => { + claimingQueue.delete(type); + }, 500); + os.api('i/claim-achievement', { name: type }); } if (_DEV_) { - (window as any).unlockAllAchievements = async () => { + (window as any).unlockAllAchievements = () => { for (const t of ACHIEVEMENT_TYPES) { - await new Promise(resolve => setTimeout(resolve, 100)); claimAchievement(t); } }; From d6ff50a30b8ec6e1875a539438a116f80b538490 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 22 Jan 2023 04:28:19 +0900 Subject: [PATCH 47/70] New Crowdin updates (#9676) * New translations ja-JP.yml (German) * New translations ja-JP.yml (English) * New translations ja-JP.yml (German) * New translations ja-JP.yml (Korean) * New translations ja-JP.yml (Chinese Simplified) * New translations ja-JP.yml (Chinese Traditional) * New translations ja-JP.yml (English) * New translations ja-JP.yml (Russian) * New translations ja-JP.yml (English) * New translations ja-JP.yml (German) * New translations ja-JP.yml (Russian) * New translations ja-JP.yml (Chinese Traditional) * New translations ja-JP.yml (English) * New translations ja-JP.yml (Russian) * New translations ja-JP.yml (Chinese Simplified) * New translations ja-JP.yml (Chinese Traditional) * New translations ja-JP.yml (English) * New translations ja-JP.yml (Russian) * New translations ja-JP.yml (Chinese Simplified) * New translations ja-JP.yml (Chinese Traditional) * New translations ja-JP.yml (English) * New translations ja-JP.yml (Russian) --- locales/de-DE.yml | 229 ++++++++++++++++++++++++++++++++++++++++++++++ locales/en-US.yml | 197 ++++++++++++++++++++++++++++++++++++++- locales/ko-KR.yml | 13 +++ locales/ru-RU.yml | 225 +++++++++++++++++++++++++++++++++++++++++++++ locales/zh-CN.yml | 77 ++++++++++++++++ locales/zh-TW.yml | 106 ++++++++++++++++++++- 6 files changed, 845 insertions(+), 2 deletions(-) diff --git a/locales/de-DE.yml b/locales/de-DE.yml index f6095e4db6..ecead98c32 100644 --- a/locales/de-DE.yml +++ b/locales/de-DE.yml @@ -938,6 +938,234 @@ cannotPerformTemporary: "Vorübergehend nicht verfügbar" cannotPerformTemporaryDescription: "Diese Aktion ist wegen des Überschreitenes des Ausführungslimits temporär nicht verfügbar. Bitte versuche es nach einiger Zeit erneut." preset: "Vorlage" selectFromPresets: "Aus Vorlagen wählen" +achievements: "Errungenschaften" +_achievements: + earnedAt: "Freigeschaltet am" + _types: + _notes1: + title: "Hallo Misskey!" + description: "Sende deine erste Notiz" + flavor: "Hab eine schöne Zeit mit Misskey!" + _notes10: + title: "Ein paar Notizen" + description: "10 Notizen gesendet" + _notes100: + title: "Viele Notizen" + description: "100 Notizen gesendet" + _notes500: + title: "Überschüttet mit Notizen" + description: "500 Notizen gesendet" + _notes1000: + title: "Berg an Notizen" + description: "1.000 Notizen gesendet" + _notes5000: + title: "Überquellende Notizen" + description: "5.000 Notizen gesendet" + _notes10000: + title: "Supernotiz" + description: "10.000 Notizen gesendet" + _notes20000: + title: "Brauche... mehr... Notizen" + description: "20.000 Notizen gesendet" + _notes30000: + title: "Notizen, Notizen, Notizen" + description: "30.000 Notizen gesendet" + _notes40000: + title: "Notizfabrik" + description: "40.000 Notizen gesendet" + _notes50000: + title: "Planet der Notizen" + description: "50.000 Notizen gesendet" + _notes60000: + title: "Notizquasar" + description: "60.000 Notizen gesendet" + _notes70000: + title: "Schwarzes Notizloch" + description: "70.000 Notizen gesendet" + _notes80000: + title: "Notizgalaxie" + description: "80.000 Notizen gesendet" + _notes90000: + title: "Notizversum" + description: "90.000 Notizen gesendet" + _notes100000: + title: "ALL YOUR NOTE ARE BELONG TO US" + description: "100.000 Notizen gesendet" + flavor: "Du hast wirklich viel zu sagen." + _login3: + title: "Anfänger Ⅰ" + description: "An 3 Tagen eingeloggt" + flavor: "Nenn' mich ab heute Misskist" + _login7: + title: "Anfänger Ⅱ" + description: "An 7 Tagen eingeloggt" + flavor: "Na, eingewöht?" + _login15: + title: "Anfänger Ⅲ" + description: "An 15 Tagen eingeloggt" + _login30: + title: "Misskist Ⅰ" + description: "An 30 Tagen eingeloggt" + _login60: + title: "Misskist Ⅱ" + description: "An 60 Tagen eingeloggt" + _login100: + title: "Misskist Ⅲ" + description: "An 100 Tagen eingeloggt" + flavor: "Violent Misskist" + _login200: + title: "Stammbesucher Ⅰ" + description: "An 200 Tagen eingeloggt" + _login300: + title: "Stammbesucher Ⅱ" + description: "An 300 Tagen eingeloggt" + _login400: + title: "Stammbesucher Ⅲ" + description: "An 400 Tagen eingeloggt" + _login500: + title: "Veteran Ⅰ" + description: "An 500 Tagen eingeloggt" + flavor: "Meine Kameraden, ich liebe sie, die Notizen." + _login600: + title: "Veteran Ⅱ" + description: "An 600 Tagen eingeloggt" + _login700: + title: "Veteran Ⅲ" + description: "An 700 Tagen eingeloggt" + _login800: + title: "Meister der Notizen Ⅰ" + description: "An 800 Tagen eingeloggt" + _login900: + title: "Meister der Notizen Ⅱ" + description: "An 900 Tagen eingeloggt" + _login1000: + title: "Meister der Notizen Ⅲ" + description: "An 1000 Tagen eingeloggt" + flavor: "Danke, dass du Misskey nutzt!" + _noteClipped1: + title: "Muss... clippen..." + description: "Die erste Notiz geclippt" + _noteFavorited1: + title: "Sternengucker" + description: "Eine Notiz als Favorit markiert" + _profileFilled: + title: "Perfekte Vorbereitung" + description: "Fülle dein Profil aus" + _markedAsCat: + title: "Ich der Kater" + description: "Markiere dein Konto als Katze" + flavor: "Einen Namen bekommst du später. " + _following1: + title: "Das Folgen beginnt" + description: "Du folgst deiner ersten Person" + _following10: + title: "Folge ihnen... folge ihnen..." + description: "Du folgst über 10 Leuten" + _following50: + title: "Viele Freunde" + description: "Du folgst über 50 Leuten" + _following100: + title: "100 Freunde" + description: "Du folgst über 100 Leuten" + _following300: + title: "Freundeüberschuss" + description: "Du folgst über 300 Leuten" + _followers1: + title: "Der erste Follower" + description: "Du hast deinen ersten Follower erhalten" + _followers10: + title: "Mir nach!" + description: "Die Anzahl deiner Follower hat 10 überschritten" + _followers50: + title: "Wirrwarr" + description: "Die Anzahl deiner Follower hat 50 überschritten" + _followers100: + title: "Beliebt" + description: "Die Anzahl deiner Follower hat 100 überschritten" + _followers300: + title: "Stellt euch bitte in einer Reihe auf" + description: "Die Anzahl deiner Follower hat 300 überschritten" + _followers500: + title: "Funkmast" + description: "Die Anzahl deiner Follower hat 500 überschritten" + _followers1000: + title: "Influencer" + description: "Die Anzahl deiner Follower hat 1000 überschritten" + _collectAchievements30: + title: "Sammler der Errungenschaften" + description: "Schalte 30 Errungenschaften frei" + _viewAchievements3min: + title: "Fan von Errungenschaften" + description: "Schau dir die Liste deiner Errungenschaften für mindestens 3 Minuten an" + _iLoveMisskey: + title: "I Love Misskey" + description: "Sende \"I ❤ #Misskey\"" + flavor: "Danke, dass du Misskey verwendest! - vom Entwicklerteam" + _client30min: + title: "Kleine Pause" + description: "Seit dem Öffnen deines Clients sind 30 Minuten vergangen" + _noteDeletedWithin1min: + title: "Ups" + description: "Lösche eine Notiz innerhalb von 1 Minute nachdem sie gesendet wurde" + _postedAtLateNight: + title: "Nachtaktiv" + description: "Sende mitten in der Nacht eine Notiz" + flavor: "Geh bald schlafen." + _postedAt0min0sec: + title: "Zeitansage" + description: "Sende um 00:00 eine Notiz" + flavor: "Klick Klick Klick Dooong" + _selfQuote: + title: "Selbstzitat" + description: "Zitiere eine eigene Notiz" + _htl20npm: + title: "Fließende Chronik" + description: "Deine Startseitenchronik erreicht eine Geschwindigkeit von 20 npm (Notizen pro Minute)" + _outputHelloWorldOnScratchpad: + title: "Hallo Welt!" + description: "Gib \"hello world\" in der Testumgebung aus" + _open3windows: + title: "Splitscreen" + description: "Habe zur gleichen Zeit mindestens 3 Fenster offen" + _driveFolderCircularReference: + title: "Zyklischer Verweis" + description: "Versuche, in Drive einen Zirkelbezug von Ordnern herzustellen" + _reactWithoutRead: + title: "Hast du das wirklich gelesen?" + description: "Reagiere auf eine Notiz mit mindestens 100 Zeichen innerhalb von 3 Sekunden der Erstellung der Notiz" + _clickedClickHere: + title: "Klicke hier" + description: "Du hast hier geklickt" + _justPlainLucky: + title: "Pures Glück" + description: "Kann alle 10 Sekunden mit einer Warscheinlichkeit von 0.01% erhalten werden" + _setNameToSyuilo: + title: "Gottkomplex" + description: "Setze deinen Namen auf \"syuilo\"" + _passedSinceAccountCreated1: + title: "Einjahresjubiläum" + description: "Seit der Erstellung deines Kontos ist 1 Jahr vergangen" + _passedSinceAccountCreated2: + title: "Zweijahresjubiläum" + description: "Seit der Erstellung deines Kontos sind 2 Jahre vergangen" + _passedSinceAccountCreated3: + title: "Dreijahresjubiläum" + description: "Seit der Erstellung deines Kontos sind 3 Jahre vergangen" + _loggedInOnBirthday: + title: "Alles Gute Zum Geburtstag" + description: "Logge dich an deinem Geburtstag ein" + _loggedInOnNewYearsDay: + title: "Frohes Neujahr" + description: "Logge dich am Neujahrstag ein" + flavor: "Auf ein weiteres tolles Jahr in dieser Instanz" + _cookieClicked: + title: "Ein Spiel, in dem du auf einen Keks klickst" + description: "Den Keks geklickt" + flavor: "Bist du hier richtig?" + _brainDiver: + title: "Brain Diver" + description: "Sende den Link zu Brain Diver" + flavor: "Misskey-Misskey La-Tu-Ma" _role: new: "Rolle erstellen" edit: "Rolle bearbeiten" @@ -1587,6 +1815,7 @@ _notification: pollEnded: "Umfrageergebnisse sind verfügbar" unreadAntennaNote: "Antenne {name}" emptyPushNotificationMessage: "Push-Benachrichtigungen wurden aktualisiert" + achievementEarned: "Errungenschaft freigeschaltet" _types: all: "Alle" follow: "Neue Follower" diff --git a/locales/en-US.yml b/locales/en-US.yml index b9f1603d26..e398f1fd58 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -938,8 +938,198 @@ cannotPerformTemporary: "Temporarily unavailable" cannotPerformTemporaryDescription: "This action cannot be performed temporarily due to exceeding the execution limit. Please wait for a while and then try again." preset: "Preset" selectFromPresets: "Choose from presets" +achievements: "Achievements" _achievements: + earnedAt: "Unlocked at" _types: + _notes1: + title: "just setting up my msky" + description: "Post your first note" + flavor: "Have a good Misskey life!" + _notes10: + title: "Some notes" + description: "Post 10 notes" + _notes100: + title: "A lot of notes" + description: "Post 100 notes" + _notes500: + title: "Covered in notes" + description: "Post 500 notes" + _notes1000: + title: "A mountain of notes" + description: "Post 1,000 notes" + _notes5000: + title: "Overflowing notes" + description: "Post 5,000 notes" + _notes10000: + title: "Supernote" + description: "Post 10,000 notes" + _notes20000: + title: "Need... more... notes..." + description: "Post 20,000 notes" + _notes30000: + title: "Notes notes notes!" + description: "Post 30,000 notes" + _notes40000: + title: "Note factory" + description: "Post 40,000 notes" + _notes50000: + title: "Planet of notes" + description: "Post 50,000 notes" + _notes60000: + title: "Note quasar" + description: "Post 60,000 notes" + _notes70000: + title: "Note black hole" + description: "Post 70,000 notes" + _notes80000: + title: "Note galaxy" + description: "Post 80,000 notes" + _notes90000: + title: "Note universe" + description: "Post 90,000 notes" + _notes100000: + title: "ALL YOUR NOTE ARE BELONG TO US" + description: "Post 100,000 notes" + flavor: "You sure have a lot to say." + _login3: + title: "Beginner I" + description: "Log in for a total of 3 days" + flavor: "Starting today, just call me Misskist" + _login7: + title: "Beginner II" + description: "Log in for a total of 7 days" + flavor: "Feel like you've gotten the hang of things yet?" + _login15: + title: "Beginner III" + description: "Log in for a total of 15 days" + _login30: + title: "Misskist I" + description: "Log in for a total of 30 days" + _login60: + title: "Misskist II" + description: "Log in for a total of 60 days" + _login100: + title: "Misskist III" + description: "Log in for a total of 100 days" + flavor: "Violent Misskist" + _login200: + title: "Regular I" + description: "Log in for a total of 200 days" + _login300: + title: "Regular II" + description: "Log in for a total of 300 days" + _login400: + title: "Regular III" + description: "Log in for a total of 400 days" + _login500: + title: "Expert I" + description: "Log in for a total of 500 days" + flavor: "My friends, it has often been said that I like notes" + _login600: + title: "Expert II" + description: "Log in for a total of 600 days" + _login700: + title: "Expert III" + description: "Log in for a total of 700 days" + _login800: + title: "Master of Notes I" + description: "Log in for a total of 800 days" + _login900: + title: "Master of Notes II" + description: "Log in for a total of 900 days" + _login1000: + title: "Master of Notes III" + description: "Log in for a total of 1,000 days" + flavor: "Thank you for using Misskey!" + _noteClipped1: + title: "Must... clip..." + description: "Clip your first note" + _noteFavorited1: + title: "Stargazer" + description: "Favorite your first note" + _profileFilled: + title: "Well-prepared" + description: "Set up your profile" + _markedAsCat: + title: "I Am a Cat" + description: "Mark your account as a cat" + flavor: "I'll give you a name later." + _following1: + title: "Following your first user" + description: "Follow a user" + _following10: + title: "Keep up... keep up..." + description: "Follow 10 users" + _following50: + title: "Lots of friends" + description: "Follow 50 accounts" + _following100: + title: "100 Friends" + description: "Follow 100 accounts" + _following300: + title: "Friend overload" + description: "Follow 300 accounts" + _followers1: + title: "First follower" + description: "Gain 1 follower" + _followers10: + title: "Follow me!" + description: "Gain 10 followers" + _followers50: + title: "Coming in crowds" + description: "Gain 50 followers" + _followers100: + title: "Popular" + description: "Gain 100 followers" + _followers300: + title: "Please form a single line" + description: "Gain 300 followers" + _followers500: + title: "Radio Tower" + description: "Gain 500 followers" + _followers1000: + title: "Influencer" + description: "Gain 1,000 followers" + _collectAchievements30: + title: "Achievement Collector" + description: "Earn 30 achievements" + _viewAchievements3min: + title: "Likes Achievements" + description: "Look at your list of achievements for at least 3 minutes" + _iLoveMisskey: + title: "I Love Misskey" + description: "Post \"I ❤ #Misskey\"" + flavor: "Misskey's development team greatly appreciates your support!" + _client30min: + title: "Short break" + description: "Spend 30 minutes on Misskey" + _noteDeletedWithin1min: + title: "Nevermind" + description: "Delete a note within a minute of posting it" + _postedAtLateNight: + title: "Nocturnal" + description: "Post a note late at night" + flavor: "It's about time to go to bed." + _postedAt0min0sec: + title: "Speaking Clock" + description: "Post a note at 00:00" + flavor: "Click Click Click Claaang" + _selfQuote: + title: "Self-Reference" + description: "Quote your own note" + _htl20npm: + title: "Flowing Timeline" + description: "Have the speed of your home timeline exceed 20 npm (notes per minute)" + _outputHelloWorldOnScratchpad: + title: "Hello, world!" + description: "Output \"hello world\" in the Scratchpad" + _open3windows: + title: "Multi-Window" + description: "Have at least 3 windows open at the same time" + _driveFolderCircularReference: + title: "Circular Reference" + description: "Attempt to create a recursively nested folder in Drive" _reactWithoutRead: title: "Did you really read that?" description: "React on a note that's over 100 characters long within 3 seconds of it being posted" @@ -963,10 +1153,15 @@ _achievements: description: "Three years have passed since your account was created" _loggedInOnBirthday: title: "Happy Birthday" - description: "Logged in on your birthday" + description: "Log in on your birthday" + _loggedInOnNewYearsDay: + title: "Happy New Year!" + description: "Logged in on the first day of the year" + flavor: "To another great year on this instance" _cookieClicked: title: "A game in which you click cookies" description: "Clicked the cookie" + flavor: "Wait, are you on the correct website?" _brainDiver: title: "Brain Diver" description: "Post the link to Brain Diver" diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml index 3e6ab5a2f5..bf747e3cb7 100644 --- a/locales/ko-KR.yml +++ b/locales/ko-KR.yml @@ -1094,6 +1094,9 @@ _achievements: _collectAchievements30: title: "도전과제 콜렉터" description: "30개의 도전과제를 획득했습니다" + _viewAchievements3min: + title: "저 도전과제 좋아해요" + description: "도전과제 목록을 3분 이상 보세요" _iLoveMisskey: title: "I Love Misskey" description: "\"I ❤ #Misskey\"를 포스트했습니다" @@ -1118,6 +1121,12 @@ _achievements: _htl20npm: title: "타임라인 폭주 중" description: "1분 사이에 홈 타임라인에 노트가 20개 넘게 생성되었습니다" + _outputHelloWorldOnScratchpad: + title: "Hello, world!" + description: "스크래치패드에서 hello world를 출력하세요" + _open3windows: + title: "멀티 윈도우" + description: "3개 이상의 창을 여세요" _driveFolderCircularReference: title: "순환 참조" description: "드라이브 폴더를 자신을 가리키도록 만드려 시도했습니다" @@ -1145,6 +1154,10 @@ _achievements: _loggedInOnBirthday: title: "생일 축하합니다!" description: "설정한 생일에 로그인했습니다" + _loggedInOnNewYearsDay: + title: "새해 복 많이 받으세요" + description: "새해 첫 날에 로그인했습니다" + flavor: "올해에도 저희 인스턴스에 관심을 가져 주셔서 감사합니다" _cookieClicked: title: "쿠키 클리커 게임" description: "쿠키를 클릭했습니다" diff --git a/locales/ru-RU.yml b/locales/ru-RU.yml index d7aca1c9fc..133169e8da 100644 --- a/locales/ru-RU.yml +++ b/locales/ru-RU.yml @@ -2,6 +2,7 @@ _lang_: "Русский" headlineMisskey: "Сеть, сплетённая из заметок" introMisskey: "Добро пожаловать! Misskey — это децентрализованный сервис микроблогов с открытым исходным кодом.\nПишите «заметки» — делитесь со всеми происходящим вокруг или рассказывайте о себе 📡\nСтавьте «реакции» — выражайте свои чувства и эмоции от заметок других 👍\nОткройте для себя новый мир 🚀" +poweredByMisskeyDescription: "{name} – один из инстансов (также называемый экземпляром Misskey), использующий платформу с открытым исходным кодом <b>Misskey</b>." monthAndDay: "{day}.{month}" search: "Поиск" notifications: "Уведомления" @@ -12,6 +13,7 @@ fetchingAsApObject: "Приём с других сайтов" ok: "Окей" gotIt: "Ясно!" cancel: "Отмена" +noThankYou: "Нет, спасибо" enterUsername: "Введите имя пользователя" renotedBy: "{user} делится" noNotes: "Нет ни одной заметки" @@ -47,6 +49,7 @@ deleteAndEdit: "Удалить и отредактировать" deleteAndEditConfirm: "Удалить эту заметку и создать отредактированную? Все реакции, ссылки и ответы на существующую будут будут потеряны." addToList: "Добавить в список" sendMessage: "Отправить сообщение" +copyRSS: "Скопировать RSS" copyUsername: "Скопировать имя пользователя" searchUser: "Поиск людей" reply: "Ответить" @@ -452,6 +455,7 @@ language: "Язык" uiLanguage: "Язык интерфейса" groupInvited: "Приглашение в группу" aboutX: "Описание {x}" +emojiStyle: "Стиль эмодзи" disableDrawer: "Не использовать выдвижные меню" youHaveNoGroups: "У вас нет ни одной группы" joinOrCreateGroup: "Получайте приглашения в группы или создавайте свои собственные" @@ -709,6 +713,7 @@ accentColor: "Акцент" textColor: "Текст" saveAs: "Сохранить под названием…" advanced: "Для продвинутых" +advancedSettings: "Расширенные настройки " value: "Значения" createdAt: "Создано" updatedAt: "Обновлено" @@ -840,34 +845,254 @@ numberOfColumn: "Количество столбцов" searchByGoogle: "Поиск" instanceDefaultLightTheme: "Светлая тема по умолчанию" instanceDefaultDarkTheme: "Темная тема по умолчанию" +instanceDefaultThemeDescription: "Описание темы по умолчанию для инстанса" mutePeriod: "Продолжительность скрытия" indefinitely: "вечно" tenMinutes: "10 минут" oneHour: "1 час" oneDay: "1 день" oneWeek: "1 неделя" +reflectMayTakeTime: "Изменения могут занять время для отображения" +failedToFetchAccountInformation: "Не удалось получить информацию об аккаунте" cropImage: "Кадрирование" cropImageAsk: "Нужно ли кадрировать изображение?" file: "Файлы" recentNHours: "Последние {n} ч" recentNDays: "Последние {n} сут" +noEmailServerWarning: "Почтовый сервер не установлен " +thereIsUnresolvedAbuseReportWarning: "Остались нерешённые жалобы" recommended: "Рекомендуем" check: "Проверить" driveCapOverrideLabel: "Изменение лимита дискового пространства для этого пользователя" +driveCapOverrideCaption: "Укажите меньше или равное нулю для отмены" +requireAdminForView: "Для просмотра необходимо иметь аккаунт администратора" +isSystemAccount: "Данная учётная запись создана автоматически и управляется системой" +typeToConfirm: "Введите {x} для продолжения" deleteAccount: "Удаление учётной записи" +document: "Документ" +numberOfPageCache: "Количество сохранённых страниц в кэше" +numberOfPageCacheDescription: "Описание количества страниц в кэше" +logoutConfirm: "Вы хотите выйти из аккаунта?" +lastActiveDate: "Последняя дата использования" +statusbar: "Статусбар" +pleaseSelect: "Пожалуйста, выберите" reverse: "Переворот" colored: "Выделена цветом" +refreshInterval: "Интервал перезагрузки" label: "Метка" +type: "Тип" +speed: "Скорость" +sensitiveMediaDetection: "Определение содержимого деликатного характера" localOnly: "Локально" +remoteOnly: "Только удалённо" +failedToUpload: "Сбой выгрузки" +cannotUploadBecauseInappropriate: "Файл не может быть загружен, так как было установлено, что он может содержать неприемлемое содержимое." +cannotUploadBecauseNoFreeSpace: "Файл не может быть загружен, так как не осталось места на диске" beta: "Бета" enableAutoSensitive: "Автоматическое определение NSFW" enableAutoSensitiveDescription: "Если доступно, используйте машинное обучение для автоматической установки флага NSFW на носителе. Даже если эта функция отключена, она может быть установлена автоматически в зависимости от инстанта." account: "Учётные записи" windowMaximize: "Развернуть" windowRestore: "Восстановить" +loggedInAsBot: "Вы под аккаунтом бота!" like: "Нравится!" +unlike: "Отменить «нравится»" show: "Отображение" +pleaseDonate: "Сайт {host} работает на Misskey. Это бесплатное программное обеспечение, и ваши пожертвования очень бы помогли продолжать его разработку!" color: "Цвет" +_achievements: + _types: + _notes1: + title: "Первые шаги в Misskey" + description: "Опубликована первая заметка" + _notes10: + title: "Несколько заметок" + description: "Опубликовано 10 заметок" + _notes100: + title: "Много заметок" + description: "Опубликовано 100 заметок" + _notes500: + title: "Всё в заметках" + description: "Опубликовано 500 заметок" + _notes1000: + title: "Гора заметок" + description: "Опубликовано 1000 заметок" + _notes5000: + title: "Заметки льются рекой" + description: "Опубликовано 5000 заметок" + _notes10000: + title: "Превосходство в заметках" + description: "Опубликовано 10 000 заметок" + _notes20000: + title: "Нужно больше заметок!" + description: "Опубликовано 20 000 заметок" + _notes30000: + title: "Заметки, заметки, заметки" + description: "Опубликовано 30 000 заметок" + _notes40000: + title: "Фабрика заметок" + description: "Опубликовано 40 000 заметок" + _notes50000: + title: "Планета заметок" + description: "Опубликовано 50 000 заметок" + _notes60000: + title: "Замет-квазар" + description: "Опубликовано 60 000 заметок" + _notes70000: + title: "Чёрная дыра из заметок" + description: "Опубликовано 70 000 заметок" + _notes80000: + title: "Галактика заметок" + description: "Опубликовано 80 000 заметок" + _notes90000: + title: "Вселенная заметок" + description: "Опубликовано 90 000 заметок" + _notes100000: + title: "ALL YOUR NOTE ARE BELONG TO US" + description: "Опубликовано 100 000 заметок" + _login3: + title: "Новичок Ⅰ" + description: "3 дня на сайте" + _login7: + title: "Новичок Ⅱ" + description: "Неделя на сайте" + flavor: "Кажется, вы начали свыкаться с этим, нет?" + _login15: + title: "Новичок Ⅲ" + description: "15 дней на сайте" + _login30: + title: "Мискиец Ⅰ" + description: "30 дней на сайте" + _login60: + title: "Мискиец Ⅱ" + description: "60 дней на сайте" + _login100: + title: "Мискиец Ⅲ" + description: "100 дней на сайте" + _login200: + title: "Завсегдатай Ⅰ" + description: "200 дней на сайте" + _login300: + title: "Завсегдатай Ⅱ" + description: "300 дней на сайте" + _login400: + title: "Завсегдатай Ⅲ" + description: "400 дней на сайте" + _login500: + title: "Ветеран Ⅰ" + description: "500 дней на сайте" + _login600: + title: "Ветеран Ⅱ" + description: "600 дней на сайте" + _login700: + title: "Ветеран Ⅲ" + description: "700 дней на сайте" + _login800: + title: "Повелитель заметок Ⅰ" + description: "800 дней на сайте" + _login900: + title: "Повелитель заметок Ⅱ" + description: "900 дней на сайте" + _login1000: + title: "Повелитель заметок Ⅲ" + description: "1000 дней на сайте" + _noteClipped1: + title: "Нельзя не сохранить" + description: "Первая заметка в подборке" + _noteFavorited1: + title: "Смотрящий на звёзды" + description: "Первое добавление в избранное" + _profileFilled: + title: "Приготовления закончены" + description: "Заполнен профиль" + _markedAsCat: + description: "Включена опция «Аккаунт кота»" + _followers50: + title: "Один за другим" + description: "Подписчиков больше 50" + _followers100: + title: "Всеобщий любимец" + description: "Подписчиков больше 100" + _followers300: + title: "В очередь!" + description: "Подписчиков больше 300" + _followers500: + title: "Радиостанция" + description: "Подписчиков больше 300" + _followers1000: + title: "Авторитет" + description: "Подписчиков больше 1000" + _collectAchievements30: + title: "Достигатор" + description: "Получено 30 достижений" + _viewAchievements3min: + title: "Любовь к успехам" + description: "Более 3 минут любования достижениями" + _iLoveMisskey: + title: "Я люблю Misskey" + description: "Написана заметка «I ❤ #Misskey»" + _client30min: + title: "Перерыв на обед" + description: "Прошло 30 минут с момента запуска клиента" + _noteDeletedWithin1min: + title: "Ой, нет!" + description: "Заметка удалена через минуту после публикации" + _postedAtLateNight: + title: "Житель ночи" + description: "Заметка опубликована в глухую ночь" + _postedAt0min0sec: + title: "Говорящие часы" + description: "Заметка опубликована ровно в 0 минут 0 секунд" + flavor: "Дин-дон дин-дон" + _selfQuote: + title: "Самовоспроизведение" + description: "Процитирована собственная заметка" + _htl20npm: + title: "В потоке" + description: "Достигнута скорость домашней ленты в 20 з/мин (заметок минуту)" + _outputHelloWorldOnScratchpad: + title: "Привет, мир!" + description: "Выведен текст «hello world» в Когтеточке" + _open3windows: + title: "Многооконный" + description: "Открыто одновременно 3 окна" + _driveFolderCircularReference: + description: "Попытка создать на «диске» рекурсивно вложенную папку" + _reactWithoutRead: + title: "Не читай @ отвечай!" + description: "На заметку более чем 100 знаков написан ответ в первые же 3 секунды с её появления." + _clickedClickHere: + title: "Нажмите здесь" + description: "Нажато здесь" + _justPlainLucky: + title: "Чистая удача" + description: "Может достаться с вероятностью 0,01% каждые 10 секунд." + _setNameToSyuilo: + title: "Комплекс бога" + description: "Установлено «syuilo» в качестве имени" + _passedSinceAccountCreated1: + title: "Первая годовщина" + description: "Прошёл 1 год с момента регистрации" + _passedSinceAccountCreated2: + title: "Вторая годовщина" + description: "Прошло 2 года с момента регистрации" + _passedSinceAccountCreated3: + title: "Третья годовщина" + description: "Прошло 3 года с момента регистрации" + _loggedInOnBirthday: + title: "С днём рождения!" + description: "Вход на сайт в свой день рождения" + _loggedInOnNewYearsDay: + title: "С Новым годом!" + description: "Вход на сайт в первый день года" + _cookieClicked: + title: "Игра, в которой вы щёлкаете по печенькам" + description: "Нажато печенье" + flavor: "Стоп, вы вообще на том сайте-то?" + _brainDiver: + title: "Brain Diver" + description: "Опубликована ссылка на песню «Brain Diver»" + flavor: "Мисски-Мисски Ла-Ту-Ма" _role: priority: "Приоритет" _priority: diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index 817fc69462..33127264b6 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -938,6 +938,82 @@ cannotPerformTemporary: "暂时不可用" cannotPerformTemporaryDescription: "因操作过于频繁,暂时不可用,请稍后再试。" preset: "預設值" selectFromPresets: "從預設值中選擇" +achievements: "成就" +_achievements: + earnedAt: "达成时间" + _types: + _notes1: + title: "初来乍到" + description: "第一次发帖" + flavor: "祝您在Misskey玩的愉快~" + _notes10: + title: "一些帖子" + description: "发布了10篇帖子" + _notes100: + title: "很多帖子" + description: "发布了100篇帖子" + _notes500: + title: "满是帖子" + description: "发布了500篇帖子" + _notes1000: + title: "帖子成山" + description: "发布了1,000篇帖子" + _notes5000: + title: "帖如泉涌" + description: "发布了5,000篇帖子" + _notes10000: + title: "超级帖" + description: "发布了10,000篇帖子" + _notes20000: + title: "还想要更多帖子" + description: "发布了20,000篇帖子" + _notes30000: + title: "帖子帖子帖子" + description: "发布了30,000篇帖子" + _notes40000: + title: "帖子工厂" + description: "发布了40,000篇帖子" + _notes50000: + title: "帖子星球" + description: "发布了50,000篇帖子" + _notes60000: + title: "帖子类星体" + description: "发布了60,000篇帖子" + _notes70000: + title: "帖子黑洞" + description: "发布了70,000篇帖子" + _notes80000: + title: "帖子星系" + description: "发布了80,000篇帖子" + _notes90000: + title: "帖子起源" + description: "发布了90,000篇帖子" + _notes100000: + title: "ALL YOUR NOTE ARE BELONG TO US" + description: "发布了100,000篇帖子" + flavor: "真的有那么多可以写的东西吗?" + _login1000: + flavor: "感谢您使用Misskey!" + _noteFavorited1: + title: "观星者" + _markedAsCat: + title: "我是猫" + _following50: + title: "我的朋友很多" + _viewAchievements3min: + description: "盯着成就看三分钟" + _iLoveMisskey: + title: "I Love Misskey" + description: "发布\"I ❤ #Misskey\"帖子" + flavor: "感谢您使用 Misskey ! by 开发团队" + _outputHelloWorldOnScratchpad: + title: "Hello, world!" + _loggedInOnBirthday: + title: "生日快乐" + description: "在生日当天登录" + _loggedInOnNewYearsDay: + title: "恭贺新禧" + description: "在元旦登入" _role: new: "创建角色" edit: "编辑角色" @@ -1587,6 +1663,7 @@ _notification: pollEnded: "问卷调查结果已生成。" unreadAntennaNote: "天线 {name}" emptyPushNotificationMessage: "推送通知已更新" + achievementEarned: "获得成就" _types: all: "全部" follow: "关注中" diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml index 025a4c3e5d..b88cfd91ae 100644 --- a/locales/zh-TW.yml +++ b/locales/zh-TW.yml @@ -989,11 +989,115 @@ _achievements: title: "貼文宇宙" description: "發表了90000則貼文" _notes100000: + title: "ALL YOUR NOTE ARE BELONG TO US" description: "發表了100,000則貼文" flavor: "有這麼多東西要寫嗎?" _login3: - title: "初學者 I" + title: "初學者Ⅰ" description: "總登入天數為3天" + flavor: "從今天開始,我就是Misskeyist" + _login7: + title: "初學者ⅠⅠ" + description: "總登入天數為7天" + flavor: "您開始習慣了嗎?" + _login15: + title: "初學者III" + description: "總登入天數為15天" + _login30: + title: "Misskeyist Ⅰ" + description: "總登入天數為30天" + _login60: + title: "Misskeyist ⅠⅠ" + description: "總登入天數為60天" + _login100: + title: "Misskeyist ⅠⅠⅠ" + description: "總登入天數為100天" + flavor: "辣個 Misskeyist 用戶" + _login200: + title: "普通Ⅰ" + description: "總登入天數為200天" + _login300: + title: "普通IⅠ" + description: "總登入天數為300天" + _login400: + title: "普通IIⅠ" + description: "總登入天數為400天" + _login500: + title: "老兵Ⅰ" + description: "總登入天數為500天" + flavor: "諸君,我喜歡貼文" + _login600: + title: "老兵ⅠⅠ" + description: "總登入天數為600天" + _login700: + title: "老兵ⅠⅠⅠ" + description: "總登入天數為700天" + _login800: + title: "貼文大師Ⅰ" + description: "總登入天數為800天" + _login900: + title: "貼文大師ⅠⅠ" + description: "總登入天數為900天" + _login1000: + title: "貼文大師ⅠⅠⅠ" + description: "總登入天數為1,000天" + flavor: "感謝您使用Misskey!" + _followers500: + title: "基站" + description: "超過500名追隨者" + _followers1000: + title: "影響者" + description: "超過1000名追隨者" + _collectAchievements30: + title: "成就收藏家" + description: "獲得30個以上的成就" + _viewAchievements3min: + title: "喜愛成就" + description: "看成就列表要花了3分鐘以上" + _iLoveMisskey: + title: "I Love Misskey" + description: "發布「I ❤ #Misskey」" + flavor: "感謝您使用Misskey! by 開發團隊" + _client30min: + title: "休息一下" + description: "用戶端啟動已超過30分鐘" + _noteDeletedWithin1min: + title: "現在沒有" + description: "發文後1分鐘內刪文" + _postedAtLateNight: + title: "夜行性" + description: "在深夜發佈貼文" + flavor: "該去睡覺了。" + _postedAt0min0sec: + title: "報時" + description: "在0分0秒發佈貼文" + _selfQuote: + title: "自我引用" + description: "引用了自己的貼文" + _htl20npm: + title: "流動的TL" + description: "在首頁時間軸的流速超過20npm" + _outputHelloWorldOnScratchpad: + title: "Hello world!" + description: "在暫存記憶體輸出了 hello world" + _open3windows: + title: "多重視窗" + description: "開啟3個以上的視窗" + _driveFolderCircularReference: + title: "循環引用" + description: "試圖遞迴套入雲端硬碟資料夾" + _reactWithoutRead: + title: "有好好讀過嗎?" + description: "對包含100字以上內容的貼文做出情感反應" + _clickedClickHere: + title: "點擊這裡" + description: "已點擊這裡了" + _justPlainLucky: + title: "只是運氣好" + description: "每10秒有0.01%的機率獲得" + _setNameToSyuilo: + title: "神的情結" + description: "將名稱設定為 syuilo" _role: new: "建立角色" edit: "編輯角色" From 7800a12e526f839ce3e0b5248e521b67cb51d347 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 22 Jan 2023 04:28:43 +0900 Subject: [PATCH 48/70] 13.1.1 --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ca8379bcfd..1841be3fb3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,15 @@ You should also include the user name that made the change. --> +## 13.1.1 (2023/01/22) + +### Improvements +- ローカルのカスタム絵文字を表示する際のパフォーマンスを改善 +- Client: 瞬間的に大量の実績を解除した際の挙動を改善 + +### Bugfixes +- Client: アップデート時にローカリゼーションデータが更新されないことがあるのを修正 + ## 13.1.0 (2023/01/21) ### Improvements From 363d727c55c94edb3a6407693ecb1753e63b0798 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 22 Jan 2023 05:33:26 +0900 Subject: [PATCH 49/70] refactor --- packages/frontend/src/components/global/MkEmoji.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/src/components/global/MkEmoji.vue b/packages/frontend/src/components/global/MkEmoji.vue index aaad81c656..a6594a5262 100644 --- a/packages/frontend/src/components/global/MkEmoji.vue +++ b/packages/frontend/src/components/global/MkEmoji.vue @@ -35,7 +35,7 @@ const url = computed(() => { const found = customEmojis.find(x => x.name === customEmojiName); return found ? found.url : null; } else { - const rawUrl = props.host ? `/emoji/${customEmojiName}@${props.host}.webp` : `/emoji/${customEmojiName}.webp`; + const rawUrl = `/emoji/${customEmojiName}@${props.host}.webp`; return defaultStore.state.disableShowingAnimatedImages ? getStaticImageUrl(rawUrl) : rawUrl; From 957eff0e63771427f3756cf6aa536a4c1cc0a426 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 22 Jan 2023 05:39:33 +0900 Subject: [PATCH 50/70] =?UTF-8?q?fix(client):=20=E3=83=AA=E3=82=A2?= =?UTF-8?q?=E3=82=AF=E3=82=B7=E3=83=A7=E3=83=B3=E3=81=AE=E3=82=AB=E3=82=B9?= =?UTF-8?q?=E3=82=BF=E3=83=A0=E7=B5=B5=E6=96=87=E5=AD=97=E3=81=8C=E8=A1=A8?= =?UTF-8?q?=E7=A4=BA=E3=81=95=E3=82=8C=E3=81=AA=E3=81=84=E3=81=AE=E3=82=92?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix #9682 --- packages/frontend/src/components/global/MkEmoji.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/src/components/global/MkEmoji.vue b/packages/frontend/src/components/global/MkEmoji.vue index a6594a5262..f16e25624f 100644 --- a/packages/frontend/src/components/global/MkEmoji.vue +++ b/packages/frontend/src/components/global/MkEmoji.vue @@ -25,7 +25,7 @@ const props = defineProps<{ const char2path = defaultStore.state.emojiStyle === 'twemoji' ? char2twemojiFilePath : char2fluentEmojiFilePath; const isCustom = computed(() => props.emoji.startsWith(':')); -const customEmojiName = props.emoji.substr(1, props.emoji.length - 2); +const customEmojiName = props.emoji.substr(1, props.emoji.length - 2).replace('@.', ''); const char = computed(() => isCustom.value ? undefined : props.emoji); const useOsNativeEmojis = computed(() => defaultStore.state.emojiStyle === 'native' && !props.isReaction); const url = computed(() => { From ccb22539e1f90d2eb7e380fcdb8c69639d381c63 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 22 Jan 2023 05:40:48 +0900 Subject: [PATCH 51/70] 13.1.2 --- CHANGELOG.md | 5 +++++ package.json | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1841be3fb3..b016986872 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,11 @@ You should also include the user name that made the change. --> +## 13.1.2 (2023/01/22) + +### Bugfixes +- Client: リアクションのカスタム絵文字の表示の問題を修正 + ## 13.1.1 (2023/01/22) ### Improvements diff --git a/package.json b/package.json index d30ea8eea3..435ef58a3a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "13.1.0", + "version": "13.1.2", "codename": "nasubi", "repository": { "type": "git", From aa339be2ab83d447b925ca956ed3fda20abcdaa9 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 22 Jan 2023 07:04:20 +0900 Subject: [PATCH 52/70] =?UTF-8?q?fix(client):=20=E3=83=AA=E3=82=A2?= =?UTF-8?q?=E3=82=AF=E3=82=B7=E3=83=A7=E3=83=B3=E3=81=AE=E3=82=AB=E3=82=B9?= =?UTF-8?q?=E3=82=BF=E3=83=A0=E7=B5=B5=E6=96=87=E5=AD=97=E3=81=8C=E8=A1=A8?= =?UTF-8?q?=E7=A4=BA=E3=81=95=E3=82=8C=E3=81=AA=E3=81=84=E3=81=93=E3=81=A8?= =?UTF-8?q?=E3=81=8C=E3=81=82=E3=82=8B=E3=81=AE=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 #9683 --- packages/frontend/src/components/global/MkEmoji.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/frontend/src/components/global/MkEmoji.vue b/packages/frontend/src/components/global/MkEmoji.vue index f16e25624f..93f50da20e 100644 --- a/packages/frontend/src/components/global/MkEmoji.vue +++ b/packages/frontend/src/components/global/MkEmoji.vue @@ -31,11 +31,11 @@ const useOsNativeEmojis = computed(() => defaultStore.state.emojiStyle === 'nati const url = computed(() => { if (char.value) { return char2path(char.value); - } else if (props.host == null) { + } else if (props.host == null && !customEmojiName.includes('@')) { const found = customEmojis.find(x => x.name === customEmojiName); return found ? found.url : null; } else { - const rawUrl = `/emoji/${customEmojiName}@${props.host}.webp`; + const rawUrl = props.host ? `/emoji/${customEmojiName}@${props.host}.webp` : `/emoji/${customEmojiName}.webp`; return defaultStore.state.disableShowingAnimatedImages ? getStaticImageUrl(rawUrl) : rawUrl; From 7d7167df6dfb7673bfd90534c79ca895117ce44e Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 22 Jan 2023 07:04:30 +0900 Subject: [PATCH 53/70] New Crowdin updates (#9680) * New translations ja-JP.yml (Russian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Russian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Russian) --- locales/it-IT.yml | 158 +++++++++++++++++++++++++++++++++++++++++++++- locales/ru-RU.yml | 57 +++++++++++++++-- 2 files changed, 209 insertions(+), 6 deletions(-) diff --git a/locales/it-IT.yml b/locales/it-IT.yml index e215b9f79e..cc93891bcb 100644 --- a/locales/it-IT.yml +++ b/locales/it-IT.yml @@ -938,6 +938,161 @@ cannotPerformTemporary: "Indisponibilità temporanea" cannotPerformTemporaryDescription: "L'attività non può essere svolta, poiché si è raggiunto il limite di esecuzioni possibili. Per favore, riprova più tardi." preset: "Preimpostato" selectFromPresets: "Seleziona preimpostato" +achievements: "Obiettivi raggiunti" +_achievements: + earnedAt: "Data di conseguimento" + _types: + _notes1: + title: "Ho iniziato a usare Misskey" + description: "Ho pubblicato la mia prima Nota" + flavor: "Goditi la vita su Misskey!" + _notes10: + title: "Alcune Note" + description: "Ho inserito 10 Note" + _notes100: + title: "Un po' di Note" + description: "Ho inserito 100 Note" + _notes500: + title: "Un bel po' di Note" + description: "Ho inserito 500 Note" + _notes1000: + title: "Una montagna di Note" + description: "Ho inserito 1.000 Note" + _notes5000: + title: "Un sovraccarico di Note!" + description: "Ho inserito 5.000 Note" + _notes10000: + title: "SuperNote!" + description: "Ho inserito 10.000 Note" + _notes20000: + title: "Voglio più... Note!" + description: "Ho inserito 20.000 Note" + _notes30000: + title: "Note, Note, Note!" + description: "Ho inserito 30.000 Note" + _notes40000: + title: "Una fabbrica di Note" + description: "Ho inserito 40.000 Note" + _notes50000: + title: "Un pianeta di Note" + description: "Ho inserito 50.000 Note" + _notes60000: + title: "Un quasar di Note" + description: "Ho inserito 60.000 Note" + _notes70000: + title: "Un buco nero supermassiccio di Note" + description: "Ho inserito 70.000 Note" + _notes80000: + title: "Una galassia di Note" + description: "Ho inserito 80.000 Note" + _notes90000: + title: "Un universo di Note!" + description: "Ho inserito 90.000 Note" + _notes100000: + title: "ALL YOUR NOTE ARE BELONG TO US" + description: "Ho inserito 100.000 Note" + flavor: "Hai molto da scrivere?" + _login3: + title: "Principiante I" + description: "Accedi per 3 giorni di fila" + flavor: "Da oggi, chiamatemi Misskist" + _login7: + title: "Principiante II" + description: "Accedi per 7 giorni di fila" + flavor: "Ti sembra di avere la situazione sotto controllo?" + _login15: + title: "Principiante III" + description: "Accedi per 15 giorni di fila" + _login30: + title: "Misskist I" + description: "Accedi per 30 giorni di fila" + _login60: + title: "Misskeist II" + description: "Accedi per 60 giorni di fila" + _login100: + title: "Misskeist III" + description: "Accedi per 100 giorni di fila" + flavor: "Violent Misskeist" + _login200: + title: "Regolare I" + description: "Accedi per 200 giorni totali" + _login300: + title: "Regolare II" + description: "Accedi per un totale di 300 giorni" + _login400: + title: "Regolare III" + description: "Accedi per un totale di 400 giorni" + _login500: + title: "Professionista I" + description: "Accedi per un totale di 500 giorni" + flavor: "Amici cari, mi piacciono le Note" + _login600: + title: "Professionista II" + description: "Accedi per un totale di 600 giorni" + _login700: + title: "Professionista III" + description: "Accedi per un totale di 700 giorni" + _login800: + title: "Maestro di Note I" + description: "Accedi per un totale di 800 giorni" + _login900: + title: "Maestro di Note II" + description: "Accedi per un totale di 900 giorni" + _login1000: + title: "Maestro di Note III" + description: "Accedi per un totale di 1.000 giorni" + flavor: "Grazie per aver usato Misskey!" + _noteClipped1: + title: "Devo clippare!" + description: "Ho raccolto in Clip la prima Nota" + _noteFavorited1: + title: "Guarda le stelle" + description: "Aggiungi una Nota ai preferiti per la prima volta" + _profileFilled: + title: "Perfettamente" + description: "Imposta il tuo profilo" + _markedAsCat: + title: "Io sono un gatto" + description: "Aggiungi le orecchie da gatto al tuo profilo" + flavor: "Ti chiamerò..." + _following1: + title: "Hai seguito il tuo primo profilo" + description: "Il tuo primo profilo Follower" + _following10: + title: "Segui, segui!" + description: "Hai seguito 10 profili" + _following50: + title: "Tanti amici" + description: "Hai seguito 50 profili" + _following100: + title: "Cento amici" + description: "Hai seguito 100 profili" + _following300: + title: "Sovraccarico di amici" + description: "Hai seguito 300 profili" + _followers1: + title: "Primo Follower" + description: "Hai ottenuto un Follower" + _followers10: + title: "Follow me!" + description: "Hai ottenuto 10 Follower" + _followers50: + title: "Follower a frotte" + description: "Hai ottenuto 50 Follower" + _followers100: + title: "Popolare" + description: "Hai ottenuto 100 Follower" + _followers300: + title: "Mettetevi in fila" + description: "Hai ottenuto 300 Follower" + _followers500: + title: "Trasmettitore" + description: "Hai ottenuto 500 Follower" + _followers1000: + title: "Influenzer" + _brainDiver: + title: "Brain Diver" + description: "Pubblica un link a Brain Diver" _role: new: "Nuovo ruolo" edit: "Modifica ruolo" @@ -1293,7 +1448,7 @@ _tutorial: step3_1: "Hai finito di impostare il tuo profilo?" step3_2: "Ora puoi pubblicare una «Nota». Proviamo subito! Premi il bottone con l'icona «penna» per iniziare a scrivere in una finestra di dialogo. " step3_3: "Scritto il testo della nota, puoi pubblicarla premendo il pulsante nella parte superiore destra della finestra di dialogo." - step3_4: "Non ti viene niente in mente? Perché non scrivi semplicemente \"Ho appena cominciato a usare Misskey\"?" + step3_4: "Non ti viene niente in mente? Perché non scrivi semplicemente \"Ho appena iniziato a usare Misskey\"?" step4_1: "Hai pubblicato qualcosa?" step4_2: "Se puoi visualizzare la tua nota sulla timeline, ce l'hai fatta!" step5_1: "Adesso, cerca di seguire altre persone per vivacizzare la tua timeline. " @@ -1587,6 +1742,7 @@ _notification: pollEnded: "Risultati del sondaggio." unreadAntennaNote: "Antenna {name}" emptyPushNotificationMessage: "Le notifiche push sono state aggiornate." + achievementEarned: "Obiettivo raggiunto" _types: all: "Tutto" follow: "Novità follower" diff --git a/locales/ru-RU.yml b/locales/ru-RU.yml index 133169e8da..f22a093cab 100644 --- a/locales/ru-RU.yml +++ b/locales/ru-RU.yml @@ -899,12 +899,16 @@ like: "Нравится!" unlike: "Отменить «нравится»" show: "Отображение" pleaseDonate: "Сайт {host} работает на Misskey. Это бесплатное программное обеспечение, и ваши пожертвования очень бы помогли продолжать его разработку!" +roles: "Роли" +role: "Роль" color: "Цвет" +achievements: "Достижения" _achievements: _types: _notes1: title: "Первые шаги в Misskey" description: "Опубликована первая заметка" + flavor: "Приятных дней с Misskey!" _notes10: title: "Несколько заметок" description: "Опубликовано 10 заметок" @@ -950,9 +954,11 @@ _achievements: _notes100000: title: "ALL YOUR NOTE ARE BELONG TO US" description: "Опубликовано 100 000 заметок" + flavor: "Вам правда нужно столько писать?" _login3: title: "Новичок Ⅰ" description: "3 дня на сайте" + flavor: "С сегодняшнего дня зовите меня просто мискиец" _login7: title: "Новичок Ⅱ" description: "Неделя на сайте" @@ -981,6 +987,7 @@ _achievements: _login500: title: "Ветеран Ⅰ" description: "500 дней на сайте" + flavor: "Господа, я люблю заметки" _login600: title: "Ветеран Ⅱ" description: "600 дней на сайте" @@ -996,6 +1003,7 @@ _achievements: _login1000: title: "Повелитель заметок Ⅲ" description: "1000 дней на сайте" + flavor: "Спасибо, что пользуетесь Misskey!" _noteClipped1: title: "Нельзя не сохранить" description: "Первая заметка в подборке" @@ -1006,22 +1014,45 @@ _achievements: title: "Приготовления закончены" description: "Заполнен профиль" _markedAsCat: + title: "Ваш покорный слуга кот" description: "Включена опция «Аккаунт кота»" + flavor: "Позвольте представиться: я — кот, просто кот, у меня еще нет имени." + _following1: + title: "Я не один" + description: "Сделана первая подписка" + _following10: + title: "Не останавливайся… Не останавливайся…" + description: "Количество подписок достигло 10" + _following50: + title: "Много друзей" + description: "Количество подписок достигло 50" + _following100: + title: "Сотня друзей" + description: "Количество подписок достигло 100" + _following300: + title: "Друзья в избытке" + description: "Количество подписок достигло 300" + _followers1: + title: "Первый подписчик" + description: "Появился 1 подписчик" + _followers10: + title: "Следуй за мной!" + description: "Количество подписчиков достигло 10" _followers50: title: "Один за другим" - description: "Подписчиков больше 50" + description: "Количество подписчиков достигло 50" _followers100: title: "Всеобщий любимец" - description: "Подписчиков больше 100" + description: "Количество подписчиков достигло 100" _followers300: title: "В очередь!" - description: "Подписчиков больше 300" + description: "Количество подписчиков достигло 300" _followers500: title: "Радиостанция" - description: "Подписчиков больше 300" + description: "Количество подписчиков достигло 500" _followers1000: title: "Авторитет" - description: "Подписчиков больше 1000" + description: "Количество подписчиков достигло 1000" _collectAchievements30: title: "Достигатор" description: "Получено 30 достижений" @@ -1031,6 +1062,7 @@ _achievements: _iLoveMisskey: title: "Я люблю Misskey" description: "Написана заметка «I ❤ #Misskey»" + flavor: "Спасибо за поддержку Misskey! Ваша команда разработчиков" _client30min: title: "Перерыв на обед" description: "Прошло 30 минут с момента запуска клиента" @@ -1040,6 +1072,7 @@ _achievements: _postedAtLateNight: title: "Житель ночи" description: "Заметка опубликована в глухую ночь" + flavor: "Вроде бы пора спать" _postedAt0min0sec: title: "Говорящие часы" description: "Заметка опубликована ровно в 0 минут 0 секунд" @@ -1057,6 +1090,7 @@ _achievements: title: "Многооконный" description: "Открыто одновременно 3 окна" _driveFolderCircularReference: + title: "Циклическая ссылка" description: "Попытка создать на «диске» рекурсивно вложенную папку" _reactWithoutRead: title: "Не читай @ отвечай!" @@ -1085,6 +1119,7 @@ _achievements: _loggedInOnNewYearsDay: title: "С Новым годом!" description: "Вход на сайт в первый день года" + flavor: "Желаем отличного года на нашем сайте!" _cookieClicked: title: "Игра, в которой вы щёлкаете по печенькам" description: "Нажато печенье" @@ -1094,6 +1129,17 @@ _achievements: description: "Опубликована ссылка на песню «Brain Diver»" flavor: "Мисски-Мисски Ла-Ту-Ма" _role: + new: "Новая роль" + name: "Название роли" + description: "Описание роли" + permission: "Ролевые полномочия" + assignTarget: "Метод присвоения" + manual: "Вручную" + conditional: "По условию" + isPublic: "Общедоступная роль" + descriptionOfIsPublic: "Список тех, кому назначена эта роль будет доступен всем. Кроме того эта роль будет отмечена у каждого в профиле." + canEditMembersByModerator: "Могут назначать модераторы" + descriptionOfCanEditMembersByModerator: "Если включено, на эту роль могут назначать пользователей как администраторы, так и модераторы. Если выключено, назначать могут только администраторы." priority: "Приоритет" _priority: low: "Низкий" @@ -1641,6 +1687,7 @@ _notification: youReceivedFollowRequest: "У вас новый запрос на подписку." yourFollowRequestAccepted: "Ваш запрос на подписку одобрен." youWereInvitedToGroup: "Вы приглашены в группу." + achievementEarned: "Получено достижение" _types: all: "Все" follow: "Подписки" From 7fdf298bd470ab4ed739f85a23ac3a07956777c1 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 22 Jan 2023 07:04:58 +0900 Subject: [PATCH 54/70] 13.1.3 --- CHANGELOG.md | 5 +++++ package.json | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b016986872..970a862cba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,11 @@ You should also include the user name that made the change. --> +## 13.1.3 (2023/01/22) + +### Bugfixes +- Client: リアクションのカスタム絵文字の表示の問題を修正 + ## 13.1.2 (2023/01/22) ### Bugfixes diff --git a/package.json b/package.json index 435ef58a3a..e48eea7d7b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "13.1.2", + "version": "13.1.3", "codename": "nasubi", "repository": { "type": "git", From 26ae2dfc0f494c377abd878c00044049fcd2bf37 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 22 Jan 2023 08:00:42 +0900 Subject: [PATCH 55/70] add achievement --- locales/ja-JP.yml | 3 +++ packages/backend/src/core/AchievementService.ts | 3 ++- .../src/server/api/endpoints/notes/favorites/create.ts | 6 ++++++ packages/frontend/src/scripts/achievements.ts | 6 ++++++ 4 files changed, 17 insertions(+), 1 deletion(-) diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 898ae01e72..57296b9857 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1049,6 +1049,9 @@ _achievements: _noteFavorited1: title: "星をみるひと" description: "初めてノートをお気に入りに登録した" + _myNoteFavorited1: + title: "星が欲しい" + description: "自分のノートが他の人からお気に入りに登録された" _profileFilled: title: "準備万端" description: "プロフィール設定を行った" diff --git a/packages/backend/src/core/AchievementService.ts b/packages/backend/src/core/AchievementService.ts index 26dd356d36..be763e4629 100644 --- a/packages/backend/src/core/AchievementService.ts +++ b/packages/backend/src/core/AchievementService.ts @@ -44,6 +44,7 @@ const ACHIEVEMENT_TYPES = [ 'loggedInOnNewYearsDay', 'noteClipped1', 'noteFavorited1', + 'myNoteFavorited1', 'profileFilled', 'markedAsCat', 'following1', @@ -94,7 +95,7 @@ export class AchievementService { @bindThis public async create( userId: User['id'], - type: string, + type: typeof ACHIEVEMENT_TYPES[number], ): Promise<void> { if (!ACHIEVEMENT_TYPES.includes(type)) return; diff --git a/packages/backend/src/server/api/endpoints/notes/favorites/create.ts b/packages/backend/src/server/api/endpoints/notes/favorites/create.ts index acf22a5ad4..e423f0f109 100644 --- a/packages/backend/src/server/api/endpoints/notes/favorites/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/favorites/create.ts @@ -6,6 +6,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import { GetterService } from '@/server/api/GetterService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; +import { AchievementService } from '@/core/AchievementService.js'; export const meta = { tags: ['notes', 'favorites'], @@ -51,6 +52,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { private idService: IdService, private getterService: GetterService, + private achievementService: AchievementService, ) { super(meta, paramDef, async (ps, me) => { // Get favoritee @@ -76,6 +78,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { noteId: note.id, userId: me.id, }); + + if (note.userHost == null) { + this.achievementService.create(note.userId, 'myNoteFavorited1'); + } }); } } diff --git a/packages/frontend/src/scripts/achievements.ts b/packages/frontend/src/scripts/achievements.ts index c97358e880..f511fce3ea 100644 --- a/packages/frontend/src/scripts/achievements.ts +++ b/packages/frontend/src/scripts/achievements.ts @@ -40,6 +40,7 @@ export const ACHIEVEMENT_TYPES = [ 'loggedInOnNewYearsDay', 'noteClipped1', 'noteFavorited1', + 'myNoteFavorited1', 'profileFilled', 'markedAsCat', 'following1', @@ -240,6 +241,11 @@ export const ACHIEVEMENT_BADGES = { bg: null, frame: 'bronze', }, + 'myNoteFavorited1': { + img: '/fluent-emoji/1f320.png', + bg: null, + frame: 'silver', + }, 'profileFilled': { img: '/fluent-emoji/1f44c.png', bg: 'linear-gradient(0deg, rgb(187 183 59), rgb(255 143 77))', From 9dacf11702dc9f71447bc15f1b98ab5532e46f27 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 22 Jan 2023 16:52:15 +0900 Subject: [PATCH 56/70] =?UTF-8?q?fix(client):=20=E3=83=AD=E3=83=BC?= =?UTF-8?q?=E3=82=AB=E3=83=AA=E3=82=BC=E3=83=BC=E3=82=B7=E3=83=A7=E3=83=B3?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=99=82=E3=81=AB=E3=83=AA=E3=83=AD=E3=83=BC?= =?UTF-8?q?=E3=83=89=E3=81=8C=E7=B9=B0=E3=82=8A=E8=BF=94=E3=81=95=E3=82=8C?= =?UTF-8?q?=E3=82=8B=E3=81=93=E3=81=A8=E3=81=8C=E3=81=82=E3=82=8B=E3=81=AE?= =?UTF-8?q?=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/config.ts | 6 +++++- packages/frontend/src/i18n.ts | 4 ++++ packages/frontend/src/init.ts | 11 +++++++---- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/packages/frontend/src/config.ts b/packages/frontend/src/config.ts index 4b084d365b..073b21a0ae 100644 --- a/packages/frontend/src/config.ts +++ b/packages/frontend/src/config.ts @@ -10,8 +10,12 @@ export const apiUrl = url + '/api'; export const wsUrl = url.replace('http://', 'ws://').replace('https://', 'wss://') + '/streaming'; export const lang = miLocalStorage.getItem('lang'); export const langs = _LANGS_; -export const locale = JSON.parse(miLocalStorage.getItem('locale')); +export let locale = JSON.parse(miLocalStorage.getItem('locale')); export const version = _VERSION_; export const instanceName = siteName === 'Misskey' ? host : siteName; export const ui = miLocalStorage.getItem('ui'); export const debug = miLocalStorage.getItem('debug') === 'true'; + +export function updateLocale(newLocale) { + locale = newLocale; +} diff --git a/packages/frontend/src/i18n.ts b/packages/frontend/src/i18n.ts index 31e066960d..220c6210c0 100644 --- a/packages/frontend/src/i18n.ts +++ b/packages/frontend/src/i18n.ts @@ -3,3 +3,7 @@ import { locale } from '@/config'; import { I18n } from '@/scripts/i18n'; export const i18n = markRaw(new I18n(locale)); + +export function updateI18n(newLocale) { + i18n.ts = newLocale; +} diff --git a/packages/frontend/src/init.ts b/packages/frontend/src/init.ts index 079003ee83..36897545e2 100644 --- a/packages/frontend/src/init.ts +++ b/packages/frontend/src/init.ts @@ -25,10 +25,10 @@ import JSON5 from 'json5'; import widgets from '@/widgets'; import directives from '@/directives'; import components from '@/components'; -import { version, ui, lang, host } from '@/config'; +import { version, ui, lang, host, updateLocale } from '@/config'; import { applyTheme } from '@/scripts/theme'; import { isDeviceDarkmode } from '@/scripts/is-device-darkmode'; -import { i18n } from '@/i18n'; +import { i18n, updateI18n } from '@/i18n'; import { confirm, alert, post, popup, toast } from '@/os'; import { stream } from '@/stream'; import * as sound from '@/scripts/sound'; @@ -87,9 +87,12 @@ import { fetchCustomEmojis } from './custom-emojis'; if (localeOutdated) { const res = await window.fetch(`/assets/locales/${lang}.${version}.json`); if (res.status === 200) { - miLocalStorage.setItem('locale', await res.text()); + const newLocale = await res.text(); + const parsedNewLocale = JSON.parse(newLocale); + miLocalStorage.setItem('locale', newLocale); miLocalStorage.setItem('localeVersion', version); - location.reload(); + updateLocale(parsedNewLocale); + updateI18n(parsedNewLocale); } } //#endregion From bd8b624bae31f2eea5a7c414f29aafdfeb96fa10 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 22 Jan 2023 17:04:51 +0900 Subject: [PATCH 57/70] tweak bootstrap of client --- packages/backend/src/server/web/boot.js | 78 ++++++++----------------- 1 file changed, 23 insertions(+), 55 deletions(-) diff --git a/packages/backend/src/server/web/boot.js b/packages/backend/src/server/web/boot.js index a4513696a1..3d6dabe571 100644 --- a/packages/backend/src/server/web/boot.js +++ b/packages/backend/src/server/web/boot.js @@ -22,18 +22,13 @@ renderError('SOMETHING_HAPPENED_IN_PROMISE', e); }; - const v = localStorage.getItem('v') || VERSION; - let forceError = localStorage.getItem('forceError'); if (forceError != null) { renderError('FORCED_ERROR', 'This error is forced by having forceError in local storage.') } //#region Detect language & fetch translations - const localeVersion = localStorage.getItem('localeVersion'); - const localeOutdated = (localeVersion == null || localeVersion !== v); - - if (!localStorage.hasOwnProperty('locale') || localeOutdated) { + if (!localStorage.hasOwnProperty('locale')) { const supportedLangs = LANGS; let lang = localStorage.getItem('lang'); if (lang == null || !supportedLangs.includes(lang)) { @@ -47,13 +42,31 @@ } } - const res = await window.fetch(`/assets/locales/${lang}.${v}.json`); - if (res.status === 200) { + const metaRes = await window.fetch('/api/meta', { + method: 'POST', + body: JSON.stringify({}), + credentials: 'omit', + cache: 'no-cache', + headers: { + 'Content-Type': 'application/json', + }, + }); + if (metaRes.status !== 200) { + renderError('META_FETCH'); + return; + } + const meta = await res.json(); + const v = meta.version; + if (v == null) { + renderError('META_FETCH_V'); + return; + } + const localRes = await window.fetch(`/assets/locales/${lang}.${v}.json`); + if (localRes.status === 200) { localStorage.setItem('lang', lang); - localStorage.setItem('locale', await res.text()); + localStorage.setItem('locale', await localRes.text()); localStorage.setItem('localeVersion', v); } else { - await checkUpdate(); renderError('LOCALE_FETCH'); return; } @@ -64,7 +77,6 @@ function importAppScript() { import(`/vite/${CLIENT_ENTRY}`) .catch(async e => { - await checkUpdate(); console.error(e); renderError('APP_IMPORT', e); }); @@ -291,48 +303,4 @@ } `) } - - // eslint-disable-next-line no-inner-declarations - async function checkUpdate() { - try { - const res = await window.fetch('/api/meta', { - method: 'POST', - cache: 'no-cache', - body: '{}', - headers: { - 'Content-Type': 'application/json', - }, - }); - - const meta = await res.json(); - - if (meta.version == null) { - throw new Error('failed to fetch instance metadata'); - } - - if (meta.version != v) { - localStorage.setItem('v', meta.version); - refresh(); - } - } catch (e) { - console.error(e); - renderError('UPDATE_CHECK', e); - throw e; - } - } - - // eslint-disable-next-line no-inner-declarations - function refresh() { - // Clear cache (service worker) - try { - navigator.serviceWorker.controller.postMessage('clear'); - navigator.serviceWorker.getRegistrations().then(registrations => { - registrations.forEach(registration => registration.unregister()); - }); - } catch (e) { - console.error(e); - } - - location.reload(); - } })(); From 492fb9a115fe10f6aa410f5efdf1f63bd6b43bd6 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 22 Jan 2023 17:06:18 +0900 Subject: [PATCH 58/70] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 970a862cba..feb2cb1f80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -65,6 +65,7 @@ You should also include the user name that made the change. - Node.js 18.x or later is required - PostgreSQL 15.x is required - Misskey not using 15 specific features at 13.0.0, but may do so in the future. + - Docker環境でPostgreSQLのアップデートを行う際のガイドはこちら: https://github.com/misskey-dev/misskey/pull/9641#issue-1536336620 - Elasticsearchのサポートが削除されました - 代わりに今後任意の検索プロバイダを設定できる仕組みを構想しています。その仕組みを使えば今まで通りElasticsearchも利用できます - Yarnからpnpmに移行されました From 468ec36830e99a7012d9a58c38cf18f81bd8d0f4 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 22 Jan 2023 17:09:26 +0900 Subject: [PATCH 59/70] New Crowdin updates (#9685) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (German) * New translations ja-JP.yml (English) * New translations ja-JP.yml (Chinese Simplified) * New translations ja-JP.yml (Russian) * New translations ja-JP.yml (Chinese Simplified) * New translations ja-JP.yml (Russian) * New translations ja-JP.yml (Chinese Traditional) --- locales/de-DE.yml | 3 ++ locales/en-US.yml | 3 ++ locales/it-IT.yml | 123 ++++++++++++++++++++++++++++++++++++---------- locales/ru-RU.yml | 50 +++++++++++++++++++ locales/zh-CN.yml | 31 ++++++++++++ locales/zh-TW.yml | 12 +++++ 6 files changed, 197 insertions(+), 25 deletions(-) diff --git a/locales/de-DE.yml b/locales/de-DE.yml index ecead98c32..e81f7579c2 100644 --- a/locales/de-DE.yml +++ b/locales/de-DE.yml @@ -1048,6 +1048,9 @@ _achievements: _noteFavorited1: title: "Sternengucker" description: "Eine Notiz als Favorit markiert" + _myNoteFavorited1: + title: "Sternensucher" + description: "Ein anderer Benutzer hat eine deiner Notizen als Favoriten markiert" _profileFilled: title: "Perfekte Vorbereitung" description: "Fülle dein Profil aus" diff --git a/locales/en-US.yml b/locales/en-US.yml index e398f1fd58..6a43d45697 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -1048,6 +1048,9 @@ _achievements: _noteFavorited1: title: "Stargazer" description: "Favorite your first note" + _myNoteFavorited1: + title: "Seeking Stars" + description: "Have somebody else favorite one of your notes" _profileFilled: title: "Well-prepared" description: "Set up your profile" diff --git a/locales/it-IT.yml b/locales/it-IT.yml index cc93891bcb..c5d89a0f55 100644 --- a/locales/it-IT.yml +++ b/locales/it-IT.yml @@ -1,7 +1,7 @@ --- _lang_: "Italiano" headlineMisskey: "Rete collegata tramite note" -introMisskey: "Eccoci! Misskey è un servizio di microblogging decentralizzato, libero e aperto. \n📡 Puoi pubblicare «Note» per condividere ciò che sta succedendo o per dire a tutti qualcosa su di te. \n👍 Puoi reagire inviando emoji rapidi alle «Note» provenienti da altri profili nel Fediverso.\n🚀 Esplora un nuovo mondo insieme a noi!" +introMisskey: "Eccoci! Misskey è un servizio di microblogging decentralizzato, libero e aperto. \n\n📡 Puoi pubblicare «Note» per condividere ciò che sta succedendo o per dire a tutti qualcosa su di te. \n\n👍 Puoi reagire inviando emoji rapidi alle «Note» provenienti da altri profili nel Fediverso.\n\n🚀 Esplora un nuovo mondo insieme a noi!" poweredByMisskeyDescription: "{name} è uno dei servizi (chiamati istanze) che utilizzano la piattaforma open source <b>Misskey</b>." monthAndDay: "{day}/{month}" search: "Cerca" @@ -943,79 +943,79 @@ _achievements: earnedAt: "Data di conseguimento" _types: _notes1: - title: "Ho iniziato a usare Misskey" - description: "Ho pubblicato la mia prima Nota" + title: "Hai iniziato a usare Misskey" + description: "Hai pubblicato la prima Nota" flavor: "Goditi la vita su Misskey!" _notes10: title: "Alcune Note" - description: "Ho inserito 10 Note" + description: "Hai inserito 10 Note" _notes100: title: "Un po' di Note" - description: "Ho inserito 100 Note" + description: "Hai inserito 100 Note" _notes500: title: "Un bel po' di Note" - description: "Ho inserito 500 Note" + description: "Hai inserito 500 Note" _notes1000: title: "Una montagna di Note" - description: "Ho inserito 1.000 Note" + description: "Hai inserito 1.000 Note" _notes5000: title: "Un sovraccarico di Note!" - description: "Ho inserito 5.000 Note" + description: "Hai inserito 5.000 Note" _notes10000: title: "SuperNote!" - description: "Ho inserito 10.000 Note" + description: "Hai inserito 10.000 Note" _notes20000: title: "Voglio più... Note!" - description: "Ho inserito 20.000 Note" + description: "Hai inserito 20.000 Note" _notes30000: title: "Note, Note, Note!" - description: "Ho inserito 30.000 Note" + description: "Hai inserito 30.000 Note" _notes40000: title: "Una fabbrica di Note" - description: "Ho inserito 40.000 Note" + description: "Hai inserito 40.000 Note" _notes50000: title: "Un pianeta di Note" - description: "Ho inserito 50.000 Note" + description: "Hai inserito 50.000 Note" _notes60000: title: "Un quasar di Note" - description: "Ho inserito 60.000 Note" + description: "Hai inserito 60.000 Note" _notes70000: title: "Un buco nero supermassiccio di Note" - description: "Ho inserito 70.000 Note" + description: "Hai inserito 70.000 Note" _notes80000: title: "Una galassia di Note" - description: "Ho inserito 80.000 Note" + description: "Hai inserito 80.000 Note" _notes90000: title: "Un universo di Note!" - description: "Ho inserito 90.000 Note" + description: "Hai inserito 90.000 Note" _notes100000: title: "ALL YOUR NOTE ARE BELONG TO US" - description: "Ho inserito 100.000 Note" + description: "Hai inserito 100.000 Note" flavor: "Hai molto da scrivere?" _login3: title: "Principiante I" - description: "Accedi per 3 giorni di fila" + description: "Accedi per un totale di 3 giorni" flavor: "Da oggi, chiamatemi Misskist" _login7: title: "Principiante II" - description: "Accedi per 7 giorni di fila" + description: "Accedi per un totale di 7 giorni" flavor: "Ti sembra di avere la situazione sotto controllo?" _login15: title: "Principiante III" - description: "Accedi per 15 giorni di fila" + description: "Accedi per un totale di 15 giorni" _login30: title: "Misskist I" - description: "Accedi per 30 giorni di fila" + description: "Accedi per un totale di 30 giorni" _login60: title: "Misskeist II" - description: "Accedi per 60 giorni di fila" + description: "Accedi per un totale di 60 giorni" _login100: title: "Misskeist III" - description: "Accedi per 100 giorni di fila" + description: "Accedi per un totale di 100 giorni" flavor: "Violent Misskeist" _login200: title: "Regolare I" - description: "Accedi per 200 giorni totali" + description: "Accedi per un totale di 200 giorni" _login300: title: "Regolare II" description: "Accedi per un totale di 300 giorni" @@ -1090,9 +1090,82 @@ _achievements: description: "Hai ottenuto 500 Follower" _followers1000: title: "Influenzer" + description: "Hai superato i 1.000 Follower" + _collectAchievements30: + title: "Collezionista di successi" + description: "Hai raggiunto 30 obiettivi" + _viewAchievements3min: + title: "Mi piacciono i risultati" + description: "Guarda la tua collezione di obiettivi per almeno 3 minuti" + _iLoveMisskey: + title: "I LOVE Misskey" + description: "Pubblica «I ♥ #Misskey»" + flavor: "Grazie per aver utilizzato Misskey! Dal team di sviluppo" + _client30min: + title: "Piccola pausa" + description: "Hai passato più di 30 minuti di fila su Misskey" + _noteDeletedWithin1min: + title: "Ooops!" + description: "Hai eliminato una nota entro un minuto dalla sua pubblicazione" + _postedAtLateNight: + title: "Biassanot!" + description: "Hai pubblicato una nota in tarda notte" + flavor: "Andiamo a dormire presto" + _postedAt0min0sec: + title: "Mezzanotte" + description: "Hai pubblicato una Nota a mezzanotte in punto" + flavor: "tic, tac, tic, tac! Gong!" + _selfQuote: + title: "Autoreferenziale" + description: "Hai citato una delle tue Note" + _htl20npm: + title: "Timeline scorrevole" + description: "La tua Timeline personale ha superato la velocità di 20 Note orarie (Note al minuto)" + _outputHelloWorldOnScratchpad: + title: "Hello, world!" + description: "Hai scritto «Hello world» nel blocco appunti" + _open3windows: + title: "Finestrato" + description: "Hai aperto almeno 3 finestre contemporaneamente" + _driveFolderCircularReference: + title: "Riferimento circolare" + description: "Hai provato a nidificare in modo ricorsivo le cartelle del Drive" + _reactWithoutRead: + title: "Hai letto bene?" + description: "Hai reagito ad una Nota più lunga di 100 caratteri entro 3 secondi dalla sua pubblicazione" + _clickedClickHere: + title: "Clicca qui" + description: "Hai cliccato qui" + _justPlainLucky: + title: "Proprio fortunato" + description: "Ottenuto con una probabilità dello 0,01% ogni 10 secondi" + _setNameToSyuilo: + title: "Complesso divino" + description: "Hai impostati il tuo nome in «syuilo»" + _passedSinceAccountCreated1: + title: "Primo Anniversario" + description: "È passato un anno da quando hai creato il profilo" + _passedSinceAccountCreated2: + title: "Secondo Anniversario" + description: "Sono passati due anni da quando hai creato il profilo" + _passedSinceAccountCreated3: + title: "Terzo Anniversario" + description: "Sono passati tre anni da quando hai creato il profilo" + _loggedInOnBirthday: + title: "Buon compleanno!" + description: "Hai effettuato l'accesso il giorno del tuo compleanno" + _loggedInOnNewYearsDay: + title: "Buon anno nuovo!" + description: "Hai usato effettuato l'accesso il giorno di capodanno" + flavor: "Anche quest'anno, grazie per il tuo continuo supporto a questa istanza" + _cookieClicked: + title: "Clicca il biscotto" + description: "Hai giocato a cliccare il cookie" + flavor: "Hai autorizzato i cookie?" _brainDiver: title: "Brain Diver" description: "Pubblica un link a Brain Diver" + flavor: "Sulle note di Brain Diver" _role: new: "Nuovo ruolo" edit: "Modifica ruolo" diff --git a/locales/ru-RU.yml b/locales/ru-RU.yml index f22a093cab..5419413984 100644 --- a/locales/ru-RU.yml +++ b/locales/ru-RU.yml @@ -891,19 +891,48 @@ cannotUploadBecauseNoFreeSpace: "Файл не может быть загруж beta: "Бета" enableAutoSensitive: "Автоматическое определение NSFW" enableAutoSensitiveDescription: "Если доступно, используйте машинное обучение для автоматической установки флага NSFW на носителе. Даже если эта функция отключена, она может быть установлена автоматически в зависимости от инстанта." +navbar: "Панель навигации" +shuffle: "Перемешать" account: "Учётные записи" +move: "Переместить" +pushNotification: "Push-уведомления" +subscribePushNotification: "Включить push-уведомления" +unsubscribePushNotification: "Выключить push-уведомления" +pushNotificationAlreadySubscribed: "Push-уведомления уже включены" +pushNotificationNotSupported: "Push-уведмления не поддерживаются инстансом или браузером" +sendPushNotificationReadMessage: "Удалять push-уведомления когда сообщение или прочитано" +sendPushNotificationReadMessageCaption: "На мгновение появится уведомление \"{emptyPushNotificationMessage}\". Расход заряда батареи может увеличиться " windowMaximize: "Развернуть" windowRestore: "Восстановить" +caption: "Подпись (Automatic Translation)" loggedInAsBot: "Вы под аккаунтом бота!" +tools: "Инструменты" +cannotLoad: "Не удалось загрузить" +numberOfProfileView: "Количество профилей для просмотра" like: "Нравится!" unlike: "Отменить «нравится»" +numberOfLikes: "Количество лайков" show: "Отображение" +neverShow: "Больше не показывать" +remindMeLater: "Напомнить позже" +didYouLikeMisskey: "Вам нравится Misskey?" pleaseDonate: "Сайт {host} работает на Misskey. Это бесплатное программное обеспечение, и ваши пожертвования очень бы помогли продолжать его разработку!" roles: "Роли" role: "Роль" +normalUser: "Обычный пользователь" +undefined: "неопределён" +assign: "Назначить" +unassign: "Отменить назначение" color: "Цвет" +manageCustomEmojis: "Управлять пользовательскими эмодзи" +youCannotCreateAnymore: "Вы достигли лимита создания." +cannotPerformTemporary: "Временно недоступен" +cannotPerformTemporaryDescription: "Это действие временно невозможно выполнить из-за превышения лимита выполнения." +preset: "Шаблоны" +selectFromPresets: "Выбрать из шаблонов" achievements: "Достижения" _achievements: + earnedAt: "Разблокировано в" _types: _notes1: title: "Первые шаги в Misskey" @@ -975,6 +1004,7 @@ _achievements: _login100: title: "Мискиец Ⅲ" description: "100 дней на сайте" + flavor: "Жестокий Misskist " _login200: title: "Завсегдатай Ⅰ" description: "200 дней на сайте" @@ -1010,6 +1040,9 @@ _achievements: _noteFavorited1: title: "Смотрящий на звёзды" description: "Первое добавление в избранное" + _myNoteFavorited1: + title: "В поиске звёзд" + description: "Кому-то понравилась ваша заметка" _profileFilled: title: "Приготовления закончены" description: "Заполнен профиль" @@ -1130,14 +1163,24 @@ _achievements: flavor: "Мисски-Мисски Ла-Ту-Ма" _role: new: "Новая роль" + edit: "Изменить роль" name: "Название роли" description: "Описание роли" permission: "Ролевые полномочия" + descriptionOfPermission: "<b>Модераторы</b> могут изменять базовые операции для модераторов.\n<b>Администраторы</b> могут изменять полностью настройки инстанса." assignTarget: "Метод присвоения" + descriptionOfAssignTarget: "<b>Вручную</b> чтобы указать кому выдавать роль, а кому нет.\n<b>По условию<b> чтобы автоматически выдавать и удалять роль при условиях." manual: "Вручную" conditional: "По условию" + condition: "Условия" + isConditionalRole: "Эта роль выдаётся по условию." isPublic: "Общедоступная роль" descriptionOfIsPublic: "Список тех, кому назначена эта роль будет доступен всем. Кроме того эта роль будет отмечена у каждого в профиле." + options: "Настройки ролей" + policies: "Политики" + baseRole: "Шаблон роли" + useBaseValue: "Использовать значение из шаблона" + chooseRoleToAssign: "Выберите роль, которую хотите выдать" canEditMembersByModerator: "Могут назначать модераторы" descriptionOfCanEditMembersByModerator: "Если включено, на эту роль могут назначать пользователей как администраторы, так и модераторы. Если выключено, назначать могут только администраторы." priority: "Приоритет" @@ -1145,6 +1188,8 @@ _role: low: "Низкий" middle: "Средне" high: "Высокий" + _options: + canManageCustomEmojis: "Управлять пользовательскими эмодзи" _sensitiveMediaDetection: description: "Машинное обучение может быть использовано для автоматического обнаружения чувствительных медиа для модерации. Нагрузка на сервер увеличивается незначительно." setSensitiveFlagAutomatically: "Установить флаг NSFW" @@ -1191,6 +1236,11 @@ _plugin: install: "Установка расширений" installWarn: "Пожалуйста, не устанавливайте расширения, которым не доверяете." manage: "Управление расширениями" +_preferencesBackups: + saveConfirm: "Сохранить бэкап как {name}?" + deleteConfirm: "Удалить резервную копию {name}?" + renameConfirm: "Переименовать резервную копию с \"{old}\" на \"{new}\"?" + noBackups: "Резервной копии не существует. Вы можете создать резервную копию в настройках на этом инстансе с помощью \"Создать новую резервную копию\"." _registry: scope: "Область" key: "Ключ" diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index 33127264b6..22c45f5167 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -992,22 +992,53 @@ _achievements: title: "ALL YOUR NOTE ARE BELONG TO US" description: "发布了100,000篇帖子" flavor: "真的有那么多可以写的东西吗?" + _login3: + title: "初学者 I" + description: "连续登录3天" + _login7: + description: "连续登录7天" + _login15: + description: "连续登录15天" + _login30: + description: "连续登录30天" + _login60: + description: "连续登录60天" _login1000: flavor: "感谢您使用Misskey!" _noteFavorited1: title: "观星者" _markedAsCat: title: "我是猫" + description: "将账户设定为一只猫" + _following10: + title: "关注,跟随" _following50: title: "我的朋友很多" + _following300: + description: "关注数超过300" + _followers100: + title: "胜友如云" + _collectAchievements30: + description: "获得超过30个成就" _viewAchievements3min: description: "盯着成就看三分钟" _iLoveMisskey: title: "I Love Misskey" description: "发布\"I ❤ #Misskey\"帖子" flavor: "感谢您使用 Misskey ! by 开发团队" + _noteDeletedWithin1min: + description: "发帖后一分钟内就将其删除" + _postedAtLateNight: + title: "夜行者" + description: "深夜发布帖子" _outputHelloWorldOnScratchpad: title: "Hello, world!" + _passedSinceAccountCreated1: + description: "账户创建时间超过1年" + _passedSinceAccountCreated2: + description: "账户创建时间超过2年" + _passedSinceAccountCreated3: + description: "账户创建时间超过3年" _loggedInOnBirthday: title: "生日快乐" description: "在生日当天登录" diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml index b88cfd91ae..4c7e5fb19f 100644 --- a/locales/zh-TW.yml +++ b/locales/zh-TW.yml @@ -1042,6 +1042,18 @@ _achievements: title: "貼文大師ⅠⅠⅠ" description: "總登入天數為1,000天" flavor: "感謝您使用Misskey!" + _noteClipped1: + title: "忍不住要收進摘錄裡" + description: "第一次將貼文收進摘錄" + _noteFavorited1: + title: "觀星者" + description: "第一次將貼文收進我的最愛" + _profileFilled: + title: "有備而來" + description: "設定了個人檔案" + _markedAsCat: + title: "我是貓" + description: "已將帳戶設定為貓" _followers500: title: "基站" description: "超過500名追隨者" From da32be3ef33aeda1c24899a1e7d338062d523e48 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 22 Jan 2023 17:10:49 +0900 Subject: [PATCH 60/70] 13.1.4 --- CHANGELOG.md | 8 ++++++++ package.json | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index feb2cb1f80..c1ff53fd0c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,14 @@ You should also include the user name that made the change. --> +## 13.1.4 (2023/01/22) + +### Improvements +- 新たな実績を追加 + +### Bugfixes +- Client: ローカリゼーション更新時にリロードが繰り返されることがあるのを修正 + ## 13.1.3 (2023/01/22) ### Bugfixes diff --git a/package.json b/package.json index e48eea7d7b..3f145c5f05 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "13.1.3", + "version": "13.1.4", "codename": "nasubi", "repository": { "type": "git", From 19c93151ce89d11ad0ac2b2ce43fb176a0e848a2 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 22 Jan 2023 17:18:39 +0900 Subject: [PATCH 61/70] tweak boot.js --- packages/backend/src/server/web/boot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/server/web/boot.js b/packages/backend/src/server/web/boot.js index 3d6dabe571..e635959fcf 100644 --- a/packages/backend/src/server/web/boot.js +++ b/packages/backend/src/server/web/boot.js @@ -55,7 +55,7 @@ renderError('META_FETCH'); return; } - const meta = await res.json(); + const meta = await metaRes.json(); const v = meta.version; if (v == null) { renderError('META_FETCH_V'); From 97d6c1ee862bf1277d69b8bd8658a4c16ad76d5b Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 22 Jan 2023 17:19:03 +0900 Subject: [PATCH 62/70] 13.1.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3f145c5f05..b0a64b0357 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "13.1.4", + "version": "13.1.5", "codename": "nasubi", "repository": { "type": "git", From 7a6534f30bae2f3321c20b853853da4fb97d8d6e Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 22 Jan 2023 17:29:31 +0900 Subject: [PATCH 63/70] =?UTF-8?q?=E3=82=AB=E3=82=B9=E3=82=BF=E3=83=A0?= =?UTF-8?q?=E7=B5=B5=E6=96=87=E5=AD=97=E3=81=AEURL=E3=81=8C=E7=A9=BA?= =?UTF-8?q?=E6=96=87=E5=AD=97=E5=88=97=E3=81=AB=E3=81=AA=E3=82=8B=E5=A0=B4?= =?UTF-8?q?=E5=90=88=E3=81=8C=E3=81=82=E3=82=8B=E3=81=AE=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 --- packages/backend/src/core/entities/EmojiEntityService.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/core/entities/EmojiEntityService.ts b/packages/backend/src/core/entities/EmojiEntityService.ts index cee85a5688..611552d89e 100644 --- a/packages/backend/src/core/entities/EmojiEntityService.ts +++ b/packages/backend/src/core/entities/EmojiEntityService.ts @@ -32,8 +32,8 @@ export class EmojiEntityService { name: emoji.name, category: emoji.category, host: opts.omitHost ? undefined : emoji.host, - // ?? emoji.originalUrl してるのは後方互換性のため - url: opts.withUrl ? (emoji.publicUrl ?? emoji.originalUrl) : undefined, + // || emoji.originalUrl してるのは後方互換性のため(publicUrlはstringなので??はだめ) + url: opts.withUrl ? (emoji.publicUrl || emoji.originalUrl) : undefined, }; } From 80a400a67ca0f602e07edb089deb9c41e463dfb8 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 22 Jan 2023 17:30:04 +0900 Subject: [PATCH 64/70] 13.1.6 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b0a64b0357..7c1db9cc66 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "13.1.5", + "version": "13.1.6", "codename": "nasubi", "repository": { "type": "git", From 42f3d9188bb1428ed4978ca0974f49e754d7f7d1 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 22 Jan 2023 20:22:38 +0900 Subject: [PATCH 65/70] add a secret achievement --- locales/ja-JP.yml | 3 + .../backend/src/core/AchievementService.ts | 1 + packages/frontend/src/pages/about-misskey.vue | 122 +++++++++++------- packages/frontend/src/scripts/achievements.ts | 8 +- 4 files changed, 88 insertions(+), 46 deletions(-) diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 57296b9857..cd4438fdeb 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1105,6 +1105,9 @@ _achievements: title: "I Love Misskey" description: "\"I ❤ #Misskey\"を投稿した" flavor: "Misskeyを使ってくださりありがとうございます! by 開発チーム" + _foundTreasure: + title: "宝探し" + description: "隠されたお宝を発見した" _client30min: title: "ひとやすみ" description: "クライアントを起動してから30分以上経過した" diff --git a/packages/backend/src/core/AchievementService.ts b/packages/backend/src/core/AchievementService.ts index be763e4629..2536ea34e1 100644 --- a/packages/backend/src/core/AchievementService.ts +++ b/packages/backend/src/core/AchievementService.ts @@ -62,6 +62,7 @@ const ACHIEVEMENT_TYPES = [ 'collectAchievements30', 'viewAchievements3min', 'iLoveMisskey', + 'foundTreasure', 'client30min', 'noteDeletedWithin1min', 'postedAtLateNight', diff --git a/packages/frontend/src/pages/about-misskey.vue b/packages/frontend/src/pages/about-misskey.vue index 1c3535a833..a6d4c60653 100644 --- a/packages/frontend/src/pages/about-misskey.vue +++ b/packages/frontend/src/pages/about-misskey.vue @@ -4,11 +4,14 @@ <div style="overflow: clip;"> <MkSpacer :content-max="600" :margin-min="20"> <div class="_gaps_m znqjceqz"> - <div ref="containerEl" v-panel class="about" :class="{ playing: easterEggEngine != null }"> - <img src="/client-assets/about-icon.png" alt="" class="icon" draggable="false" @load="iconLoaded" @click="gravity"/> - <div class="misskey">Misskey</div> - <div class="version">v{{ version }}</div> - <span v-for="emoji in easterEggEmojis" :key="emoji.id" class="emoji" :data-physics-x="emoji.left" :data-physics-y="emoji.top" :class="{ _physics_circle_: !emoji.emoji.startsWith(':') }"><MkEmoji class="emoji" :emoji="emoji.emoji" :is-reaction="false" :normal="true" :no-style="true"/></span> + <div v-panel class="about"> + <div ref="containerEl" class="container" :class="{ playing: easterEggEngine != null }"> + <img src="/client-assets/about-icon.png" alt="" class="icon" draggable="false" @load="iconLoaded" @click="gravity"/> + <div class="misskey">Misskey</div> + <div class="version">v{{ version }}</div> + <span v-for="emoji in easterEggEmojis" :key="emoji.id" class="emoji" :data-physics-x="emoji.left" :data-physics-y="emoji.top" :class="{ _physics_circle_: !emoji.emoji.startsWith(':') }"><MkEmoji class="emoji" :emoji="emoji.emoji" :is-reaction="false" :normal="true" :no-style="true"/></span> + </div> + <button v-if="thereIsTreasure" class="_button treasure" @click="getTreasure"><img src="/fluent-emoji/1f3c6.png" class="treasureImg"></button> </div> <div style="text-align: center;"> {{ i18n.ts._aboutMisskey.about }}<br><a href="https://misskey-hub.net/docs/misskey.html" target="_blank" class="_link">{{ i18n.ts.learnMore }}</a> @@ -70,6 +73,8 @@ import { i18n } from '@/i18n'; import { defaultStore } from '@/store'; import * as os from '@/os'; import { definePageMetadata } from '@/scripts/page-metadata'; +import { claimAchievement, claimedAchievements } from '@/scripts/achievements'; +import { $i } from '@/account'; const patrons = [ 'まっちゃとーにゅ', @@ -152,6 +157,8 @@ const patrons = [ 'pixeldesu', ]; +let thereIsTreasure = $ref($i && !claimedAchievements.includes('foundTreasure')); + let easterEggReady = false; let easterEggEmojis = $ref([]); let easterEggEngine = $ref(null); @@ -187,6 +194,11 @@ function iLoveMisskey() { }); } +function getTreasure() { + thereIsTreasure = false; + claimAchievement('foundTreasure'); +} + onBeforeUnmount(() => { if (easterEggEngine) { easterEggEngine.stop(); @@ -207,52 +219,72 @@ definePageMetadata({ .znqjceqz { > .about { position: relative; - text-align: center; - padding: 16px; border-radius: var(--radius); - &.playing { - &, * { - user-select: none; - } - - * { - will-change: transform; - } - - > .emoji { - visibility: visible; - } - } - - > .icon { - display: block; - width: 80px; - margin: 0 auto; - border-radius: 16px; - } - - > .misskey { - margin: 0.75em auto 0 auto; - width: max-content; - } - - > .version { - margin: 0 auto; - width: max-content; - opacity: 0.5; - } - - > .emoji { + > .treasure { position: absolute; - top: 0; + top: 55px; left: 0; - visibility: hidden; + right: 0; + margin: 0 auto; + width: min-content; + + > .treasureImg { + width: 25px; + vertical-align: bottom; + } + } + + > .container { + position: relative; + text-align: center; + padding: 16px; + + &.playing { + &, * { + user-select: none; + } + + * { + will-change: transform; + } + + > .emoji { + visibility: visible; + } + } + + > .icon { + display: block; + width: 80px; + margin: 0 auto; + border-radius: 16px; + position: relative; + z-index: 1; + } + + > .misskey { + margin: 0.75em auto 0 auto; + width: max-content; + } + + > .version { + margin: 0 auto; + width: max-content; + opacity: 0.5; + } > .emoji { - pointer-events: none; - font-size: 24px; - width: 24px; + position: absolute; + top: 0; + left: 0; + visibility: hidden; + + > .emoji { + pointer-events: none; + font-size: 24px; + width: 24px; + } } } } diff --git a/packages/frontend/src/scripts/achievements.ts b/packages/frontend/src/scripts/achievements.ts index f511fce3ea..53a921418c 100644 --- a/packages/frontend/src/scripts/achievements.ts +++ b/packages/frontend/src/scripts/achievements.ts @@ -58,6 +58,7 @@ export const ACHIEVEMENT_TYPES = [ 'collectAchievements30', 'viewAchievements3min', 'iLoveMisskey', + 'foundTreasure', 'client30min', 'noteDeletedWithin1min', 'postedAtLateNight', @@ -331,6 +332,11 @@ export const ACHIEVEMENT_BADGES = { bg: 'linear-gradient(0deg, rgb(255 77 77), rgb(247 155 214))', frame: 'silver', }, + 'foundTreasure': { + img: '/fluent-emoji/1f3c6.png', + bg: 'linear-gradient(0deg, rgb(255 77 77), rgb(247 155 214))', + frame: 'gold', + }, 'client30min': { img: '/fluent-emoji/1f552.png', bg: 'linear-gradient(0deg, rgb(220 223 225), rgb(172 192 207))', @@ -437,7 +443,7 @@ export const ACHIEVEMENT_BADGES = { frame: 'bronze' | 'silver' | 'gold' | 'platinum'; }>; -export const claimedAchievements = ($i && $i.achievements) ? $i.achievements.map(x => x.name) : []; +export const claimedAchievements: typeof ACHIEVEMENT_TYPES[number][] = ($i && $i.achievements) ? $i.achievements.map(x => x.name) : []; const claimingQueue = new Set<string>(); From ede96eca28c670f67b225d6fdd691c6bc83e2ad4 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 22 Jan 2023 20:25:10 +0900 Subject: [PATCH 66/70] :art: --- packages/frontend/src/pages/about-misskey.vue | 7 ++++++- packages/frontend/src/scripts/achievements.ts | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/frontend/src/pages/about-misskey.vue b/packages/frontend/src/pages/about-misskey.vue index a6d4c60653..82f4e30a45 100644 --- a/packages/frontend/src/pages/about-misskey.vue +++ b/packages/frontend/src/pages/about-misskey.vue @@ -223,7 +223,7 @@ definePageMetadata({ > .treasure { position: absolute; - top: 55px; + top: 60px; left: 0; right: 0; margin: 0 auto; @@ -266,16 +266,21 @@ definePageMetadata({ > .misskey { margin: 0.75em auto 0 auto; width: max-content; + position: relative; + z-index: 1; } > .version { margin: 0 auto; width: max-content; opacity: 0.5; + position: relative; + z-index: 1; } > .emoji { position: absolute; + z-index: 1; top: 0; left: 0; visibility: hidden; diff --git a/packages/frontend/src/scripts/achievements.ts b/packages/frontend/src/scripts/achievements.ts index 53a921418c..8c59c92b14 100644 --- a/packages/frontend/src/scripts/achievements.ts +++ b/packages/frontend/src/scripts/achievements.ts @@ -334,7 +334,7 @@ export const ACHIEVEMENT_BADGES = { }, 'foundTreasure': { img: '/fluent-emoji/1f3c6.png', - bg: 'linear-gradient(0deg, rgb(255 77 77), rgb(247 155 214))', + bg: 'linear-gradient(0deg, rgb(197 69 192), rgb(2 112 155))', frame: 'gold', }, 'client30min': { From b906ff3fedf2ec420a057a6d2c851df2b7b77495 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 22 Jan 2023 20:30:56 +0900 Subject: [PATCH 67/70] add an achievement --- locales/ja-JP.yml | 3 +++ packages/backend/src/core/AchievementService.ts | 1 + packages/frontend/src/pages/about.vue | 9 ++++++++- packages/frontend/src/scripts/achievements.ts | 7 +++++++ 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index cd4438fdeb..a241d54b47 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1128,6 +1128,9 @@ _achievements: _htl20npm: title: "流れるTL" description: "ホームタイムラインの流速が20npmを越す" + _viewInstanceChart: + title: "アナリスト" + description: "インスタンスのチャートを表示した" _outputHelloWorldOnScratchpad: title: "Hello, world!" description: "スクラッチパッドで hello world を出力した" diff --git a/packages/backend/src/core/AchievementService.ts b/packages/backend/src/core/AchievementService.ts index 2536ea34e1..5fd9c451ce 100644 --- a/packages/backend/src/core/AchievementService.ts +++ b/packages/backend/src/core/AchievementService.ts @@ -69,6 +69,7 @@ const ACHIEVEMENT_TYPES = [ 'postedAt0min0sec', 'selfQuote', 'htl20npm', + 'viewInstanceChart', 'outputHelloWorldOnScratchpad', 'open3windows', 'driveFolderCircularReference', diff --git a/packages/frontend/src/pages/about.vue b/packages/frontend/src/pages/about.vue index 4d971c5a9f..e5b9aecc61 100644 --- a/packages/frontend/src/pages/about.vue +++ b/packages/frontend/src/pages/about.vue @@ -86,7 +86,7 @@ </template> <script lang="ts" setup> -import { ref, computed } from 'vue'; +import { ref, computed, watch } from 'vue'; import XEmojis from './about.emojis.vue'; import XFederation from './about.federation.vue'; import { version, instanceName, host } from '@/config'; @@ -100,6 +100,7 @@ import * as os from '@/os'; import number from '@/filters/number'; import { i18n } from '@/i18n'; import { definePageMetadata } from '@/scripts/page-metadata'; +import { claimAchievement } from '@/scripts/achievements'; const props = withDefaults(defineProps<{ initialTab?: string; @@ -110,6 +111,12 @@ const props = withDefaults(defineProps<{ let stats = $ref(null); let tab = $ref(props.initialTab); +watch($$(tab), () => { + if (tab === 'charts') { + claimAchievement('viewInstanceChart'); + } +}); + const initStats = () => os.api('stats', { }).then((res) => { stats = res; diff --git a/packages/frontend/src/scripts/achievements.ts b/packages/frontend/src/scripts/achievements.ts index 8c59c92b14..c77f8e12d3 100644 --- a/packages/frontend/src/scripts/achievements.ts +++ b/packages/frontend/src/scripts/achievements.ts @@ -65,6 +65,7 @@ export const ACHIEVEMENT_TYPES = [ 'postedAt0min0sec', 'selfQuote', 'htl20npm', + 'viewInstanceChart', 'outputHelloWorldOnScratchpad', 'open3windows', 'driveFolderCircularReference', @@ -367,6 +368,11 @@ export const ACHIEVEMENT_BADGES = { bg: 'linear-gradient(0deg, rgb(220 223 225), rgb(172 192 207))', frame: 'bronze', }, + 'viewInstanceChart': { + img: '/fluent-emoji/1f4ca.png', + bg: 'linear-gradient(0deg, rgb(58 231 198), rgb(37 194 255))', + frame: 'bronze', + }, 'outputHelloWorldOnScratchpad': { img: '/fluent-emoji/1f530.png', bg: 'linear-gradient(0deg, rgb(58 231 198), rgb(37 194 255))', @@ -448,6 +454,7 @@ export const claimedAchievements: typeof ACHIEVEMENT_TYPES[number][] = ($i && $i const claimingQueue = new Set<string>(); export async function claimAchievement(type: typeof ACHIEVEMENT_TYPES[number]) { + if ($i == null) return; if (claimedAchievements.includes(type)) return; claimingQueue.add(type); claimedAchievements.push(type); From 313a489ba01de5aa8b3eae18ae78d57a0561b254 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 22 Jan 2023 20:46:43 +0900 Subject: [PATCH 68/70] New Crowdin updates (#9689) * New translations ja-JP.yml (Chinese Traditional) * New translations ja-JP.yml (Chinese Simplified) * New translations ja-JP.yml (Italian) * New translations ja-JP.yml (Ukrainian) --- locales/it-IT.yml | 5 +++- locales/uk-UA.yml | 9 ++++++ locales/zh-CN.yml | 2 +- locales/zh-TW.yml | 70 +++++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 79 insertions(+), 7 deletions(-) diff --git a/locales/it-IT.yml b/locales/it-IT.yml index c5d89a0f55..1726a84ff8 100644 --- a/locales/it-IT.yml +++ b/locales/it-IT.yml @@ -95,7 +95,7 @@ follow: "Segui" followRequest: "Richiesta di follow" followRequests: "Richieste di follow" unfollow: "Smetti di seguire" -followRequestPending: "La richiesta di follow deve essere approvata" +followRequestPending: "Richiesta in approvazione" enterEmoji: "Inserisci emoji" renote: "Rinota" unrenote: "Annulla rinota" @@ -1048,6 +1048,9 @@ _achievements: _noteFavorited1: title: "Guarda le stelle" description: "Aggiungi una Nota ai preferiti per la prima volta" + _myNoteFavorited1: + title: "Fornitura stelline" + description: "Qualcuno ha preferito una delle tue Note" _profileFilled: title: "Perfettamente" description: "Imposta il tuo profilo" diff --git a/locales/uk-UA.yml b/locales/uk-UA.yml index 6ca8d7059d..ddb13d875d 100644 --- a/locales/uk-UA.yml +++ b/locales/uk-UA.yml @@ -899,6 +899,15 @@ unlike: "Не вподобати" numberOfLikes: "Вподобання" show: "Відображення" color: "Колір" +achievements: "Досягнення" +_achievements: + _types: + _notes1: + title: "налаштовую свій msky" + description: "Перша нотатка" + flavor: "Приємного часу з Misskey!" + _notes10: + title: "Декілька нотаток" _role: priority: "Пріоритет" _priority: diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index 22c45f5167..433af4c836 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -956,7 +956,7 @@ _achievements: title: "满是帖子" description: "发布了500篇帖子" _notes1000: - title: "帖子成山" + title: "积帖成山" description: "发布了1,000篇帖子" _notes5000: title: "帖如泉涌" diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml index 4c7e5fb19f..a8a06528d2 100644 --- a/locales/zh-TW.yml +++ b/locales/zh-TW.yml @@ -1047,25 +1047,59 @@ _achievements: description: "第一次將貼文收進摘錄" _noteFavorited1: title: "觀星者" - description: "第一次將貼文收進我的最愛" + description: "第一次將貼文收藏至我的最愛" + _myNoteFavorited1: + title: "想要星星" + description: "自己的貼文被他人收藏至「我的最愛」了" _profileFilled: title: "有備而來" description: "設定了個人檔案" _markedAsCat: title: "我是貓" description: "已將帳戶設定為貓" + flavor: "還沒有名字。" + _following1: + title: "首次追隨" + description: "首次追隨了" + _following10: + title: "跟著跟著" + description: "跟隨超過10人了" + _following50: + title: "朋友很多" + description: "跟隨超過50人了" + _following100: + title: "100位朋友" + description: "跟隨超過100人了" + _following300: + title: "朋友過多" + description: "跟隨超過300人了" + _followers1: + title: "第一個追隨者" + description: "第一次被追隨" + _followers10: + title: "Follow me!" + description: "跟隨者超過10人了" + _followers50: + title: "成群結隊" + description: "跟隨者超過50人了" + _followers100: + title: "紅人" + description: "跟隨者超過100人了" + _followers300: + title: "請排成一排" + description: "跟隨者超過300人了" _followers500: title: "基站" - description: "超過500名追隨者" + description: "超過500名追隨者了" _followers1000: title: "影響者" - description: "超過1000名追隨者" + description: "超過1000名追隨者了" _collectAchievements30: title: "成就收藏家" description: "獲得30個以上的成就" _viewAchievements3min: title: "喜愛成就" - description: "看成就列表要花了3分鐘以上" + description: "看成就列表要花3分鐘以上" _iLoveMisskey: title: "I Love Misskey" description: "發布「I ❤ #Misskey」" @@ -1083,6 +1117,7 @@ _achievements: _postedAt0min0sec: title: "報時" description: "在0分0秒發佈貼文" + flavor: "啵.啵.啵.嗶ー" _selfQuote: title: "自我引用" description: "引用了自己的貼文" @@ -1094,7 +1129,7 @@ _achievements: description: "在暫存記憶體輸出了 hello world" _open3windows: title: "多重視窗" - description: "開啟3個以上的視窗" + description: "開啟了3個以上的視窗" _driveFolderCircularReference: title: "循環引用" description: "試圖遞迴套入雲端硬碟資料夾" @@ -1110,6 +1145,30 @@ _achievements: _setNameToSyuilo: title: "神的情結" description: "將名稱設定為 syuilo" + _passedSinceAccountCreated1: + title: "一周年" + description: "自建立帳戶開始過了1年" + _passedSinceAccountCreated2: + title: "二周年" + description: "自建立帳戶開始過了2年" + _passedSinceAccountCreated3: + title: "三周年" + description: "自建立帳戶開始過了3年" + _loggedInOnBirthday: + title: "生日快樂" + description: "在生日當天登入了" + _loggedInOnNewYearsDay: + title: "新年快樂" + description: "在元旦當天登入了" + flavor: "今年也請對敝實例多多指教" + _cookieClicked: + title: "點擊餅乾的遊戲" + description: "點擊了餅乾" + flavor: "是不是軟體有問題?" + _brainDiver: + title: "Brain Driver" + description: "發佈了Brain Driver的連結" + flavor: "Misskey-Misskey La-Tu-Ma" _role: new: "建立角色" edit: "編輯角色" @@ -1759,6 +1818,7 @@ _notification: pollEnded: "問卷調查已產生結果" unreadAntennaNote: "天線 {name}" emptyPushNotificationMessage: "推送通知已更新" + achievementEarned: "獲得成就" _types: all: "全部 " follow: "追隨中" From e6338a555d0829cd8c9b119c07f131db3637db3d Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 22 Jan 2023 20:58:52 +0900 Subject: [PATCH 69/70] =?UTF-8?q?mfm=E3=81=ABscale=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolve #9609 --- packages/frontend/src/components/mfm.ts | 6 ++++++ packages/frontend/src/scripts/mfm-tags.ts | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/frontend/src/components/mfm.ts b/packages/frontend/src/components/mfm.ts index 9421625c9e..91875de9cf 100644 --- a/packages/frontend/src/components/mfm.ts +++ b/packages/frontend/src/components/mfm.ts @@ -200,6 +200,12 @@ export default defineComponent({ style = `transform: translateX(${x}em) translateY(${y}em);`; break; } + case 'scale': { + const x = Math.min(parseInt(token.props.args.x ?? '1'), 5); + const y = Math.min(parseInt(token.props.args.y ?? '1'), 5); + style = `transform: scale(${x}, ${y});`; + break; + } case 'fg': { let color = token.props.args.color; if (!/^[0-9a-f]{3,6}$/i.test(color)) color = 'f00'; diff --git a/packages/frontend/src/scripts/mfm-tags.ts b/packages/frontend/src/scripts/mfm-tags.ts index be944a7139..a84198282d 100644 --- a/packages/frontend/src/scripts/mfm-tags.ts +++ b/packages/frontend/src/scripts/mfm-tags.ts @@ -1 +1 @@ -export const MFM_TAGS = ['tada', 'jelly', 'twitch', 'shake', 'spin', 'jump', 'bounce', 'flip', 'x2', 'x3', 'x4', 'position', 'fg', 'bg', 'font', 'blur', 'rainbow', 'sparkle', 'rotate']; +export const MFM_TAGS = ['tada', 'jelly', 'twitch', 'shake', 'spin', 'jump', 'bounce', 'flip', 'x2', 'x3', 'x4', 'scale', 'position', 'fg', 'bg', 'font', 'blur', 'rainbow', 'sparkle', 'rotate']; From 2e6d8c792b3ab268124b15d15b2ac49f8c1905f1 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Sun, 22 Jan 2023 21:01:42 +0900 Subject: [PATCH 70/70] 13.1.7 --- CHANGELOG.md | 6 ++++++ package.json | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c1ff53fd0c..2b820f22c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,12 @@ You should also include the user name that made the change. --> +## 13.1.7 (2023/01/22) + +### Improvements +- 新たな実績を追加 +- MFMにscaleタグを追加 + ## 13.1.4 (2023/01/22) ### Improvements diff --git a/package.json b/package.json index 7c1db9cc66..31033bafe3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "13.1.6", + "version": "13.1.7", "codename": "nasubi", "repository": { "type": "git",