diff --git a/.github/workflows/docker-develop.yml b/.github/workflows/docker-develop.yml index d7f02fd8f3..05bb7f77f9 100644 --- a/.github/workflows/docker-develop.yml +++ b/.github/workflows/docker-develop.yml @@ -16,21 +16,21 @@ jobs: uses: actions/checkout@v4.0.0 - name: Set up Docker Buildx id: buildx - uses: docker/setup-buildx-action@v2.10.0 + uses: docker/setup-buildx-action@v3.0.0 with: platforms: linux/amd64,linux/arm64 - name: Docker meta id: meta - uses: docker/metadata-action@v4 + uses: docker/metadata-action@v5 with: images: misskey/misskey - name: Log in to Docker Hub - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Build and Push to Docker Hub - uses: docker/build-push-action@v4 + uses: docker/build-push-action@v5 with: builder: ${{ steps.buildx.outputs.name }} context: . diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index e142e0915b..32a98a416d 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -15,12 +15,12 @@ jobs: uses: actions/checkout@v4.0.0 - name: Set up Docker Buildx id: buildx - uses: docker/setup-buildx-action@v2.10.0 + uses: docker/setup-buildx-action@v3.0.0 with: platforms: linux/amd64,linux/arm64 - name: Docker meta id: meta - uses: docker/metadata-action@v4 + uses: docker/metadata-action@v5 with: images: misskey/misskey tags: | @@ -31,12 +31,12 @@ jobs: type=semver,pattern={{major}}.{{minor}} type=semver,pattern={{major}} - name: Log in to Docker Hub - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Build and Push to Docker Hub - uses: docker/build-push-action@v4 + uses: docker/build-push-action@v5 with: builder: ${{ steps.buildx.outputs.name }} context: . diff --git a/.github/workflows/ok-to-test.yml b/.github/workflows/ok-to-test.yml index 71d09f7814..c02b980e4d 100644 --- a/.github/workflows/ok-to-test.yml +++ b/.github/workflows/ok-to-test.yml @@ -17,7 +17,7 @@ jobs: # See app.yml for an example app manifest - name: Generate token id: generate_token - uses: tibdex/github-app-token@v1 + uses: tibdex/github-app-token@v2 with: app_id: ${{ secrets.DEPLOYBOT_APP_ID }} private_key: ${{ secrets.DEPLOYBOT_PRIVATE_KEY }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 03c93f70df..7f34a8a985 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ - センシティブチャンネルのノートはユーザープロフィールに表示されません - 二要素認証のバックアップコードが生成されるようになりました ref. https://github.com/MisskeyIO/misskey/pull/121 - 二要素認証でパスキーをサポートするようになりました +- 通知をテストできるようになりました ### Client - プロフィールにその人が作ったPlayの一覧出せるように @@ -33,7 +34,6 @@ - 投稿フォームのプレビューの表示状態を記憶するように - AiScriptからMisskeyサーバーAPIを呼び出す際の制限を撤廃 - Playで直接投稿フォームを埋め込めるように(`Ui:C:postForm`) -- 通知をテストできるように - Enhance: ユーザーメニューでスイッチでユーザーリストに追加・削除できるように - Enhance: 自分が押したリアクションのデザインを改善 - Enhance: ノート検索にローカルのみ検索可能なオプションの追加 @@ -46,6 +46,8 @@ - リアクションの表示サイズをより大きくできるように - ノート詳細ページ読み込み時のパフォーマンスを改善 - タイムラインでリスト/アンテナ選択時のパフォーマンスを改善 +- 「Moderation note」、「Add moderation note」をローカライズできるように +- 新しい実績を追加 - Fix: サーバー情報画面(`/instance-info/{domain}`)でブロックができないのを修正 - Fix: 未読のお知らせの「わかった」をクリック・タップしてもその場で「わかった」が消えない問題を修正 - Fix: iOSで画面を回転させるとテキストサイズが変わる問題を修正 @@ -62,6 +64,7 @@ - 使われていないアンテナの自動停止を設定可能に - nodeinfo 2.1対応 - 自分へのメンション一覧を取得する際のパフォーマンスを向上 +- Docker環境でjemallocを使用することでメモリ使用量を削減 - Fix: ノート検索 `notes/search` にてhostを指定した際に検索結果に反映されるように - Fix: 一部のfeatured noteを照会できない問題を修正 - Fix: muteがapiからのuser list timeline取得で機能しない問題を修正 diff --git a/Dockerfile b/Dockerfile index 678b991e81..654af605dc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -62,7 +62,7 @@ ARG GID="991" RUN apt-get update \ && apt-get install -y --no-install-recommends \ - ffmpeg tini curl \ + ffmpeg tini curl libjemalloc-dev libjemalloc2 \ && corepack enable \ && groupadd -g "${GID}" misskey \ && useradd -l -u "${UID}" -g "${GID}" -m -d /misskey misskey \ @@ -81,6 +81,7 @@ COPY --chown=misskey:misskey --from=native-builder /misskey/packages/backend/bui COPY --chown=misskey:misskey --from=native-builder /misskey/fluent-emojis /misskey/fluent-emojis COPY --chown=misskey:misskey . ./ +ENV LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2 ENV NODE_ENV=production HEALTHCHECK --interval=5s --retries=20 CMD ["/bin/bash", "/misskey/healthcheck.sh"] ENTRYPOINT ["/usr/bin/tini", "--"] diff --git a/locales/index.d.ts b/locales/index.d.ts index 40c9054c67..792c775813 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -420,6 +420,8 @@ export interface Locale { "totpDescription": string; "moderator": string; "moderation": string; + "moderationNote": string; + "addModerationNote": string; "nUsersMentioned": string; "securityKeyAndPasskey": string; "securityKey": string; @@ -1468,6 +1470,10 @@ export interface Locale { "description": string; "flavor": string; }; + "_smashTestNotificationButton": { + "title": string; + "description": string; + }; }; }; "_role": { diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 4697448841..ed911caa41 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -417,6 +417,8 @@ totp: "認証アプリ" totpDescription: "認証アプリを使ってワンタイムパスワードを入力" moderator: "モデレーター" moderation: "モデレーション" +moderationNote: "モデレーションノート" +addModerationNote: "モデレーションノートを追加する" nUsersMentioned: "{n}人が投稿" securityKeyAndPasskey: "セキュリティキー・パスキー" securityKey: "セキュリティキー" @@ -1392,6 +1394,9 @@ _achievements: title: "Brain Diver" description: "Brain Diverへのリンクを投稿した" flavor: "Misskey-Misskey La-Tu-Ma" + _smashTestNotificationButton: + title: "テスト過剰" + description: "通知のテストをごく短時間のうちに連続して行った" _role: new: "ロールの作成" diff --git a/packages/backend/src/core/AchievementService.ts b/packages/backend/src/core/AchievementService.ts index aa810015ed..a35acd3680 100644 --- a/packages/backend/src/core/AchievementService.ts +++ b/packages/backend/src/core/AchievementService.ts @@ -85,6 +85,7 @@ export const ACHIEVEMENT_TYPES = [ 'setNameToSyuilo', 'cookieClicked', 'brainDiver', + 'smashTestNotificationButton', ] as const; @Injectable() diff --git a/packages/backend/src/server/api/endpoints/notifications/create.ts b/packages/backend/src/server/api/endpoints/notifications/create.ts index 020fc2d3df..268628cf76 100644 --- a/packages/backend/src/server/api/endpoints/notifications/create.ts +++ b/packages/backend/src/server/api/endpoints/notifications/create.ts @@ -14,6 +14,11 @@ export const meta = { kind: 'write:notifications', + limit: { + duration: 1000 * 60, + max: 10, + }, + errors: { }, } as const; diff --git a/packages/backend/src/server/api/endpoints/notifications/test-notification.ts b/packages/backend/src/server/api/endpoints/notifications/test-notification.ts index 04a68a8054..8f5f8485c3 100644 --- a/packages/backend/src/server/api/endpoints/notifications/test-notification.ts +++ b/packages/backend/src/server/api/endpoints/notifications/test-notification.ts @@ -13,6 +13,11 @@ export const meta = { requireCredential: true, kind: 'write:notifications', + + limit: { + duration: 1000 * 60, + max: 10, + }, } as const; export const paramDef = { diff --git a/packages/frontend/src/pages/admin-user.vue b/packages/frontend/src/pages/admin-user.vue index f8535e46be..1d18819f1d 100644 --- a/packages/frontend/src/pages/admin-user.vue +++ b/packages/frontend/src/pages/admin-user.vue @@ -52,7 +52,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> <MkTextarea v-model="moderationNote" manualSave> - <template #label>Moderation note</template> + <template #label>{{ i18n.ts.moderationNote }}</template> </MkTextarea> <!-- diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index 86fc12e514..bc4b5db8d6 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -104,7 +104,7 @@ SPDX-License-Identifier: AGPL-3.0-only <option value="horizontal"><i class="ti ti-carousel-horizontal"></i> {{ i18n.ts.horizontal }}</option> </MkRadios> - <MkButton @click="testNotification('client')">{{ i18n.ts._notification.checkNotificationBehavior }}</MkButton> + <MkButton @click="testNotification">{{ i18n.ts._notification.checkNotificationBehavior }}</MkButton> </div> </FormSection> @@ -184,6 +184,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { computed, ref, watch } from 'vue'; +import * as Misskey from 'misskey-js'; import MkSwitch from '@/components/MkSwitch.vue'; import MkSelect from '@/components/MkSelect.vue'; import MkRadios from '@/components/MkRadios.vue'; @@ -202,6 +203,8 @@ import { definePageMetadata } from '@/scripts/page-metadata'; import { miLocalStorage } from '@/local-storage'; import { isWebKit } from '@/scripts/useragent'; import { testNotification } from '@/scripts/test-notification'; +import { globalEvents } from '@/events'; +import { claimAchievement } from '@/scripts/achievements'; const lang = ref(miLocalStorage.getItem('lang')); const fontSize = ref(miLocalStorage.getItem('fontSize')); @@ -315,6 +318,32 @@ function removeEmojiIndex(lang: string) { os.promiseDialog(main()); } +let smashCount = 0; +let smashTimer: number | null = null; +function testNotification(): void { + const notification: Misskey.entities.Notification = { + id: Math.random().toString(), + createdAt: new Date().toUTCString(), + isRead: false, + type: 'test', + }; + + globalEvents.emit('clientNotification', notification); + + // セルフ通知破壊 実績関連 + smashCount++; + if (smashCount >= 10) { + claimAchievement('smashTestNotificationButton'); + smashCount = 0; + } + if (smashTimer) { + clearTimeout(smashTimer); + } + smashTimer = window.setTimeout(() => { + smashCount = 0; + }, 300); +} + const headerActions = $computed(() => []); const headerTabs = $computed(() => []); diff --git a/packages/frontend/src/pages/settings/notifications.vue b/packages/frontend/src/pages/settings/notifications.vue index b20add724c..193795cdec 100644 --- a/packages/frontend/src/pages/settings/notifications.vue +++ b/packages/frontend/src/pages/settings/notifications.vue @@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only </FormSection> <FormSection> <div class="_gaps_m"> - <FormLink @click="testNotification('server')">{{ i18n.ts._notification.sendTestNotification }}</FormLink> + <FormLink @click="testNotification">{{ i18n.ts._notification.sendTestNotification }}</FormLink> </div> </FormSection> <FormSection> @@ -46,7 +46,6 @@ import { i18n } from '@/i18n'; import { definePageMetadata } from '@/scripts/page-metadata'; import MkPushNotificationAllowButton from '@/components/MkPushNotificationAllowButton.vue'; import { notificationTypes } from '@/const'; -import { testNotification } from '@/scripts/test-notification'; let allowButton = $shallowRef<InstanceType<typeof MkPushNotificationAllowButton>>(); let pushRegistrationInServer = $computed(() => allowButton?.pushRegistrationInServer); @@ -89,6 +88,10 @@ function onChangeSendReadMessage(v: boolean) { }); } +function testNotification(): void { + os.api('notifications/test-notification'); +} + const headerActions = $computed(() => []); const headerTabs = $computed(() => []); diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue index bef1bda7ba..a8720e1050 100644 --- a/packages/frontend/src/pages/user/home.vue +++ b/packages/frontend/src/pages/user/home.vue @@ -57,10 +57,10 @@ SPDX-License-Identifier: AGPL-3.0-only </div> <div v-if="iAmModerator" class="moderationNote"> <MkTextarea v-if="editModerationNote || (moderationNote != null && moderationNote !== '')" v-model="moderationNote" manualSave> - <template #label>Moderation note</template> + <template #label>{{ i18n.ts.moderationNote }}</template> </MkTextarea> <div v-else> - <MkButton small @click="editModerationNote = true">Add moderation note</MkButton> + <MkButton small @click="editModerationNote = true">{{ i18n.ts.addModerationNote }}</MkButton> </div> </div> <div v-if="isEditingMemo || memoDraft" class="memo" :class="{'no-memo': !memoDraft}"> diff --git a/packages/frontend/src/scripts/achievements.ts b/packages/frontend/src/scripts/achievements.ts index 8b3a518830..54242b330d 100644 --- a/packages/frontend/src/scripts/achievements.ts +++ b/packages/frontend/src/scripts/achievements.ts @@ -81,6 +81,7 @@ export const ACHIEVEMENT_TYPES = [ 'setNameToSyuilo', 'cookieClicked', 'brainDiver', + 'smashTestNotificationButton', ] as const; export const ACHIEVEMENT_BADGES = { @@ -454,6 +455,11 @@ export const ACHIEVEMENT_BADGES = { bg: 'linear-gradient(0deg, rgb(144, 224, 255), rgb(255, 168, 252))', frame: 'bronze', }, + 'smashTestNotificationButton': { + img: '/fluent-emoji/1f514.png', + bg: 'linear-gradient(0deg, rgb(187 183 59), rgb(255 143 77))', + frame: 'bronze', + }, /* @see <https://github.com/misskey-dev/misskey/pull/10365#discussion_r1155511107> } as const satisfies Record<typeof ACHIEVEMENT_TYPES[number], { img: string; diff --git a/packages/frontend/src/scripts/test-notification.ts b/packages/frontend/src/scripts/test-notification.ts deleted file mode 100644 index 0e8289e19e..0000000000 --- a/packages/frontend/src/scripts/test-notification.ts +++ /dev/null @@ -1,34 +0,0 @@ -/* - * SPDX-FileCopyrightText: syuilo and other misskey contributors - * SPDX-License-Identifier: AGPL-3.0-only - */ - -import * as Misskey from 'misskey-js'; -import * as os from '@/os'; -import { globalEvents } from '@/events'; - -/** - * テスト通知を送信 - * - * - `client` … 通知ポップアップのみを表示 - * - `server` … サーバー側から通知を送信 - * - * @param type 通知タイプを指定 - */ -export function testNotification(type: 'client' | 'server'): void { - const notification: Misskey.entities.Notification = { - id: Math.random().toString(), - createdAt: new Date().toUTCString(), - isRead: false, - type: 'test', - }; - - switch (type) { - case 'server': - os.api('notifications/test-notification'); - break; - case 'client': - globalEvents.emit('clientNotification', notification); - break; - } -}