Merge remote-branch 'misskey/develop'
This commit is contained in:
commit
0a3e7413a3
2
.github/workflows/api-cherrypick-js.yml
vendored
2
.github/workflows/api-cherrypick-js.yml
vendored
|
@ -14,7 +14,7 @@ jobs:
|
||||||
- run: corepack enable
|
- run: corepack enable
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v4.0.0
|
uses: actions/setup-node@v4.0.1
|
||||||
with:
|
with:
|
||||||
node-version-file: '.node-version'
|
node-version-file: '.node-version'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
|
|
2
.github/workflows/get-api-diff.yml
vendored
2
.github/workflows/get-api-diff.yml
vendored
|
@ -37,7 +37,7 @@ jobs:
|
||||||
version: 8
|
version: 8
|
||||||
run_install: false
|
run_install: false
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v4.0.0
|
uses: actions/setup-node@v4.0.1
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
|
|
6
.github/workflows/lint.yml
vendored
6
.github/workflows/lint.yml
vendored
|
@ -19,7 +19,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
version: 8
|
version: 8
|
||||||
run_install: false
|
run_install: false
|
||||||
- uses: actions/setup-node@v4.0.0
|
- uses: actions/setup-node@v4.0.1
|
||||||
with:
|
with:
|
||||||
node-version-file: '.node-version'
|
node-version-file: '.node-version'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
|
@ -46,7 +46,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
version: 7
|
version: 7
|
||||||
run_install: false
|
run_install: false
|
||||||
- uses: actions/setup-node@v4.0.0
|
- uses: actions/setup-node@v4.0.1
|
||||||
with:
|
with:
|
||||||
node-version-file: '.node-version'
|
node-version-file: '.node-version'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
|
@ -72,7 +72,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
version: 7
|
version: 7
|
||||||
run_install: false
|
run_install: false
|
||||||
- uses: actions/setup-node@v4.0.0
|
- uses: actions/setup-node@v4.0.1
|
||||||
with:
|
with:
|
||||||
node-version-file: '.node-version'
|
node-version-file: '.node-version'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
|
|
4
.github/workflows/test-backend.yml
vendored
4
.github/workflows/test-backend.yml
vendored
|
@ -17,7 +17,7 @@ jobs:
|
||||||
|
|
||||||
services:
|
services:
|
||||||
postgres:
|
postgres:
|
||||||
image: postgres:13
|
image: postgres:15
|
||||||
ports:
|
ports:
|
||||||
- 54312:5432
|
- 54312:5432
|
||||||
env:
|
env:
|
||||||
|
@ -38,7 +38,7 @@ jobs:
|
||||||
version: 8
|
version: 8
|
||||||
run_install: false
|
run_install: false
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v4.0.0
|
uses: actions/setup-node@v4.0.1
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
|
|
2
.github/workflows/test-cherrypick-js.yml
vendored
2
.github/workflows/test-cherrypick-js.yml
vendored
|
@ -26,7 +26,7 @@ jobs:
|
||||||
- run: corepack enable
|
- run: corepack enable
|
||||||
|
|
||||||
- name: Setup Node.js ${{ matrix.node-version }}
|
- name: Setup Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v4.0.0
|
uses: actions/setup-node@v4.0.1
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
|
|
6
.github/workflows/test-frontend.yml
vendored
6
.github/workflows/test-frontend.yml
vendored
|
@ -25,7 +25,7 @@ jobs:
|
||||||
version: 8
|
version: 8
|
||||||
run_install: false
|
run_install: false
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v4.0.0
|
uses: actions/setup-node@v4.0.1
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
|
@ -56,7 +56,7 @@ jobs:
|
||||||
|
|
||||||
services:
|
services:
|
||||||
postgres:
|
postgres:
|
||||||
image: postgres:13
|
image: postgres:15
|
||||||
ports:
|
ports:
|
||||||
- 54312:5432
|
- 54312:5432
|
||||||
env:
|
env:
|
||||||
|
@ -83,7 +83,7 @@ jobs:
|
||||||
version: 7
|
version: 7
|
||||||
run_install: false
|
run_install: false
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v4.0.0
|
uses: actions/setup-node@v4.0.1
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
|
|
2
.github/workflows/test-production.yml
vendored
2
.github/workflows/test-production.yml
vendored
|
@ -28,7 +28,7 @@ jobs:
|
||||||
version: 8
|
version: 8
|
||||||
run_install: false
|
run_install: false
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v4.0.0
|
uses: actions/setup-node@v4.0.1
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
### Note
|
### Note
|
||||||
- Node.js 20.10.0が最小要件になりました
|
- Node.js 20.10.0が最小要件になりました
|
||||||
|
- 絵文字の追加辞書を既にインストールしている場合は、お手数ですが再インストールのほどお願いします
|
||||||
- 絵文字ピッカーにピン留め表示する絵文字設定が「リアクション用」と「絵文字入力用」に分かれました。以前の設定は「リアクション用」として使用されます。
|
- 絵文字ピッカーにピン留め表示する絵文字設定が「リアクション用」と「絵文字入力用」に分かれました。以前の設定は「リアクション用」として使用されます。
|
||||||
|
|
||||||
**影響:**
|
**影響:**
|
||||||
|
@ -31,9 +32,12 @@
|
||||||
- Feat: メールアドレスの認証にverifymail.ioを使えるように (cherry-pick from https://github.com/TeamNijimiss/misskey/commit/971ba07a44550f68d2ba31c62066db2d43a0caed)
|
- Feat: メールアドレスの認証にverifymail.ioを使えるように (cherry-pick from https://github.com/TeamNijimiss/misskey/commit/971ba07a44550f68d2ba31c62066db2d43a0caed)
|
||||||
- Feat: モデレーターがユーザーのアイコンもしくはバナー画像を未設定状態にできる機能を追加 (cherry-pick from https://github.com/TeamNijimiss/misskey/commit/e0eb5a752f6e5616d6312bb7c9790302f9dbff83)
|
- Feat: モデレーターがユーザーのアイコンもしくはバナー画像を未設定状態にできる機能を追加 (cherry-pick from https://github.com/TeamNijimiss/misskey/commit/e0eb5a752f6e5616d6312bb7c9790302f9dbff83)
|
||||||
- Feat: TL上からノートが見えなくなるワードミュートであるハードミュートを追加
|
- Feat: TL上からノートが見えなくなるワードミュートであるハードミュートを追加
|
||||||
|
- Enhance: 公開ロールにアサインされたときに通知が作成されるように
|
||||||
- Enhance: アイコンデコレーションを複数設定できるように
|
- Enhance: アイコンデコレーションを複数設定できるように
|
||||||
- Enhance: アイコンデコレーションの位置を微調整できるように
|
- Enhance: アイコンデコレーションの位置を微調整できるように
|
||||||
- Enhance: つながりの公開範囲をフォロー/フォロワーで個別に設定可能に #12072
|
- Enhance: つながりの公開範囲をフォロー/フォロワーで個別に設定可能に #12072
|
||||||
|
- Enhance: ローカリゼーションの更新
|
||||||
|
- Enhance: 依存関係の更新
|
||||||
- Fix: MFM `$[unixtime ]` に不正な値を入力した際に発生する各種エラーを修正
|
- Fix: MFM `$[unixtime ]` に不正な値を入力した際に発生する各種エラーを修正
|
||||||
|
|
||||||
### Client
|
### Client
|
||||||
|
@ -79,6 +83,7 @@
|
||||||
- Fix: AiScriptの`readline`が不正な値を返すことがある問題を修正
|
- Fix: AiScriptの`readline`が不正な値を返すことがある問題を修正
|
||||||
- Fix: 投票のみ/画像のみの引用RNが、通知欄でただのRNとして判定されるバグを修正
|
- Fix: 投票のみ/画像のみの引用RNが、通知欄でただのRNとして判定されるバグを修正
|
||||||
- Fix: CWをつけて引用RNしても、普通のRNとして扱われてしまうバグを修正しました。
|
- Fix: CWをつけて引用RNしても、普通のRNとして扱われてしまうバグを修正しました。
|
||||||
|
- Fix: 「画像が1枚のみのメディアリストの高さ」を「デフォルト」以外に設定していると、CWの中などに添付された画像が見られないバグを修正
|
||||||
|
|
||||||
### Server
|
### Server
|
||||||
- Enhance: MFM `$[ruby ]` が他ソフトウェアと連合されるように
|
- Enhance: MFM `$[ruby ]` が他ソフトウェアと連合されるように
|
||||||
|
@ -97,6 +102,7 @@
|
||||||
- Fix: 「みつける」が年越し時に壊れる問題を修正
|
- Fix: 「みつける」が年越し時に壊れる問題を修正
|
||||||
- Fix: アカウントをブロックした際に、自身のユーザーのページでノートが相手に表示される問題を修正
|
- Fix: アカウントをブロックした際に、自身のユーザーのページでノートが相手に表示される問題を修正
|
||||||
- Fix: モデレーションログがモデレーターは閲覧できないように修正
|
- Fix: モデレーションログがモデレーターは閲覧できないように修正
|
||||||
|
- Fix: ハッシュタグのトレンド除外設定が即時に効果を持つように修正
|
||||||
- Fix: HTTP Digestヘッダのアルゴリズム部分に大文字の"SHA-256"しか使えない
|
- Fix: HTTP Digestヘッダのアルゴリズム部分に大文字の"SHA-256"しか使えない
|
||||||
- Fix: 管理者用APIのアクセス権限が適切に設定されていない問題を修正
|
- Fix: 管理者用APIのアクセス権限が適切に設定されていない問題を修正
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ spec:
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 3000
|
- containerPort: 3000
|
||||||
- name: postgres
|
- name: postgres
|
||||||
image: postgres:14-alpine
|
image: postgres:15-alpine
|
||||||
env:
|
env:
|
||||||
- name: POSTGRES_USER
|
- name: POSTGRES_USER
|
||||||
value: "example-cherrypick-user"
|
value: "example-cherrypick-user"
|
||||||
|
@ -38,7 +38,7 @@ spec:
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 5432
|
- containerPort: 5432
|
||||||
- name: redis
|
- name: redis
|
||||||
image: redis:alpine
|
image: redis:7-alpine
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 6379
|
- containerPort: 6379
|
||||||
volumes:
|
volumes:
|
||||||
|
|
1
locales/index.d.ts
vendored
1
locales/index.d.ts
vendored
|
@ -2642,6 +2642,7 @@ export interface Locale {
|
||||||
"pollEnded": string;
|
"pollEnded": string;
|
||||||
"newNote": string;
|
"newNote": string;
|
||||||
"unreadAntennaNote": string;
|
"unreadAntennaNote": string;
|
||||||
|
"roleAssigned": string;
|
||||||
"emptyPushNotificationMessage": string;
|
"emptyPushNotificationMessage": string;
|
||||||
"achievementEarned": string;
|
"achievementEarned": string;
|
||||||
"testNotification": string;
|
"testNotification": string;
|
||||||
|
|
|
@ -2540,6 +2540,7 @@ _notification:
|
||||||
pollEnded: "アンケートの結果が出ました"
|
pollEnded: "アンケートの結果が出ました"
|
||||||
newNote: "新しい投稿"
|
newNote: "新しい投稿"
|
||||||
unreadAntennaNote: "アンテナ {name}"
|
unreadAntennaNote: "アンテナ {name}"
|
||||||
|
roleAssigned: "ロールが付与されました"
|
||||||
emptyPushNotificationMessage: "プッシュ通知の更新をしました"
|
emptyPushNotificationMessage: "プッシュ通知の更新をしました"
|
||||||
achievementEarned: "実績を獲得"
|
achievementEarned: "実績を獲得"
|
||||||
testNotification: "通知テスト"
|
testNotification: "通知テスト"
|
||||||
|
|
|
@ -974,8 +974,8 @@ makeReactionsPublicDescription: "나의 리액션을 누구나 볼 수 있게
|
||||||
classic: "클래식"
|
classic: "클래식"
|
||||||
muteThread: "이 글타래 뮤트"
|
muteThread: "이 글타래 뮤트"
|
||||||
unmuteThread: "글타래 뮤트 해제"
|
unmuteThread: "글타래 뮤트 해제"
|
||||||
ffVisibility: "내 인맥의 공개 범위"
|
followingVisibility: "팔로우의 공개 범위"
|
||||||
ffVisibilityDescription: "나의 팔로우와 팔로워 정보에 대한 공개 범위를 설정할 수 있어요."
|
followersVisibility: "팔로워의 공개 범위"
|
||||||
continueThread: "이 글타래 이어서 보기"
|
continueThread: "이 글타래 이어서 보기"
|
||||||
deleteAccountConfirm: "계정이 삭제되고 되돌릴 수 없게 돼요. 그래도 계속할까요? "
|
deleteAccountConfirm: "계정이 삭제되고 되돌릴 수 없게 돼요. 그래도 계속할까요? "
|
||||||
incorrectPassword: "비밀번호가 다른 것 같아요!"
|
incorrectPassword: "비밀번호가 다른 것 같아요!"
|
||||||
|
@ -1281,7 +1281,8 @@ code: "코드"
|
||||||
reloadRequiredToApplySettings: "설정을 반영하려면 페이지를 다시 불러와야 해요."
|
reloadRequiredToApplySettings: "설정을 반영하려면 페이지를 다시 불러와야 해요."
|
||||||
remainingN: "남음: {n}"
|
remainingN: "남음: {n}"
|
||||||
overwriteContentConfirm: "현재 내용을 덮어쓰기 하게 돼요. 그래도 계속 진행할까요?"
|
overwriteContentConfirm: "현재 내용을 덮어쓰기 하게 돼요. 그래도 계속 진행할까요?"
|
||||||
seasonalScreenEffect: "계절 따른 화면 연출"
|
seasonalScreenEffect: "계절에 따른 화면 연출"
|
||||||
|
decorate: "장식하기"
|
||||||
showUnreadNotificationsCount: "읽지 않은 알림 수 표시"
|
showUnreadNotificationsCount: "읽지 않은 알림 수 표시"
|
||||||
showCatOnly: "고양이만 보기"
|
showCatOnly: "고양이만 보기"
|
||||||
additionalPermissionsForFlash: "Play에 대한 추가 권한"
|
additionalPermissionsForFlash: "Play에 대한 추가 권한"
|
||||||
|
@ -2134,7 +2135,7 @@ _sfx:
|
||||||
channel: "채널 알림"
|
channel: "채널 알림"
|
||||||
reaction: "리액션 선택"
|
reaction: "리액션 선택"
|
||||||
_soundSettings:
|
_soundSettings:
|
||||||
driveFile: "드라이브에 있는 오디오 사용"
|
driveFile: "드라이브에 있는 오디오 파일 사용"
|
||||||
driveFileWarn: "드라이브에 있는 파일을 선택해 주세요."
|
driveFileWarn: "드라이브에 있는 파일을 선택해 주세요."
|
||||||
driveFileTypeWarn: "이 파일은 지원되지 않는 형식이에요."
|
driveFileTypeWarn: "이 파일은 지원되지 않는 형식이에요."
|
||||||
driveFileTypeWarnDescription: "오디오 파일을 선택해 주세요."
|
driveFileTypeWarnDescription: "오디오 파일을 선택해 주세요."
|
||||||
|
|
|
@ -1231,7 +1231,7 @@ _initialTutorial:
|
||||||
skipAreYouSure: "結束教學模式?"
|
skipAreYouSure: "結束教學模式?"
|
||||||
_landing:
|
_landing:
|
||||||
title: "歡迎使用本教學課程"
|
title: "歡迎使用本教學課程"
|
||||||
description: "在這裡您可以查看CherryPick的基本使用方法和功能。"
|
description: "在這裡您可以查看 CherryPick 的基本使用方法和功能。"
|
||||||
_note:
|
_note:
|
||||||
title: "什麼是貼文?"
|
title: "什麼是貼文?"
|
||||||
description: "在CherryPick上發布的內容稱為「貼文」。貼文在時間軸上按時間順序排列,並即時更新。"
|
description: "在CherryPick上發布的內容稱為「貼文」。貼文在時間軸上按時間順序排列,並即時更新。"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "cherrypick",
|
"name": "cherrypick",
|
||||||
"version": "4.6.0-beta.5",
|
"version": "4.6.0-beta.5",
|
||||||
"basedMisskeyVersion": "2023.12.0-beta.5",
|
"basedMisskeyVersion": "2023.12.0-beta.6",
|
||||||
"codename": "nasubi",
|
"codename": "nasubi",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|
|
@ -77,6 +77,17 @@ export class FeaturedService {
|
||||||
return Array.from(ranking.keys());
|
return Array.from(ranking.keys());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
private async removeFromRanking(name: string, windowRange: number, element: string): Promise<void> {
|
||||||
|
const currentWindow = this.getCurrentWindow(windowRange);
|
||||||
|
const previousWindow = currentWindow - 1;
|
||||||
|
|
||||||
|
const redisPipeline = this.redisClient.pipeline();
|
||||||
|
redisPipeline.zrem(`${name}:${currentWindow}`, element);
|
||||||
|
redisPipeline.zrem(`${name}:${previousWindow}`, element);
|
||||||
|
await redisPipeline.exec();
|
||||||
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public updateGlobalNotesRanking(noteId: MiNote['id'], score = 1): Promise<void> {
|
public updateGlobalNotesRanking(noteId: MiNote['id'], score = 1): Promise<void> {
|
||||||
return this.updateRankingOf('featuredGlobalNotesRanking', GLOBAL_NOTES_RANKING_WINDOW, noteId, score);
|
return this.updateRankingOf('featuredGlobalNotesRanking', GLOBAL_NOTES_RANKING_WINDOW, noteId, score);
|
||||||
|
@ -126,4 +137,9 @@ export class FeaturedService {
|
||||||
public getHashtagsRanking(threshold: number): Promise<string[]> {
|
public getHashtagsRanking(threshold: number): Promise<string[]> {
|
||||||
return this.getRankingOf('featuredHashtagsRanking', HASHTAG_RANKING_WINDOW, threshold);
|
return this.getRankingOf('featuredHashtagsRanking', HASHTAG_RANKING_WINDOW, threshold);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public removeHashtagsFromRanking(hashtag: string): Promise<void> {
|
||||||
|
return this.removeFromRanking('featuredHashtagsRanking', HASHTAG_RANKING_WINDOW, hashtag);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import { MiMeta } from '@/models/Meta.js';
|
||||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import type { GlobalEvents } from '@/core/GlobalEventService.js';
|
import type { GlobalEvents } from '@/core/GlobalEventService.js';
|
||||||
|
import { FeaturedService } from '@/core/FeaturedService.js';
|
||||||
import type { OnApplicationShutdown } from '@nestjs/common';
|
import type { OnApplicationShutdown } from '@nestjs/common';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
@ -25,6 +26,7 @@ export class MetaService implements OnApplicationShutdown {
|
||||||
@Inject(DI.db)
|
@Inject(DI.db)
|
||||||
private db: DataSource,
|
private db: DataSource,
|
||||||
|
|
||||||
|
private featuredService: FeaturedService,
|
||||||
private globalEventService: GlobalEventService,
|
private globalEventService: GlobalEventService,
|
||||||
) {
|
) {
|
||||||
//this.onMessage = this.onMessage.bind(this);
|
//this.onMessage = this.onMessage.bind(this);
|
||||||
|
@ -95,6 +97,8 @@ export class MetaService implements OnApplicationShutdown {
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async update(data: Partial<MiMeta>): Promise<MiMeta> {
|
public async update(data: Partial<MiMeta>): Promise<MiMeta> {
|
||||||
|
let before: MiMeta | undefined;
|
||||||
|
|
||||||
const updated = await this.db.transaction(async transactionalEntityManager => {
|
const updated = await this.db.transaction(async transactionalEntityManager => {
|
||||||
const metas = await transactionalEntityManager.find(MiMeta, {
|
const metas = await transactionalEntityManager.find(MiMeta, {
|
||||||
order: {
|
order: {
|
||||||
|
@ -102,10 +106,10 @@ export class MetaService implements OnApplicationShutdown {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const meta = metas[0];
|
before = metas[0];
|
||||||
|
|
||||||
if (meta) {
|
if (before) {
|
||||||
await transactionalEntityManager.update(MiMeta, meta.id, data);
|
await transactionalEntityManager.update(MiMeta, before.id, data);
|
||||||
|
|
||||||
const metas = await transactionalEntityManager.find(MiMeta, {
|
const metas = await transactionalEntityManager.find(MiMeta, {
|
||||||
order: {
|
order: {
|
||||||
|
@ -119,6 +123,21 @@ export class MetaService implements OnApplicationShutdown {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (data.hiddenTags) {
|
||||||
|
process.nextTick(() => {
|
||||||
|
const hiddenTags = new Set<string>(data.hiddenTags);
|
||||||
|
if (before) {
|
||||||
|
for (const previousHiddenTag of before.hiddenTags) {
|
||||||
|
hiddenTags.delete(previousHiddenTag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const hiddenTag of hiddenTags) {
|
||||||
|
this.featuredService.removeHashtagsFromRanking(hiddenTag);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
this.globalEventService.publishInternalEvent('metaUpdated', updated);
|
this.globalEventService.publishInternalEvent('metaUpdated', updated);
|
||||||
|
|
||||||
return updated;
|
return updated;
|
||||||
|
|
|
@ -6,7 +6,14 @@
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import * as Redis from 'ioredis';
|
import * as Redis from 'ioredis';
|
||||||
import { In } from 'typeorm';
|
import { In } from 'typeorm';
|
||||||
import type { MiRole, MiRoleAssignment, RoleAssignmentsRepository, RolesRepository, UsersRepository } from '@/models/_.js';
|
import { ModuleRef } from '@nestjs/core';
|
||||||
|
import type {
|
||||||
|
MiRole,
|
||||||
|
MiRoleAssignment,
|
||||||
|
RoleAssignmentsRepository,
|
||||||
|
RolesRepository,
|
||||||
|
UsersRepository,
|
||||||
|
} from '@/models/_.js';
|
||||||
import { MemoryKVCache, MemorySingleCache } from '@/misc/cache.js';
|
import { MemoryKVCache, MemorySingleCache } from '@/misc/cache.js';
|
||||||
import type { MiUser } from '@/models/User.js';
|
import type { MiUser } from '@/models/User.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
|
@ -16,12 +23,13 @@ import { CacheService } from '@/core/CacheService.js';
|
||||||
import type { RoleCondFormulaValue } from '@/models/Role.js';
|
import type { RoleCondFormulaValue } from '@/models/Role.js';
|
||||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||||
import type { GlobalEvents } from '@/core/GlobalEventService.js';
|
import type { GlobalEvents } from '@/core/GlobalEventService.js';
|
||||||
import { IdService } from '@/core/IdService.js';
|
|
||||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||||
|
import { IdService } from '@/core/IdService.js';
|
||||||
import { ModerationLogService } from '@/core/ModerationLogService.js';
|
import { ModerationLogService } from '@/core/ModerationLogService.js';
|
||||||
import type { Packed } from '@/misc/json-schema.js';
|
import type { Packed } from '@/misc/json-schema.js';
|
||||||
import { FanoutTimelineService } from '@/core/FanoutTimelineService.js';
|
import { FanoutTimelineService } from '@/core/FanoutTimelineService.js';
|
||||||
import type { OnApplicationShutdown } from '@nestjs/common';
|
import { NotificationService } from '@/core/NotificationService.js';
|
||||||
|
import type { OnApplicationShutdown, OnModuleInit } from '@nestjs/common';
|
||||||
|
|
||||||
export type RolePolicies = {
|
export type RolePolicies = {
|
||||||
gtlAvailable: boolean;
|
gtlAvailable: boolean;
|
||||||
|
@ -80,14 +88,17 @@ export const DEFAULT_POLICIES: RolePolicies = {
|
||||||
};
|
};
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class RoleService implements OnApplicationShutdown {
|
export class RoleService implements OnApplicationShutdown, OnModuleInit {
|
||||||
private rolesCache: MemorySingleCache<MiRole[]>;
|
private rolesCache: MemorySingleCache<MiRole[]>;
|
||||||
private roleAssignmentByUserIdCache: MemoryKVCache<MiRoleAssignment[]>;
|
private roleAssignmentByUserIdCache: MemoryKVCache<MiRoleAssignment[]>;
|
||||||
|
private notificationService: NotificationService;
|
||||||
|
|
||||||
public static AlreadyAssignedError = class extends Error {};
|
public static AlreadyAssignedError = class extends Error {};
|
||||||
public static NotAssignedError = class extends Error {};
|
public static NotAssignedError = class extends Error {};
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
private moduleRef: ModuleRef,
|
||||||
|
|
||||||
@Inject(DI.redis)
|
@Inject(DI.redis)
|
||||||
private redisClient: Redis.Redis,
|
private redisClient: Redis.Redis,
|
||||||
|
|
||||||
|
@ -122,6 +133,10 @@ export class RoleService implements OnApplicationShutdown {
|
||||||
this.redisForSub.on('message', this.onMessage);
|
this.redisForSub.on('message', this.onMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async onModuleInit() {
|
||||||
|
this.notificationService = this.moduleRef.get(NotificationService.name);
|
||||||
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
private async onMessage(_: string, data: string): Promise<void> {
|
private async onMessage(_: string, data: string): Promise<void> {
|
||||||
const obj = JSON.parse(data);
|
const obj = JSON.parse(data);
|
||||||
|
@ -430,6 +445,12 @@ export class RoleService implements OnApplicationShutdown {
|
||||||
|
|
||||||
this.globalEventService.publishInternalEvent('userRoleAssigned', created);
|
this.globalEventService.publishInternalEvent('userRoleAssigned', created);
|
||||||
|
|
||||||
|
if (role.isPublic) {
|
||||||
|
this.notificationService.createNotification(userId, 'roleAssigned', {
|
||||||
|
roleId: roleId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (moderator) {
|
if (moderator) {
|
||||||
const user = await this.usersRepository.findOneByOrFail({ id: userId });
|
const user = await this.usersRepository.findOneByOrFail({ id: userId });
|
||||||
this.moderationLogService.log(moderator, 'assignRole', {
|
this.moderationLogService.log(moderator, 'assignRole', {
|
||||||
|
|
|
@ -10,15 +10,15 @@ import type { MiUser } from '@/models/User.js';
|
||||||
import type { MiUserList } from '@/models/UserList.js';
|
import type { MiUserList } from '@/models/UserList.js';
|
||||||
import type { MiUserListMembership } from '@/models/UserListMembership.js';
|
import type { MiUserListMembership } from '@/models/UserListMembership.js';
|
||||||
import { IdService } from '@/core/IdService.js';
|
import { IdService } from '@/core/IdService.js';
|
||||||
|
import type { GlobalEvents } from '@/core/GlobalEventService.js';
|
||||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||||
import { ProxyAccountService } from '@/core/ProxyAccountService.js';
|
import { ProxyAccountService } from '@/core/ProxyAccountService.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { RoleService } from '@/core/RoleService.js';
|
|
||||||
import { QueueService } from '@/core/QueueService.js';
|
import { QueueService } from '@/core/QueueService.js';
|
||||||
import { RedisKVCache } from '@/misc/cache.js';
|
import { RedisKVCache } from '@/misc/cache.js';
|
||||||
import type { GlobalEvents } from '@/core/GlobalEventService.js';
|
import { RoleService } from '@/core/RoleService.js';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class UserListService implements OnApplicationShutdown {
|
export class UserListService implements OnApplicationShutdown {
|
||||||
|
|
|
@ -15,8 +15,8 @@ import type { Packed } from '@/misc/json-schema.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { isNotNull } from '@/misc/is-not-null.js';
|
import { isNotNull } from '@/misc/is-not-null.js';
|
||||||
import { FilterUnionByProperty, notificationTypes } from '@/types.js';
|
import { FilterUnionByProperty, notificationTypes } from '@/types.js';
|
||||||
|
import { RoleEntityService } from './RoleEntityService.js';
|
||||||
import type { OnModuleInit } from '@nestjs/common';
|
import type { OnModuleInit } from '@nestjs/common';
|
||||||
import type { CustomEmojiService } from '../CustomEmojiService.js';
|
|
||||||
import type { UserEntityService } from './UserEntityService.js';
|
import type { UserEntityService } from './UserEntityService.js';
|
||||||
import type { NoteEntityService } from './NoteEntityService.js';
|
import type { NoteEntityService } from './NoteEntityService.js';
|
||||||
import type { UserGroupInvitationEntityService } from './UserGroupInvitationEntityService.js';
|
import type { UserGroupInvitationEntityService } from './UserGroupInvitationEntityService.js';
|
||||||
|
@ -28,8 +28,8 @@ const NOTE_REQUIRED_GROUPED_NOTIFICATION_TYPES = new Set(['note', 'mention', 're
|
||||||
export class NotificationEntityService implements OnModuleInit {
|
export class NotificationEntityService implements OnModuleInit {
|
||||||
private userEntityService: UserEntityService;
|
private userEntityService: UserEntityService;
|
||||||
private noteEntityService: NoteEntityService;
|
private noteEntityService: NoteEntityService;
|
||||||
|
private roleEntityService: RoleEntityService;
|
||||||
private userGroupInvitationEntityService: UserGroupInvitationEntityService;
|
private userGroupInvitationEntityService: UserGroupInvitationEntityService;
|
||||||
private customEmojiService: CustomEmojiService;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private moduleRef: ModuleRef,
|
private moduleRef: ModuleRef,
|
||||||
|
@ -49,15 +49,14 @@ export class NotificationEntityService implements OnModuleInit {
|
||||||
//private userEntityService: UserEntityService,
|
//private userEntityService: UserEntityService,
|
||||||
//private noteEntityService: NoteEntityService,
|
//private noteEntityService: NoteEntityService,
|
||||||
//private userGroupInvitationEntityService: UserGroupInvitationEntityService,
|
//private userGroupInvitationEntityService: UserGroupInvitationEntityService,
|
||||||
//private customEmojiService: CustomEmojiService,
|
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
onModuleInit() {
|
onModuleInit() {
|
||||||
this.userEntityService = this.moduleRef.get('UserEntityService');
|
this.userEntityService = this.moduleRef.get('UserEntityService');
|
||||||
this.noteEntityService = this.moduleRef.get('NoteEntityService');
|
this.noteEntityService = this.moduleRef.get('NoteEntityService');
|
||||||
|
this.roleEntityService = this.moduleRef.get('RoleEntityService');
|
||||||
this.userGroupInvitationEntityService = this.moduleRef.get('UserGroupInvitationEntityService');
|
this.userGroupInvitationEntityService = this.moduleRef.get('UserGroupInvitationEntityService');
|
||||||
this.customEmojiService = this.moduleRef.get('CustomEmojiService');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
|
@ -88,6 +87,7 @@ export class NotificationEntityService implements OnModuleInit {
|
||||||
detail: false,
|
detail: false,
|
||||||
})
|
})
|
||||||
) : undefined;
|
) : undefined;
|
||||||
|
const role = notification.type === 'roleAssigned' ? await this.roleEntityService.pack(notification.roleId) : undefined;
|
||||||
|
|
||||||
return await awaitAll({
|
return await awaitAll({
|
||||||
id: notification.id,
|
id: notification.id,
|
||||||
|
@ -99,6 +99,9 @@ export class NotificationEntityService implements OnModuleInit {
|
||||||
...(notification.type === 'reaction' ? {
|
...(notification.type === 'reaction' ? {
|
||||||
reaction: notification.reaction,
|
reaction: notification.reaction,
|
||||||
} : {}),
|
} : {}),
|
||||||
|
...(notification.type === 'roleAssigned' ? {
|
||||||
|
role: role,
|
||||||
|
} : {}),
|
||||||
// ...(notification.type === 'pollEnded' ? {
|
// ...(notification.type === 'pollEnded' ? {
|
||||||
// note: this.noteEntityService.pack(notification.note ?? notification.noteId!, { id: notification.notifieeId }, {
|
// note: this.noteEntityService.pack(notification.note ?? notification.noteId!, { id: notification.notifieeId }, {
|
||||||
// detail: true,
|
// detail: true,
|
||||||
|
@ -240,6 +243,8 @@ export class NotificationEntityService implements OnModuleInit {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const role = notification.type === 'roleAssigned' ? await this.roleEntityService.pack(notification.roleId) : undefined;
|
||||||
|
|
||||||
return await awaitAll({
|
return await awaitAll({
|
||||||
id: notification.id,
|
id: notification.id,
|
||||||
createdAt: new Date(notification.createdAt).toISOString(),
|
createdAt: new Date(notification.createdAt).toISOString(),
|
||||||
|
@ -250,6 +255,9 @@ export class NotificationEntityService implements OnModuleInit {
|
||||||
...(notification.type === 'reaction' ? {
|
...(notification.type === 'reaction' ? {
|
||||||
reaction: notification.reaction,
|
reaction: notification.reaction,
|
||||||
} : {}),
|
} : {}),
|
||||||
|
...(notification.type === 'roleAssigned' ? {
|
||||||
|
role: role,
|
||||||
|
} : {}),
|
||||||
...(notification.type === 'groupInvited' ? {
|
...(notification.type === 'groupInvited' ? {
|
||||||
invitation: this.userGroupInvitationEntityService.pack(notification.userGroupInvitationId),
|
invitation: this.userGroupInvitationEntityService.pack(notification.userGroupInvitationId),
|
||||||
} : {}),
|
} : {}),
|
||||||
|
|
|
@ -40,6 +40,7 @@ import { packedFlashSchema } from '@/models/json-schema/flash.js';
|
||||||
import { packedAnnouncementSchema } from '@/models/json-schema/announcement.js';
|
import { packedAnnouncementSchema } from '@/models/json-schema/announcement.js';
|
||||||
import { packedSigninSchema } from '@/models/json-schema/signin.js';
|
import { packedSigninSchema } from '@/models/json-schema/signin.js';
|
||||||
import { packedRoleLiteSchema, packedRoleSchema } from '@/models/json-schema/role.js';
|
import { packedRoleLiteSchema, packedRoleSchema } from '@/models/json-schema/role.js';
|
||||||
|
import { packedAdSchema } from '@/models/json-schema/ad.js';
|
||||||
|
|
||||||
export const refs = {
|
export const refs = {
|
||||||
UserLite: packedUserLiteSchema,
|
UserLite: packedUserLiteSchema,
|
||||||
|
@ -52,6 +53,7 @@ export const refs = {
|
||||||
|
|
||||||
UserList: packedUserListSchema,
|
UserList: packedUserListSchema,
|
||||||
UserGroup: packedUserGroupSchema,
|
UserGroup: packedUserGroupSchema,
|
||||||
|
Ad: packedAdSchema,
|
||||||
Announcement: packedAnnouncementSchema,
|
Announcement: packedAnnouncementSchema,
|
||||||
App: packedAppSchema,
|
App: packedAppSchema,
|
||||||
MessagingMessage: packedMessagingMessageSchema,
|
MessagingMessage: packedMessagingMessageSchema,
|
||||||
|
|
|
@ -3,12 +3,11 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { notificationTypes } from '@/types.js';
|
|
||||||
import { MiUser } from './User.js';
|
import { MiUser } from './User.js';
|
||||||
import { MiNote } from './Note.js';
|
import { MiNote } from './Note.js';
|
||||||
import { MiFollowRequest } from './FollowRequest.js';
|
|
||||||
import { MiUserGroupInvitation } from './UserGroupInvitation.js';
|
import { MiUserGroupInvitation } from './UserGroupInvitation.js';
|
||||||
import { MiAccessToken } from './AccessToken.js';
|
import { MiAccessToken } from './AccessToken.js';
|
||||||
|
import { MiRole } from './Role.js';
|
||||||
|
|
||||||
export type MiNotification = {
|
export type MiNotification = {
|
||||||
type: 'note';
|
type: 'note';
|
||||||
|
@ -69,6 +68,11 @@ export type MiNotification = {
|
||||||
id: string;
|
id: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
notifierId: MiUser['id'];
|
notifierId: MiUser['id'];
|
||||||
|
} | {
|
||||||
|
type: 'roleAssigned';
|
||||||
|
id: string;
|
||||||
|
createdAt: string;
|
||||||
|
roleId: MiRole['id'];
|
||||||
} | {
|
} | {
|
||||||
type: 'groupInvited';
|
type: 'groupInvited';
|
||||||
id: string;
|
id: string;
|
||||||
|
|
64
packages/backend/src/models/json-schema/ad.ts
Normal file
64
packages/backend/src/models/json-schema/ad.ts
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and other misskey, cherrypick contributors
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const packedAdSchema = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
id: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false,
|
||||||
|
nullable: false,
|
||||||
|
format: 'id',
|
||||||
|
example: 'xxxxxxxxxx',
|
||||||
|
},
|
||||||
|
expiresAt: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false,
|
||||||
|
nullable: false,
|
||||||
|
format: 'date-time',
|
||||||
|
},
|
||||||
|
startsAt: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false,
|
||||||
|
nullable: false,
|
||||||
|
format: 'date-time',
|
||||||
|
},
|
||||||
|
place: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false,
|
||||||
|
nullable: false,
|
||||||
|
},
|
||||||
|
priority: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false,
|
||||||
|
nullable: false,
|
||||||
|
},
|
||||||
|
ratio: {
|
||||||
|
type: 'number',
|
||||||
|
optional: false,
|
||||||
|
nullable: false,
|
||||||
|
},
|
||||||
|
url: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false,
|
||||||
|
nullable: false,
|
||||||
|
},
|
||||||
|
imageUrl: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false,
|
||||||
|
nullable: false,
|
||||||
|
},
|
||||||
|
memo: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false,
|
||||||
|
nullable: false,
|
||||||
|
},
|
||||||
|
dayOfWeek: {
|
||||||
|
type: 'integer',
|
||||||
|
optional: false,
|
||||||
|
nullable: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as const;
|
|
@ -566,9 +566,7 @@ export const packedMeDetailedOnlySchema = {
|
||||||
mention: notificationRecieveConfig,
|
mention: notificationRecieveConfig,
|
||||||
reaction: notificationRecieveConfig,
|
reaction: notificationRecieveConfig,
|
||||||
pollEnded: notificationRecieveConfig,
|
pollEnded: notificationRecieveConfig,
|
||||||
achievementEarned: notificationRecieveConfig,
|
|
||||||
receiveFollowRequest: notificationRecieveConfig,
|
receiveFollowRequest: notificationRecieveConfig,
|
||||||
followRequestAccepted: notificationRecieveConfig,
|
|
||||||
groupInvited: notificationRecieveConfig,
|
groupInvited: notificationRecieveConfig,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -155,8 +155,8 @@ export class QueueProcessorService implements OnApplicationShutdown {
|
||||||
this.systemQueueWorker
|
this.systemQueueWorker
|
||||||
.on('active', (job) => systemLogger.debug(`active id=${job.id}`))
|
.on('active', (job) => systemLogger.debug(`active id=${job.id}`))
|
||||||
.on('completed', (job, result) => systemLogger.debug(`completed(${result}) id=${job.id}`))
|
.on('completed', (job, result) => systemLogger.debug(`completed(${result}) id=${job.id}`))
|
||||||
.on('failed', (job, err) => systemLogger.warn(`failed(${err}) id=${job ? job.id : '-'}`, { job, e: renderError(err) }))
|
.on('failed', (job, err) => systemLogger.warn(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) }))
|
||||||
.on('error', (err: Error) => systemLogger.error(`error ${err}`, { e: renderError(err) }))
|
.on('error', (err: Error) => systemLogger.error(`error ${err.stack}`, { e: renderError(err) }))
|
||||||
.on('stalled', (jobId) => systemLogger.warn(`stalled id=${jobId}`));
|
.on('stalled', (jobId) => systemLogger.warn(`stalled id=${jobId}`));
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
|
@ -194,8 +194,8 @@ export class QueueProcessorService implements OnApplicationShutdown {
|
||||||
this.dbQueueWorker
|
this.dbQueueWorker
|
||||||
.on('active', (job) => dbLogger.debug(`active id=${job.id}`))
|
.on('active', (job) => dbLogger.debug(`active id=${job.id}`))
|
||||||
.on('completed', (job, result) => dbLogger.debug(`completed(${result}) id=${job.id}`))
|
.on('completed', (job, result) => dbLogger.debug(`completed(${result}) id=${job.id}`))
|
||||||
.on('failed', (job, err) => dbLogger.warn(`failed(${err}) id=${job ? job.id : '-'}`, { job, e: renderError(err) }))
|
.on('failed', (job, err) => dbLogger.warn(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) }))
|
||||||
.on('error', (err: Error) => dbLogger.error(`error ${err}`, { e: renderError(err) }))
|
.on('error', (err: Error) => dbLogger.error(`error ${err.stack}`, { e: renderError(err) }))
|
||||||
.on('stalled', (jobId) => dbLogger.warn(`stalled id=${jobId}`));
|
.on('stalled', (jobId) => dbLogger.warn(`stalled id=${jobId}`));
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
|
@ -218,8 +218,8 @@ export class QueueProcessorService implements OnApplicationShutdown {
|
||||||
this.deliverQueueWorker
|
this.deliverQueueWorker
|
||||||
.on('active', (job) => deliverLogger.debug(`active ${getJobInfo(job, true)} to=${job.data.to}`))
|
.on('active', (job) => deliverLogger.debug(`active ${getJobInfo(job, true)} to=${job.data.to}`))
|
||||||
.on('completed', (job, result) => deliverLogger.debug(`completed(${result}) ${getJobInfo(job, true)} to=${job.data.to}`))
|
.on('completed', (job, result) => deliverLogger.debug(`completed(${result}) ${getJobInfo(job, true)} to=${job.data.to}`))
|
||||||
.on('failed', (job, err) => deliverLogger.warn(`failed(${err}) ${getJobInfo(job)} to=${job ? job.data.to : '-'}`))
|
.on('failed', (job, err) => deliverLogger.warn(`failed(${err.stack}) ${getJobInfo(job)} to=${job ? job.data.to : '-'}`))
|
||||||
.on('error', (err: Error) => deliverLogger.error(`error ${err}`, { e: renderError(err) }))
|
.on('error', (err: Error) => deliverLogger.error(`error ${err.stack}`, { e: renderError(err) }))
|
||||||
.on('stalled', (jobId) => deliverLogger.warn(`stalled id=${jobId}`));
|
.on('stalled', (jobId) => deliverLogger.warn(`stalled id=${jobId}`));
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
|
@ -242,8 +242,8 @@ export class QueueProcessorService implements OnApplicationShutdown {
|
||||||
this.inboxQueueWorker
|
this.inboxQueueWorker
|
||||||
.on('active', (job) => inboxLogger.debug(`active ${getJobInfo(job, true)}`))
|
.on('active', (job) => inboxLogger.debug(`active ${getJobInfo(job, true)}`))
|
||||||
.on('completed', (job, result) => inboxLogger.debug(`completed(${result}) ${getJobInfo(job, true)}`))
|
.on('completed', (job, result) => inboxLogger.debug(`completed(${result}) ${getJobInfo(job, true)}`))
|
||||||
.on('failed', (job, err) => inboxLogger.warn(`failed(${err}) ${getJobInfo(job)} activity=${job ? (job.data.activity ? job.data.activity.id : 'none') : '-'}`, { job, e: renderError(err) }))
|
.on('failed', (job, err) => inboxLogger.warn(`failed(${err.stack}) ${getJobInfo(job)} activity=${job ? (job.data.activity ? job.data.activity.id : 'none') : '-'}`, { job, e: renderError(err) }))
|
||||||
.on('error', (err: Error) => inboxLogger.error(`error ${err}`, { e: renderError(err) }))
|
.on('error', (err: Error) => inboxLogger.error(`error ${err.stack}`, { e: renderError(err) }))
|
||||||
.on('stalled', (jobId) => inboxLogger.warn(`stalled id=${jobId}`));
|
.on('stalled', (jobId) => inboxLogger.warn(`stalled id=${jobId}`));
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
|
@ -266,8 +266,8 @@ export class QueueProcessorService implements OnApplicationShutdown {
|
||||||
this.webhookDeliverQueueWorker
|
this.webhookDeliverQueueWorker
|
||||||
.on('active', (job) => webhookLogger.debug(`active ${getJobInfo(job, true)} to=${job.data.to}`))
|
.on('active', (job) => webhookLogger.debug(`active ${getJobInfo(job, true)} to=${job.data.to}`))
|
||||||
.on('completed', (job, result) => webhookLogger.debug(`completed(${result}) ${getJobInfo(job, true)} to=${job.data.to}`))
|
.on('completed', (job, result) => webhookLogger.debug(`completed(${result}) ${getJobInfo(job, true)} to=${job.data.to}`))
|
||||||
.on('failed', (job, err) => webhookLogger.warn(`failed(${err}) ${getJobInfo(job)} to=${job ? job.data.to : '-'}`))
|
.on('failed', (job, err) => webhookLogger.warn(`failed(${err.stack}) ${getJobInfo(job)} to=${job ? job.data.to : '-'}`))
|
||||||
.on('error', (err: Error) => webhookLogger.error(`error ${err}`, { e: renderError(err) }))
|
.on('error', (err: Error) => webhookLogger.error(`error ${err.stack}`, { e: renderError(err) }))
|
||||||
.on('stalled', (jobId) => webhookLogger.warn(`stalled id=${jobId}`));
|
.on('stalled', (jobId) => webhookLogger.warn(`stalled id=${jobId}`));
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
|
@ -295,8 +295,8 @@ export class QueueProcessorService implements OnApplicationShutdown {
|
||||||
this.relationshipQueueWorker
|
this.relationshipQueueWorker
|
||||||
.on('active', (job) => relationshipLogger.debug(`active id=${job.id}`))
|
.on('active', (job) => relationshipLogger.debug(`active id=${job.id}`))
|
||||||
.on('completed', (job, result) => relationshipLogger.debug(`completed(${result}) id=${job.id}`))
|
.on('completed', (job, result) => relationshipLogger.debug(`completed(${result}) id=${job.id}`))
|
||||||
.on('failed', (job, err) => relationshipLogger.warn(`failed(${err}) id=${job ? job.id : '-'}`, { job, e: renderError(err) }))
|
.on('failed', (job, err) => relationshipLogger.warn(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) }))
|
||||||
.on('error', (err: Error) => relationshipLogger.error(`error ${err}`, { e: renderError(err) }))
|
.on('error', (err: Error) => relationshipLogger.error(`error ${err.stack}`, { e: renderError(err) }))
|
||||||
.on('stalled', (jobId) => relationshipLogger.warn(`stalled id=${jobId}`));
|
.on('stalled', (jobId) => relationshipLogger.warn(`stalled id=${jobId}`));
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
|
@ -318,8 +318,8 @@ export class QueueProcessorService implements OnApplicationShutdown {
|
||||||
this.objectStorageQueueWorker
|
this.objectStorageQueueWorker
|
||||||
.on('active', (job) => objectStorageLogger.debug(`active id=${job.id}`))
|
.on('active', (job) => objectStorageLogger.debug(`active id=${job.id}`))
|
||||||
.on('completed', (job, result) => objectStorageLogger.debug(`completed(${result}) id=${job.id}`))
|
.on('completed', (job, result) => objectStorageLogger.debug(`completed(${result}) id=${job.id}`))
|
||||||
.on('failed', (job, err) => objectStorageLogger.warn(`failed(${err}) id=${job ? job.id : '-'}`, { job, e: renderError(err) }))
|
.on('failed', (job, err) => objectStorageLogger.warn(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) }))
|
||||||
.on('error', (err: Error) => objectStorageLogger.error(`error ${err}`, { e: renderError(err) }))
|
.on('error', (err: Error) => objectStorageLogger.error(`error ${err.stack}`, { e: renderError(err) }))
|
||||||
.on('stalled', (jobId) => objectStorageLogger.warn(`stalled id=${jobId}`));
|
.on('stalled', (jobId) => objectStorageLogger.warn(`stalled id=${jobId}`));
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,11 @@ export const meta = {
|
||||||
id: 'cb865949-8af5-4062-a88c-ef55e8786d1d',
|
id: 'cb865949-8af5-4062-a88c-ef55e8786d1d',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
res: {
|
||||||
|
type: 'object',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
ref: 'User',
|
||||||
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
|
|
@ -17,6 +17,12 @@ export const meta = {
|
||||||
|
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
requireModerator: true,
|
requireModerator: true,
|
||||||
|
res: {
|
||||||
|
type: 'object',
|
||||||
|
optional: false,
|
||||||
|
nullable: false,
|
||||||
|
ref: 'Ad',
|
||||||
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
@ -63,7 +69,18 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
ad: ad,
|
ad: ad,
|
||||||
});
|
});
|
||||||
|
|
||||||
return ad;
|
return {
|
||||||
|
id: ad.id,
|
||||||
|
expiresAt: ad.expiresAt.toISOString(),
|
||||||
|
startsAt: ad.startsAt.toISOString(),
|
||||||
|
dayOfWeek: ad.dayOfWeek,
|
||||||
|
url: ad.url,
|
||||||
|
imageUrl: ad.imageUrl,
|
||||||
|
priority: ad.priority,
|
||||||
|
ratio: ad.ratio,
|
||||||
|
place: ad.place,
|
||||||
|
memo: ad.memo,
|
||||||
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,17 @@ export const meta = {
|
||||||
|
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
requireModerator: true,
|
requireModerator: true,
|
||||||
|
res: {
|
||||||
|
type: 'array',
|
||||||
|
optional: false,
|
||||||
|
nullable: false,
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
optional: false,
|
||||||
|
nullable: false,
|
||||||
|
ref: 'Ad',
|
||||||
|
},
|
||||||
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
@ -46,7 +57,18 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
}
|
}
|
||||||
const ads = await query.limit(ps.limit).getMany();
|
const ads = await query.limit(ps.limit).getMany();
|
||||||
|
|
||||||
return ads;
|
return ads.map(ad => ({
|
||||||
|
id: ad.id,
|
||||||
|
expiresAt: ad.expiresAt.toISOString(),
|
||||||
|
startsAt: ad.startsAt.toISOString(),
|
||||||
|
dayOfWeek: ad.dayOfWeek,
|
||||||
|
url: ad.url,
|
||||||
|
imageUrl: ad.imageUrl,
|
||||||
|
memo: ad.memo,
|
||||||
|
place: ad.place,
|
||||||
|
priority: ad.priority,
|
||||||
|
ratio: ad.ratio,
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,8 @@ export const meta = {
|
||||||
id: 'f7a3462c-4e6e-4069-8421-b9bd4f4c3975',
|
id: 'f7a3462c-4e6e-4069-8421-b9bd4f4c3975',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
ref: 'EmojiDetailed',
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
|
|
@ -32,6 +32,8 @@ export const meta = {
|
||||||
id: 'f7a3462c-4e6e-4069-8421-b9bd4f4c3975',
|
id: 'f7a3462c-4e6e-4069-8421-b9bd4f4c3975',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
ref: 'EmojiDetailed',
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
|
|
@ -15,6 +15,16 @@ export const meta = {
|
||||||
kind: 'read:admin',
|
kind: 'read:admin',
|
||||||
|
|
||||||
tags: ['admin'],
|
tags: ['admin'],
|
||||||
|
res: {
|
||||||
|
type: 'array',
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
tablename: { type: 'string' },
|
||||||
|
indexname: { type: 'string' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
|
|
@ -16,6 +16,25 @@ export const meta = {
|
||||||
|
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
requireModerator: true,
|
requireModerator: true,
|
||||||
|
res: {
|
||||||
|
type: 'array',
|
||||||
|
optional: false,
|
||||||
|
nullable: false,
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
optional: false,
|
||||||
|
nullable: false,
|
||||||
|
properties: {
|
||||||
|
ip: { type: 'string' },
|
||||||
|
createdAt: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false,
|
||||||
|
nullable: false,
|
||||||
|
format: 'date-time',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
|
|
@ -28,6 +28,20 @@ export const meta = {
|
||||||
id: '224eff5e-2488-4b18-b3e7-f50d94421648',
|
id: '224eff5e-2488-4b18-b3e7-f50d94421648',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
res: {
|
||||||
|
type: 'array',
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
id: { type: 'string', format: 'misskey:id' },
|
||||||
|
createdAt: { type: 'string', format: 'date-time' },
|
||||||
|
user: { ref: 'UserDetailed' },
|
||||||
|
expiresAt: { type: 'string', format: 'date-time', nullable: true },
|
||||||
|
},
|
||||||
|
required: ['id', 'createdAt', 'user'],
|
||||||
|
},
|
||||||
|
}
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
@ -80,7 +94,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
id: assign.id,
|
id: assign.id,
|
||||||
createdAt: this.idService.parse(assign.id).date.toISOString(),
|
createdAt: this.idService.parse(assign.id).date.toISOString(),
|
||||||
user: await this.userEntityService.pack(assign.user!, me, { detail: true }),
|
user: await this.userEntityService.pack(assign.user!, me, { detail: true }),
|
||||||
expiresAt: assign.expiresAt,
|
expiresAt: assign.expiresAt?.toISOString() ?? null,
|
||||||
})));
|
})));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,23 @@ export const meta = {
|
||||||
requireCredential: false,
|
requireCredential: false,
|
||||||
|
|
||||||
tags: ['meta'],
|
tags: ['meta'],
|
||||||
|
|
||||||
|
res: {
|
||||||
|
type: 'object',
|
||||||
|
nullable: true,
|
||||||
|
properties: {
|
||||||
|
params: {
|
||||||
|
type: 'array',
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
name: { type: 'string' },
|
||||||
|
type: { type: 'string' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
|
|
@ -18,6 +18,92 @@ export const meta = {
|
||||||
|
|
||||||
allowGet: true,
|
allowGet: true,
|
||||||
cacheSec: 60 * 60,
|
cacheSec: 60 * 60,
|
||||||
|
|
||||||
|
res: {
|
||||||
|
type: 'object',
|
||||||
|
optional: false,
|
||||||
|
nullable: false,
|
||||||
|
properties: {
|
||||||
|
topSubInstances: {
|
||||||
|
type: 'array',
|
||||||
|
optional: false,
|
||||||
|
nullable: false,
|
||||||
|
items: {
|
||||||
|
properties: {
|
||||||
|
id: { type: 'string' },
|
||||||
|
firstRetrievedAt: { type: 'string' },
|
||||||
|
host: { type: 'string' },
|
||||||
|
usersCount: { type: 'number' },
|
||||||
|
notesCount: { type: 'number' },
|
||||||
|
followingCount: { type: 'number' },
|
||||||
|
followersCount: { type: 'number' },
|
||||||
|
isNotResponding: { type: 'boolean' },
|
||||||
|
isSuspended: { type: 'boolean' },
|
||||||
|
isBlocked: { type: 'boolean' },
|
||||||
|
softwareName: { type: 'string' },
|
||||||
|
softwareVersion: { type: 'string' },
|
||||||
|
openRegistrations: { type: 'boolean' },
|
||||||
|
name: { type: 'string' },
|
||||||
|
description: { type: 'string' },
|
||||||
|
maintainerName: { type: 'string' },
|
||||||
|
maintainerEmail: { type: 'string' },
|
||||||
|
isSilenced: { type: 'boolean' },
|
||||||
|
iconUrl: { type: 'string' },
|
||||||
|
faviconUrl: { type: 'string' },
|
||||||
|
themeColor: { type: 'string' },
|
||||||
|
infoUpdatedAt: {
|
||||||
|
type: 'string',
|
||||||
|
nullable: true,
|
||||||
|
},
|
||||||
|
latestRequestReceivedAt: {
|
||||||
|
type: 'string',
|
||||||
|
nullable: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
otherFollowersCount: { type: 'number' },
|
||||||
|
topPubInstances: {
|
||||||
|
type: 'array',
|
||||||
|
optional: false,
|
||||||
|
nullable: false,
|
||||||
|
items: {
|
||||||
|
properties: {
|
||||||
|
id: { type: 'string' },
|
||||||
|
firstRetrievedAt: { type: 'string' },
|
||||||
|
host: { type: 'string' },
|
||||||
|
usersCount: { type: 'number' },
|
||||||
|
notesCount: { type: 'number' },
|
||||||
|
followingCount: { type: 'number' },
|
||||||
|
followersCount: { type: 'number' },
|
||||||
|
isNotResponding: { type: 'boolean' },
|
||||||
|
isSuspended: { type: 'boolean' },
|
||||||
|
isBlocked: { type: 'boolean' },
|
||||||
|
softwareName: { type: 'string' },
|
||||||
|
softwareVersion: { type: 'string' },
|
||||||
|
openRegistrations: { type: 'boolean' },
|
||||||
|
name: { type: 'string' },
|
||||||
|
description: { type: 'string' },
|
||||||
|
maintainerName: { type: 'string' },
|
||||||
|
maintainerEmail: { type: 'string' },
|
||||||
|
isSilenced: { type: 'boolean' },
|
||||||
|
iconUrl: { type: 'string' },
|
||||||
|
faviconUrl: { type: 'string' },
|
||||||
|
themeColor: { type: 'string' },
|
||||||
|
infoUpdatedAt: {
|
||||||
|
type: 'string',
|
||||||
|
nullable: true,
|
||||||
|
},
|
||||||
|
latestRequestReceivedAt: {
|
||||||
|
type: 'string',
|
||||||
|
nullable: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
otherFollowingCount: { type: 'number' },
|
||||||
|
},
|
||||||
|
}
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
|
|
@ -32,6 +32,18 @@ export const meta = {
|
||||||
id: '693ba8ba-b486-40df-a174-72f8279b56a4',
|
id: '693ba8ba-b486-40df-a174-72f8279b56a4',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
res: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
type: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
|
|
@ -16,6 +16,18 @@ export const meta = {
|
||||||
requireCredential: false,
|
requireCredential: false,
|
||||||
allowGet: true,
|
allowGet: true,
|
||||||
cacheSec: 60 * 3,
|
cacheSec: 60 * 3,
|
||||||
|
|
||||||
|
res: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
items: {
|
||||||
|
type: 'array',
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
|
|
@ -27,6 +27,12 @@ export const meta = {
|
||||||
|
|
||||||
errors: {
|
errors: {
|
||||||
},
|
},
|
||||||
|
|
||||||
|
res: {
|
||||||
|
type: 'object',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
ref: 'Flash',
|
||||||
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
|
|
@ -16,6 +16,16 @@ export const meta = {
|
||||||
requireCredential: false,
|
requireCredential: false,
|
||||||
allowGet: true,
|
allowGet: true,
|
||||||
cacheSec: 60 * 1,
|
cacheSec: 60 * 1,
|
||||||
|
res: {
|
||||||
|
type: 'object',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
properties: {
|
||||||
|
count: {
|
||||||
|
type: 'number',
|
||||||
|
nullable: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
|
|
@ -32,6 +32,16 @@ export const meta = {
|
||||||
id: '798d6847-b1ed-4f9c-b1f9-163c42655995',
|
id: '798d6847-b1ed-4f9c-b1f9-163c42655995',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
res: {
|
||||||
|
type: 'object',
|
||||||
|
nullable: false,
|
||||||
|
optional: false,
|
||||||
|
properties: {
|
||||||
|
id: { type: 'string' },
|
||||||
|
name: { type: 'string' },
|
||||||
|
},
|
||||||
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
|
|
@ -36,6 +36,140 @@ export const meta = {
|
||||||
id: 'bf32b864-449b-47b8-974e-f9a5468546f1',
|
id: 'bf32b864-449b-47b8-974e-f9a5468546f1',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
res: {
|
||||||
|
type: 'object',
|
||||||
|
nullable: false,
|
||||||
|
optional: false,
|
||||||
|
properties: {
|
||||||
|
rp: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
id: {
|
||||||
|
type: 'string',
|
||||||
|
nullable: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
user: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
id: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
displayName: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
challenge: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
pubKeyCredParams: {
|
||||||
|
type: 'array',
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
type: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
alg: {
|
||||||
|
type: 'number',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
timeout: {
|
||||||
|
type: 'number',
|
||||||
|
nullable: true,
|
||||||
|
},
|
||||||
|
excludeCredentials: {
|
||||||
|
type: 'array',
|
||||||
|
nullable: true,
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
id: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
transports: {
|
||||||
|
type: 'array',
|
||||||
|
items: {
|
||||||
|
type: 'string',
|
||||||
|
enum: [
|
||||||
|
"ble",
|
||||||
|
"cable",
|
||||||
|
"hybrid",
|
||||||
|
"internal",
|
||||||
|
"nfc",
|
||||||
|
"smart-card",
|
||||||
|
"usb",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
authenticatorSelection: {
|
||||||
|
type: 'object',
|
||||||
|
nullable: true,
|
||||||
|
properties: {
|
||||||
|
authenticatorAttachment: {
|
||||||
|
type: 'string',
|
||||||
|
enum: [
|
||||||
|
"cross-platform",
|
||||||
|
"platform",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
requireResidentKey: {
|
||||||
|
type: 'boolean',
|
||||||
|
},
|
||||||
|
userVerification: {
|
||||||
|
type: 'string',
|
||||||
|
enum: [
|
||||||
|
"discouraged",
|
||||||
|
"preferred",
|
||||||
|
"required",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
attestation: {
|
||||||
|
type: 'string',
|
||||||
|
nullable: true,
|
||||||
|
enum: [
|
||||||
|
"direct",
|
||||||
|
"enterprise",
|
||||||
|
"indirect",
|
||||||
|
"none",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
extensions: {
|
||||||
|
type: 'object',
|
||||||
|
nullable: true,
|
||||||
|
properties: {
|
||||||
|
appid: {
|
||||||
|
type: 'string',
|
||||||
|
nullable: true,
|
||||||
|
},
|
||||||
|
credProps: {
|
||||||
|
type: 'boolean',
|
||||||
|
nullable: true,
|
||||||
|
},
|
||||||
|
hmacCreateSecret: {
|
||||||
|
type: 'boolean',
|
||||||
|
nullable: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
|
|
@ -26,6 +26,19 @@ export const meta = {
|
||||||
id: '78d6c839-20c9-4c66-b90a-fc0542168b48',
|
id: '78d6c839-20c9-4c66-b90a-fc0542168b48',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
res: {
|
||||||
|
type: 'object',
|
||||||
|
nullable: false,
|
||||||
|
optional: false,
|
||||||
|
properties: {
|
||||||
|
qr: { type: 'string' },
|
||||||
|
url: { type: 'string' },
|
||||||
|
secret: { type: 'string' },
|
||||||
|
label: { type: 'string' },
|
||||||
|
issuer: { type: 'string' },
|
||||||
|
},
|
||||||
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
|
|
@ -13,6 +13,37 @@ export const meta = {
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
|
|
||||||
secure: true,
|
secure: true,
|
||||||
|
|
||||||
|
res: {
|
||||||
|
type: 'array',
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
id: {
|
||||||
|
type: 'string',
|
||||||
|
format: 'misskey:id',
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
createdAt: {
|
||||||
|
type: 'string',
|
||||||
|
format: 'date-time',
|
||||||
|
},
|
||||||
|
lastUsedAt: {
|
||||||
|
type: 'string',
|
||||||
|
format: 'date-time',
|
||||||
|
},
|
||||||
|
permission: {
|
||||||
|
type: 'array',
|
||||||
|
uniqueItems: true,
|
||||||
|
items: {
|
||||||
|
type: 'string'
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
@ -50,7 +81,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
id: token.id,
|
id: token.id,
|
||||||
name: token.name ?? token.app?.name,
|
name: token.name ?? token.app?.name,
|
||||||
createdAt: this.idService.parse(token.id).date.toISOString(),
|
createdAt: this.idService.parse(token.id).date.toISOString(),
|
||||||
lastUsedAt: token.lastUsedAt,
|
lastUsedAt: token.lastUsedAt?.toISOString(),
|
||||||
permission: token.permission,
|
permission: token.permission,
|
||||||
})));
|
})));
|
||||||
});
|
});
|
||||||
|
|
|
@ -14,6 +14,36 @@ export const meta = {
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
|
|
||||||
secure: true,
|
secure: true,
|
||||||
|
|
||||||
|
res: {
|
||||||
|
type: 'array',
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
id: {
|
||||||
|
type: 'string',
|
||||||
|
format: 'misskey:id',
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
callbackUrl: {
|
||||||
|
type: 'string',
|
||||||
|
nullable: true,
|
||||||
|
},
|
||||||
|
permission: {
|
||||||
|
type: 'array',
|
||||||
|
uniqueItems: true,
|
||||||
|
items: {
|
||||||
|
type: 'string'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
isAuthorized: {
|
||||||
|
type: 'boolean',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
|
|
@ -64,6 +64,10 @@ export const meta = {
|
||||||
id: 'b234a14e-9ebe-4581-8000-074b3c215962',
|
id: 'b234a14e-9ebe-4581-8000-074b3c215962',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
res: {
|
||||||
|
type: 'object',
|
||||||
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
|
|
@ -9,6 +9,10 @@ import { RegistryApiService } from '@/core/RegistryApiService.js';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
|
|
||||||
|
res: {
|
||||||
|
type: 'object',
|
||||||
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
|
|
@ -18,6 +18,10 @@ export const meta = {
|
||||||
id: '97a1e8e7-c0f7-47d2-957a-92e61256e01a',
|
id: '97a1e8e7-c0f7-47d2-957a-92e61256e01a',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
res: {
|
||||||
|
type: 'object',
|
||||||
|
}
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
|
|
@ -18,6 +18,10 @@ export const meta = {
|
||||||
id: 'ac3ed68a-62f0-422b-a7bc-d5e09e8f6a6a',
|
id: 'ac3ed68a-62f0-422b-a7bc-d5e09e8f6a6a',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
res: {
|
||||||
|
type: 'object',
|
||||||
|
}
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
|
|
@ -3,12 +3,16 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
import { RegistryApiService } from '@/core/RegistryApiService.js';
|
import { RegistryApiService } from '@/core/RegistryApiService.js';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
|
|
||||||
|
res: {
|
||||||
|
type: 'object',
|
||||||
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
|
|
@ -3,13 +3,35 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
import { RegistryApiService } from '@/core/RegistryApiService.js';
|
import { RegistryApiService } from '@/core/RegistryApiService.js';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
secure: true,
|
secure: true,
|
||||||
|
|
||||||
|
res: {
|
||||||
|
type: 'array',
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
scopes: {
|
||||||
|
type: 'array',
|
||||||
|
items: {
|
||||||
|
type: 'array',
|
||||||
|
items: {
|
||||||
|
type: 'string',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
domain: {
|
||||||
|
type: 'string',
|
||||||
|
nullable: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
|
|
@ -40,6 +40,11 @@ export const meta = {
|
||||||
id: 'a2defefb-f220-8849-0af6-17f816099323',
|
id: 'a2defefb-f220-8849-0af6-17f816099323',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
res: {
|
||||||
|
type: 'object',
|
||||||
|
ref: 'UserDetailed',
|
||||||
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
|
|
@ -27,6 +27,33 @@ export const meta = {
|
||||||
id: '87a9bb19-111e-4e37-81d3-a3e7426453b0',
|
id: '87a9bb19-111e-4e37-81d3-a3e7426453b0',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
res: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
id: {
|
||||||
|
type: 'string',
|
||||||
|
format: 'misskey:id'
|
||||||
|
},
|
||||||
|
userId: {
|
||||||
|
type: 'string',
|
||||||
|
format: 'misskey:id',
|
||||||
|
},
|
||||||
|
name: { type: 'string' },
|
||||||
|
on: {
|
||||||
|
type: 'array',
|
||||||
|
items: {
|
||||||
|
type: 'string',
|
||||||
|
enum: webhookEventTypes,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
url: { type: 'string' },
|
||||||
|
secret: { type: 'string' },
|
||||||
|
active: { type: 'boolean' },
|
||||||
|
latestSentAt: { type: 'string', format: 'date-time', nullable: true },
|
||||||
|
latestStatus: { type: 'integer', nullable: true },
|
||||||
|
},
|
||||||
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
@ -73,7 +100,17 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
|
|
||||||
this.globalEventService.publishInternalEvent('webhookCreated', webhook);
|
this.globalEventService.publishInternalEvent('webhookCreated', webhook);
|
||||||
|
|
||||||
return webhook;
|
return {
|
||||||
|
id: webhook.id,
|
||||||
|
userId: webhook.userId,
|
||||||
|
name: webhook.name,
|
||||||
|
on: webhook.on,
|
||||||
|
url: webhook.url,
|
||||||
|
secret: webhook.secret,
|
||||||
|
active: webhook.active,
|
||||||
|
latestSentAt: webhook.latestSentAt?.toISOString(),
|
||||||
|
latestStatus: webhook.latestStatus,
|
||||||
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
|
import { webhookEventTypes } from '@/models/Webhook.js';
|
||||||
import type { WebhooksRepository } from '@/models/_.js';
|
import type { WebhooksRepository } from '@/models/_.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
|
|
||||||
|
@ -14,6 +15,36 @@ export const meta = {
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
|
|
||||||
kind: 'read:account',
|
kind: 'read:account',
|
||||||
|
|
||||||
|
res: {
|
||||||
|
type: 'array',
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
id: {
|
||||||
|
type: 'string',
|
||||||
|
format: 'misskey:id'
|
||||||
|
},
|
||||||
|
userId: {
|
||||||
|
type: 'string',
|
||||||
|
format: 'misskey:id',
|
||||||
|
},
|
||||||
|
name: { type: 'string' },
|
||||||
|
on: {
|
||||||
|
type: 'array',
|
||||||
|
items: {
|
||||||
|
type: 'string',
|
||||||
|
enum: webhookEventTypes,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
url: { type: 'string' },
|
||||||
|
secret: { type: 'string' },
|
||||||
|
active: { type: 'boolean' },
|
||||||
|
latestSentAt: { type: 'string', format: 'date-time', nullable: true },
|
||||||
|
latestStatus: { type: 'integer', nullable: true },
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
@ -33,7 +64,19 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
userId: me.id,
|
userId: me.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
return webhooks;
|
return webhooks.map(webhook => (
|
||||||
|
{
|
||||||
|
id: webhook.id,
|
||||||
|
userId: webhook.userId,
|
||||||
|
name: webhook.name,
|
||||||
|
on: webhook.on,
|
||||||
|
url: webhook.url,
|
||||||
|
secret: webhook.secret,
|
||||||
|
active: webhook.active,
|
||||||
|
latestSentAt: webhook.latestSentAt?.toISOString(),
|
||||||
|
latestStatus: webhook.latestStatus,
|
||||||
|
}
|
||||||
|
));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
|
import { webhookEventTypes } from '@/models/Webhook.js';
|
||||||
import type { WebhooksRepository } from '@/models/_.js';
|
import type { WebhooksRepository } from '@/models/_.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import { ApiError } from '../../../error.js';
|
import { ApiError } from '../../../error.js';
|
||||||
|
@ -23,6 +24,33 @@ export const meta = {
|
||||||
id: '50f614d9-3047-4f7e-90d8-ad6b2d5fb098',
|
id: '50f614d9-3047-4f7e-90d8-ad6b2d5fb098',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
res: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
id: {
|
||||||
|
type: 'string',
|
||||||
|
format: 'misskey:id',
|
||||||
|
},
|
||||||
|
userId: {
|
||||||
|
type: 'string',
|
||||||
|
format: 'misskey:id',
|
||||||
|
},
|
||||||
|
name: { type: 'string' },
|
||||||
|
on: {
|
||||||
|
type: 'array',
|
||||||
|
items: {
|
||||||
|
type: 'string',
|
||||||
|
enum: webhookEventTypes,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
url: { type: 'string' },
|
||||||
|
secret: { type: 'string' },
|
||||||
|
active: { type: 'boolean' },
|
||||||
|
latestSentAt: { type: 'string', format: 'date-time', nullable: true },
|
||||||
|
latestStatus: { type: 'integer', nullable: true },
|
||||||
|
},
|
||||||
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
@ -49,7 +77,17 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
throw new ApiError(meta.errors.noSuchWebhook);
|
throw new ApiError(meta.errors.noSuchWebhook);
|
||||||
}
|
}
|
||||||
|
|
||||||
return webhook;
|
return {
|
||||||
|
id: webhook.id,
|
||||||
|
userId: webhook.userId,
|
||||||
|
name: webhook.name,
|
||||||
|
on: webhook.on,
|
||||||
|
url: webhook.url,
|
||||||
|
secret: webhook.secret,
|
||||||
|
active: webhook.active,
|
||||||
|
latestSentAt: webhook.latestSentAt?.toISOString(),
|
||||||
|
latestStatus: webhook.latestStatus,
|
||||||
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,25 @@ export const meta = {
|
||||||
id: '30aaaee3-4792-48dc-ab0d-cf501a575ac5',
|
id: '30aaaee3-4792-48dc-ab0d-cf501a575ac5',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
res: {
|
||||||
|
type: 'array',
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
nullable: false,
|
||||||
|
properties: {
|
||||||
|
id: {
|
||||||
|
type: 'string',
|
||||||
|
format: 'misskey:id',
|
||||||
|
},
|
||||||
|
user: {
|
||||||
|
type: 'object',
|
||||||
|
ref: 'User',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
required: ['id', 'user'],
|
||||||
|
},
|
||||||
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
|
|
@ -15,6 +15,53 @@ export const meta = {
|
||||||
cacheSec: 60 * 1,
|
cacheSec: 60 * 1,
|
||||||
|
|
||||||
tags: ['meta'],
|
tags: ['meta'],
|
||||||
|
res: {
|
||||||
|
type: 'object',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
properties: {
|
||||||
|
machine: {
|
||||||
|
type: 'string',
|
||||||
|
nullable: false,
|
||||||
|
},
|
||||||
|
cpu: {
|
||||||
|
type: 'object',
|
||||||
|
nullable: false,
|
||||||
|
properties: {
|
||||||
|
model: {
|
||||||
|
type: 'string',
|
||||||
|
nullable: false,
|
||||||
|
},
|
||||||
|
cores: {
|
||||||
|
type: 'number',
|
||||||
|
nullable: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mem: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
total: {
|
||||||
|
type: 'number',
|
||||||
|
nullable: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
fs: {
|
||||||
|
type: 'object',
|
||||||
|
nullable: false,
|
||||||
|
properties: {
|
||||||
|
total: {
|
||||||
|
type: 'number',
|
||||||
|
nullable: false,
|
||||||
|
},
|
||||||
|
used: {
|
||||||
|
type: 'number',
|
||||||
|
nullable: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
|
|
@ -12,6 +12,30 @@ export const meta = {
|
||||||
description: 'Endpoint for testing input validation.',
|
description: 'Endpoint for testing input validation.',
|
||||||
|
|
||||||
requireCredential: false,
|
requireCredential: false,
|
||||||
|
|
||||||
|
res: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
id: {
|
||||||
|
type: 'string',
|
||||||
|
format: 'misskey:id',
|
||||||
|
},
|
||||||
|
required: {
|
||||||
|
type: 'boolean',
|
||||||
|
},
|
||||||
|
string: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
default: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
nullableDefault: {
|
||||||
|
type: 'string',
|
||||||
|
default: 'hello',
|
||||||
|
nullable: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
|
|
@ -10,6 +10,21 @@ import { DI } from '@/di-symbols.js';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
|
|
||||||
|
res: {
|
||||||
|
type: 'array',
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
name: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
unlockedAt: {
|
||||||
|
type: 'number',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
|
|
@ -25,6 +25,35 @@ export const meta = {
|
||||||
id: '7bc05c21-1d7a-41ae-88f1-66820f4dc686',
|
id: '7bc05c21-1d7a-41ae-88f1-66820f4dc686',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
res: {
|
||||||
|
type: 'array',
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
nullable: false,
|
||||||
|
properties: {
|
||||||
|
id: {
|
||||||
|
type: 'string',
|
||||||
|
format: 'misskey:id',
|
||||||
|
},
|
||||||
|
createdAt: {
|
||||||
|
type: 'string',
|
||||||
|
format: 'date-time',
|
||||||
|
},
|
||||||
|
userId: {
|
||||||
|
type: 'string',
|
||||||
|
format: 'misskey:id',
|
||||||
|
},
|
||||||
|
user: {
|
||||||
|
type: 'object',
|
||||||
|
ref: 'User',
|
||||||
|
},
|
||||||
|
withReplies: {
|
||||||
|
type: 'boolean',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
|
|
@ -14,12 +14,28 @@
|
||||||
* pollEnded - 自分のアンケートもしくは自分が投票したアンケートが終了した
|
* pollEnded - 自分のアンケートもしくは自分が投票したアンケートが終了した
|
||||||
* receiveFollowRequest - フォローリクエストされた
|
* receiveFollowRequest - フォローリクエストされた
|
||||||
* followRequestAccepted - 自分の送ったフォローリクエストが承認された
|
* followRequestAccepted - 自分の送ったフォローリクエストが承認された
|
||||||
|
* roleAssigned - ロールが付与された
|
||||||
* groupInvited - グループに招待された
|
* groupInvited - グループに招待された
|
||||||
* achievementEarned - 実績を獲得
|
* achievementEarned - 実績を獲得
|
||||||
* app - アプリ通知
|
* app - アプリ通知
|
||||||
* test - テスト通知(サーバー側)
|
* test - テスト通知(サーバー側)
|
||||||
*/
|
*/
|
||||||
export const notificationTypes = ['note', 'follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'achievementEarned', 'app', 'test'] as const;
|
export const notificationTypes = [
|
||||||
|
'note',
|
||||||
|
'follow',
|
||||||
|
'mention',
|
||||||
|
'reply',
|
||||||
|
'renote',
|
||||||
|
'quote',
|
||||||
|
'reaction',
|
||||||
|
'pollEnded',
|
||||||
|
'receiveFollowRequest',
|
||||||
|
'followRequestAccepted',
|
||||||
|
'roleAssigned',
|
||||||
|
'groupInvited',
|
||||||
|
'achievementEarned',
|
||||||
|
'app',
|
||||||
|
'test'] as const;
|
||||||
export const obsoleteNotificationTypes = ['pollVote'/*, 'groupInvited'*/] as const;
|
export const obsoleteNotificationTypes = ['pollVote'/*, 'groupInvited'*/] as const;
|
||||||
|
|
||||||
export const noteVisibilities = ['public', 'home', 'followers', 'specified'] as const;
|
export const noteVisibilities = ['public', 'home', 'followers', 'specified'] as const;
|
||||||
|
|
|
@ -7,7 +7,7 @@ services:
|
||||||
- "127.0.0.1:56312:6379"
|
- "127.0.0.1:56312:6379"
|
||||||
|
|
||||||
dbtest:
|
dbtest:
|
||||||
image: postgres:13
|
image: postgres:15
|
||||||
ports:
|
ports:
|
||||||
- "127.0.0.1:54312:5432"
|
- "127.0.0.1:54312:5432"
|
||||||
environment:
|
environment:
|
||||||
|
|
|
@ -19,6 +19,7 @@ import { CacheService } from '@/core/CacheService.js';
|
||||||
import { IdService } from '@/core/IdService.js';
|
import { IdService } from '@/core/IdService.js';
|
||||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||||
import { secureRndstr } from '@/misc/secure-rndstr.js';
|
import { secureRndstr } from '@/misc/secure-rndstr.js';
|
||||||
|
import { NotificationService } from '@/core/NotificationService.js';
|
||||||
import { sleep } from '../utils.js';
|
import { sleep } from '../utils.js';
|
||||||
import type { TestingModule } from '@nestjs/testing';
|
import type { TestingModule } from '@nestjs/testing';
|
||||||
import type { MockFunctionMetadata } from 'jest-mock';
|
import type { MockFunctionMetadata } from 'jest-mock';
|
||||||
|
@ -32,6 +33,7 @@ describe('RoleService', () => {
|
||||||
let rolesRepository: RolesRepository;
|
let rolesRepository: RolesRepository;
|
||||||
let roleAssignmentsRepository: RoleAssignmentsRepository;
|
let roleAssignmentsRepository: RoleAssignmentsRepository;
|
||||||
let metaService: jest.Mocked<MetaService>;
|
let metaService: jest.Mocked<MetaService>;
|
||||||
|
let notificationService: jest.Mocked<NotificationService>;
|
||||||
let clock: lolex.InstalledClock;
|
let clock: lolex.InstalledClock;
|
||||||
|
|
||||||
function createUser(data: Partial<MiUser> = {}) {
|
function createUser(data: Partial<MiUser> = {}) {
|
||||||
|
@ -76,6 +78,8 @@ describe('RoleService', () => {
|
||||||
.useMocker((token) => {
|
.useMocker((token) => {
|
||||||
if (token === MetaService) {
|
if (token === MetaService) {
|
||||||
return { fetch: jest.fn() };
|
return { fetch: jest.fn() };
|
||||||
|
} else if (token === NotificationService) {
|
||||||
|
return { createNotification: jest.fn() };
|
||||||
}
|
}
|
||||||
if (typeof token === 'function') {
|
if (typeof token === 'function') {
|
||||||
const mockMetadata = moduleMocker.getMetadata(token) as MockFunctionMetadata<any, any>;
|
const mockMetadata = moduleMocker.getMetadata(token) as MockFunctionMetadata<any, any>;
|
||||||
|
@ -93,6 +97,7 @@ describe('RoleService', () => {
|
||||||
roleAssignmentsRepository = app.get<RoleAssignmentsRepository>(DI.roleAssignmentsRepository);
|
roleAssignmentsRepository = app.get<RoleAssignmentsRepository>(DI.roleAssignmentsRepository);
|
||||||
|
|
||||||
metaService = app.get<MetaService>(MetaService) as jest.Mocked<MetaService>;
|
metaService = app.get<MetaService>(MetaService) as jest.Mocked<MetaService>;
|
||||||
|
notificationService = app.get<NotificationService>(NotificationService) as jest.Mocked<NotificationService>;
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
|
@ -273,4 +278,53 @@ describe('RoleService', () => {
|
||||||
expect(resultAfter25hAgain.canManageCustomEmojis).toBe(true);
|
expect(resultAfter25hAgain.canManageCustomEmojis).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('assign', () => {
|
||||||
|
test('公開ロールの場合は通知される', async () => {
|
||||||
|
const user = await createUser();
|
||||||
|
const role = await createRole({
|
||||||
|
isPublic: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
await roleService.assign(user.id, role.id);
|
||||||
|
|
||||||
|
await sleep(100);
|
||||||
|
|
||||||
|
const assignments = await roleAssignmentsRepository.find({
|
||||||
|
where: {
|
||||||
|
userId: user.id,
|
||||||
|
roleId: role.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(assignments).toHaveLength(1);
|
||||||
|
|
||||||
|
expect(notificationService.createNotification).toHaveBeenCalled();
|
||||||
|
expect(notificationService.createNotification.mock.lastCall![0]).toBe(user.id);
|
||||||
|
expect(notificationService.createNotification.mock.lastCall![1]).toBe('roleAssigned');
|
||||||
|
expect(notificationService.createNotification.mock.lastCall![2]).toBe({
|
||||||
|
roleId: role.id,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('非公開ロールの場合は通知されない', async () => {
|
||||||
|
const user = await createUser();
|
||||||
|
const role = await createRole({
|
||||||
|
isPublic: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
await roleService.assign(user.id, role.id);
|
||||||
|
|
||||||
|
await sleep(100);
|
||||||
|
|
||||||
|
const assignments = await roleAssignmentsRepository.find({
|
||||||
|
where: {
|
||||||
|
userId: user.id,
|
||||||
|
roleId: role.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(assignments).toHaveLength(1);
|
||||||
|
|
||||||
|
expect(notificationService.createNotification).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -2486,7 +2486,7 @@ type Notification_2 = components['schemas']['Notification'];
|
||||||
type NotificationsCreateRequest = operations['notifications/create']['requestBody']['content']['application/json'];
|
type NotificationsCreateRequest = operations['notifications/create']['requestBody']['content']['application/json'];
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
export const notificationTypes: readonly ["note", "follow", "mention", "reply", "renote", "quote", "reaction", "pollVote", "pollEnded", "receiveFollowRequest", "followRequestAccepted", "groupInvited", "app", "achievementEarned"];
|
export const notificationTypes: readonly ["note", "follow", "mention", "reply", "renote", "quote", "reaction", "pollVote", "pollEnded", "receiveFollowRequest", "followRequestAccepted", "groupInvited", "app", "roleAssigned", "achievementEarned"];
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
type Page = components['schemas']['Page'];
|
type Page = components['schemas']['Page'];
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
export const notificationTypes = ['note', 'follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app', 'achievementEarned'] as const;
|
export const notificationTypes = ['note', 'follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app', 'roleAssigned', 'achievementEarned'] as const;
|
||||||
|
|
||||||
export const noteVisibilities = ['public', 'home', 'followers', 'specified'] as const;
|
export const noteVisibilities = ['public', 'home', 'followers', 'specified'] as const;
|
||||||
|
|
||||||
|
|
|
@ -3,16 +3,16 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { computed, createApp, watch, markRaw, version as vueVersion, defineAsyncComponent, App } from 'vue';
|
import { computed, watch, version as vueVersion, App } from 'vue';
|
||||||
import { compareVersions } from 'compare-versions';
|
import { compareVersions } from 'compare-versions';
|
||||||
import widgets from '@/widgets/index.js';
|
import widgets from '@/widgets/index.js';
|
||||||
import directives from '@/directives/index.js';
|
import directives from '@/directives/index.js';
|
||||||
import components from '@/components/index.js';
|
import components from '@/components/index.js';
|
||||||
import { version, basedMisskeyVersion, ui, lang, updateLocale, locale } from '@/config.js';
|
import { version, basedMisskeyVersion, lang, updateLocale, locale } from '@/config.js';
|
||||||
import { applyTheme } from '@/scripts/theme.js';
|
import { applyTheme } from '@/scripts/theme.js';
|
||||||
import { isDeviceDarkmode } from '@/scripts/is-device-darkmode.js';
|
import { isDeviceDarkmode } from '@/scripts/is-device-darkmode.js';
|
||||||
import { i18n, updateI18n } from '@/i18n.js';
|
import { updateI18n } from '@/i18n.js';
|
||||||
import { $i, refreshAccount, login, updateAccount, signout } from '@/account.js';
|
import { $i, refreshAccount, login } from '@/account.js';
|
||||||
import { defaultStore, ColdDeviceStorage } from '@/store.js';
|
import { defaultStore, ColdDeviceStorage } from '@/store.js';
|
||||||
import { fetchInstance, instance } from '@/instance.js';
|
import { fetchInstance, instance } from '@/instance.js';
|
||||||
import { deviceKind } from '@/scripts/device-kind.js';
|
import { deviceKind } from '@/scripts/device-kind.js';
|
||||||
|
|
|
@ -3,14 +3,14 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { computed, createApp, watch, markRaw, version as vueVersion, defineAsyncComponent } from 'vue';
|
import { createApp, markRaw, defineAsyncComponent } from 'vue';
|
||||||
import { common } from './common.js';
|
import { common } from './common.js';
|
||||||
import { version, ui, lang, updateLocale } from '@/config.js';
|
import { ui } from '@/config.js';
|
||||||
import { i18n, updateI18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { confirm, alert, post, popup, welcomeToast } from '@/os.js';
|
import { confirm, alert, post, popup, welcomeToast } from '@/os.js';
|
||||||
import { useStream } from '@/stream.js';
|
import { useStream } from '@/stream.js';
|
||||||
import * as sound from '@/scripts/sound.js';
|
import * as sound from '@/scripts/sound.js';
|
||||||
import { $i, refreshAccount, login, updateAccount, signout } from '@/account.js';
|
import { $i, updateAccount, signout } from '@/account.js';
|
||||||
import { defaultStore, ColdDeviceStorage } from '@/store.js';
|
import { defaultStore, ColdDeviceStorage } from '@/store.js';
|
||||||
import { makeHotkey } from '@/scripts/hotkey.js';
|
import { makeHotkey } from '@/scripts/hotkey.js';
|
||||||
import { reactionPicker } from '@/scripts/reaction-picker.js';
|
import { reactionPicker } from '@/scripts/reaction-picker.js';
|
||||||
|
@ -20,7 +20,6 @@ import { mainRouter } from '@/router.js';
|
||||||
import { initializeSw } from '@/scripts/initialize-sw.js';
|
import { initializeSw } from '@/scripts/initialize-sw.js';
|
||||||
import { deckStore } from '@/ui/deck/deck-store.js';
|
import { deckStore } from '@/ui/deck/deck-store.js';
|
||||||
import { emojiPicker } from '@/scripts/emoji-picker.js';
|
import { emojiPicker } from '@/scripts/emoji-picker.js';
|
||||||
import { SnowfallEffect } from '@/scripts/snowfall-effect.js';
|
|
||||||
import { userName } from '@/filters/user.js';
|
import { userName } from '@/filters/user.js';
|
||||||
import { vibrate } from '@/scripts/vibrate.js';
|
import { vibrate } from '@/scripts/vibrate.js';
|
||||||
|
|
||||||
|
@ -82,6 +81,7 @@ export async function mainBoot() {
|
||||||
if (defaultStore.state.enableSeasonalScreenEffect) {
|
if (defaultStore.state.enableSeasonalScreenEffect) {
|
||||||
const month = new Date().getMonth() + 1;
|
const month = new Date().getMonth() + 1;
|
||||||
if (month === 12 || month === 1) {
|
if (month === 12 || month === 1) {
|
||||||
|
const SnowfallEffect = (await import('@/scripts/snowfall-effect.js')).SnowfallEffect;
|
||||||
new SnowfallEffect().render();
|
new SnowfallEffect().render();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { computed, createApp, watch, markRaw, version as vueVersion, defineAsyncComponent } from 'vue';
|
import { createApp, defineAsyncComponent } from 'vue';
|
||||||
import { common } from './common.js';
|
import { common } from './common.js';
|
||||||
|
|
||||||
export async function subBoot() {
|
export async function subBoot() {
|
||||||
|
|
|
@ -24,8 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, nextTick, ref, shallowRef, watch, computed, toRefs } from 'vue';
|
import { ref, shallowRef, toRefs } from 'vue';
|
||||||
import { i18n } from '@/i18n.js';
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
modelValue: string | null;
|
modelValue: string | null;
|
||||||
|
|
|
@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div ref="root">
|
<div>
|
||||||
<XBanner v-for="media in mediaList.filter(media => !previewable(media))" :key="media.id" :media="media"/>
|
<XBanner v-for="media in mediaList.filter(media => !previewable(media))" :key="media.id" :media="media"/>
|
||||||
<div v-if="mediaList.filter(media => previewable(media)).length > 0" :class="$style.container">
|
<div v-if="mediaList.filter(media => previewable(media)).length > 0" :class="$style.container">
|
||||||
<div
|
<div
|
||||||
|
@ -27,41 +27,6 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
/**
|
|
||||||
* アスペクト比算出のためにHTMLElement.clientWidthを使うが、
|
|
||||||
* 大変重たいのでコンテナ要素とメディアリスト幅のペアをキャッシュする
|
|
||||||
* (タイムラインごとにスクロールコンテナが存在する前提だが……)
|
|
||||||
*/
|
|
||||||
const widthCache = new Map<Element, number>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* コンテナ要素がリサイズされたらキャッシュを削除する
|
|
||||||
*/
|
|
||||||
const ro = new ResizeObserver(entries => {
|
|
||||||
for (const entry of entries) {
|
|
||||||
widthCache.delete(entry.target);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
async function getClientWidthWithCache(targetEl: HTMLElement, containerEl: HTMLElement, count = 0) {
|
|
||||||
if (_DEV_) console.log('getClientWidthWithCache', { targetEl, containerEl, count, cache: widthCache.get(containerEl) });
|
|
||||||
if (widthCache.has(containerEl)) return widthCache.get(containerEl)!;
|
|
||||||
|
|
||||||
const width = targetEl.clientWidth;
|
|
||||||
|
|
||||||
if (count <= 10 && width < 64) {
|
|
||||||
// widthが64未満はおかしいのでリトライする
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 50));
|
|
||||||
return getClientWidthWithCache(targetEl, containerEl, count + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
widthCache.set(containerEl, width);
|
|
||||||
ro.observe(containerEl);
|
|
||||||
return width;
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, onMounted, onUnmounted, shallowRef } from 'vue';
|
import { computed, onMounted, onUnmounted, shallowRef } from 'vue';
|
||||||
import * as Misskey from 'cherrypick-js';
|
import * as Misskey from 'cherrypick-js';
|
||||||
|
@ -74,15 +39,12 @@ import XVideo from '@/components/MkMediaVideo.vue';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { FILE_TYPE_BROWSERSAFE } from '@/const';
|
import { FILE_TYPE_BROWSERSAFE } from '@/const';
|
||||||
import { defaultStore } from '@/store.js';
|
import { defaultStore } from '@/store.js';
|
||||||
import { getScrollContainer, getBodyScrollHeight } from '@/scripts/scroll.js';
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
mediaList: Misskey.entities.DriveFile[];
|
mediaList: Misskey.entities.DriveFile[];
|
||||||
raw?: boolean;
|
raw?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const root = shallowRef<HTMLDivElement>();
|
|
||||||
const container = shallowRef<HTMLElement | null | undefined>(undefined);
|
|
||||||
const gallery = shallowRef<HTMLDivElement>();
|
const gallery = shallowRef<HTMLDivElement>();
|
||||||
const pswpZIndex = os.claimZIndex('middle');
|
const pswpZIndex = os.claimZIndex('middle');
|
||||||
document.documentElement.style.setProperty('--mk-pswp-root-z-index', pswpZIndex.toString());
|
document.documentElement.style.setProperty('--mk-pswp-root-z-index', pswpZIndex.toString());
|
||||||
|
@ -95,12 +57,8 @@ const popstateHandler = (): void => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* アスペクト比をmediaListWithOneImageAppearanceに基づいていい感じに調整する
|
|
||||||
* aspect-ratioではなくheightを使う
|
|
||||||
*/
|
|
||||||
async function calcAspectRatio() {
|
async function calcAspectRatio() {
|
||||||
if (!gallery.value || !root.value) return;
|
if (!gallery.value) return;
|
||||||
|
|
||||||
let img = props.mediaList[0];
|
let img = props.mediaList[0];
|
||||||
|
|
||||||
|
@ -109,41 +67,22 @@ async function calcAspectRatio() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!container.value) container.value = getScrollContainer(root.value);
|
const ratioMax = (ratio: number) => `${Math.max(ratio, img.properties.width / img.properties.height).toString()} / 1`;
|
||||||
const width = container.value ? await getClientWidthWithCache(root.value, container.value) : root.value.clientWidth;
|
|
||||||
|
|
||||||
const heightMin = (ratio: number) => {
|
|
||||||
const imgResizeRatio = width / img.properties.width;
|
|
||||||
const imgDrawHeight = img.properties.height * imgResizeRatio;
|
|
||||||
const maxHeight = width * ratio;
|
|
||||||
const height = Math.min(imgDrawHeight, maxHeight);
|
|
||||||
if (_DEV_) console.log('Image height calculated:', { width, properties: img.properties, imgResizeRatio, imgDrawHeight, maxHeight, height });
|
|
||||||
return `${height}px`;
|
|
||||||
};
|
|
||||||
|
|
||||||
switch (defaultStore.state.mediaListWithOneImageAppearance) {
|
switch (defaultStore.state.mediaListWithOneImageAppearance) {
|
||||||
case '16_9':
|
case '16_9':
|
||||||
gallery.value.style.height = heightMin(9 / 16);
|
gallery.value.style.aspectRatio = ratioMax(16 / 9);
|
||||||
break;
|
break;
|
||||||
case '1_1':
|
case '1_1':
|
||||||
gallery.value.style.height = heightMin(1);
|
gallery.value.style.aspectRatio = ratioMax(1 / 1);
|
||||||
break;
|
break;
|
||||||
case '2_3':
|
case '2_3':
|
||||||
gallery.value.style.height = heightMin(3 / 2);
|
gallery.value.style.aspectRatio = ratioMax(2 / 3);
|
||||||
break;
|
break;
|
||||||
default: {
|
default:
|
||||||
const maxHeight = Math.max(64, (container.value ? container.value.clientHeight : getBodyScrollHeight()) * 0.5 || 360);
|
gallery.value.style.aspectRatio = '';
|
||||||
if (width === 0 || !maxHeight) return;
|
|
||||||
const imgResizeRatio = width / img.properties.width;
|
|
||||||
const imgDrawHeight = img.properties.height * imgResizeRatio;
|
|
||||||
gallery.value.style.height = `${Math.max(64, Math.min(imgDrawHeight, maxHeight))}px`;
|
|
||||||
gallery.value.style.minHeight = 'initial';
|
|
||||||
gallery.value.style.maxHeight = 'initial';
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gallery.value.style.aspectRatio = 'initial';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
|
|
@ -62,7 +62,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Ref, computed, defineAsyncComponent, nextTick, onBeforeUnmount, onMounted, ref, shallowRef, watch } from 'vue';
|
import { computed, defineAsyncComponent, nextTick, onBeforeUnmount, onMounted, ref, shallowRef, watch } from 'vue';
|
||||||
import { focusPrev, focusNext } from '@/scripts/focus.js';
|
import { focusPrev, focusNext } from '@/scripts/focus.js';
|
||||||
import MkSwitchButton from '@/components/MkSwitch.button.vue';
|
import MkSwitchButton from '@/components/MkSwitch.button.vue';
|
||||||
import { MenuItem, InnerMenuItem, MenuPending, MenuAction, MenuSwitch, MenuParent } from '@/types/menu.js';
|
import { MenuItem, InnerMenuItem, MenuPending, MenuAction, MenuSwitch, MenuParent } from '@/types/menu.js';
|
||||||
|
|
|
@ -198,7 +198,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, inject, onMounted, ref, shallowRef, Ref, defineAsyncComponent, watch, provide } from 'vue';
|
import { computed, inject, onMounted, ref, shallowRef, Ref, watch, provide } from 'vue';
|
||||||
import * as mfm from 'cherrypick-mfm-js';
|
import * as mfm from 'cherrypick-mfm-js';
|
||||||
import * as Misskey from 'cherrypick-js';
|
import * as Misskey from 'cherrypick-js';
|
||||||
import MkNoteSub from '@/components/MkNoteSub.vue';
|
import MkNoteSub from '@/components/MkNoteSub.vue';
|
||||||
|
|
|
@ -303,11 +303,10 @@ import { useNoteCapture } from '@/scripts/use-note-capture.js';
|
||||||
import { deepClone } from '@/scripts/clone.js';
|
import { deepClone } from '@/scripts/clone.js';
|
||||||
import { useTooltip } from '@/scripts/use-tooltip.js';
|
import { useTooltip } from '@/scripts/use-tooltip.js';
|
||||||
import { claimAchievement } from '@/scripts/achievements.js';
|
import { claimAchievement } from '@/scripts/achievements.js';
|
||||||
import { MenuItem } from '@/types/menu.js';
|
|
||||||
import MkRippleEffect from '@/components/MkRippleEffect.vue';
|
import MkRippleEffect from '@/components/MkRippleEffect.vue';
|
||||||
import { showMovedDialog } from '@/scripts/show-moved-dialog.js';
|
import { showMovedDialog } from '@/scripts/show-moved-dialog.js';
|
||||||
import MkUserCardMini from '@/components/MkUserCardMini.vue';
|
import MkUserCardMini from '@/components/MkUserCardMini.vue';
|
||||||
import MkPagination, { Paging } from '@/components/MkPagination.vue';
|
import MkPagination from '@/components/MkPagination.vue';
|
||||||
import MkReactionIcon from '@/components/MkReactionIcon.vue';
|
import MkReactionIcon from '@/components/MkReactionIcon.vue';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import { miLocalStorage } from '@/local-storage.js';
|
import { miLocalStorage } from '@/local-storage.js';
|
||||||
|
|
|
@ -8,6 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<div :class="$style.head">
|
<div :class="$style.head">
|
||||||
<MkAvatar v-if="notification.type === 'pollEnded'" :class="$style.icon" :user="notification.note.user" link preview/>
|
<MkAvatar v-if="notification.type === 'pollEnded'" :class="$style.icon" :user="notification.note.user" link preview/>
|
||||||
<MkAvatar v-else-if="notification.type === 'note'" :class="$style.icon" :user="notification.note.user" link preview/>
|
<MkAvatar v-else-if="notification.type === 'note'" :class="$style.icon" :user="notification.note.user" link preview/>
|
||||||
|
<MkAvatar v-else-if="notification.type === 'roleAssigned'" :class="$style.icon" :user="$i" link preview/>
|
||||||
<MkAvatar v-else-if="notification.type === 'achievementEarned'" :class="$style.icon" :user="$i" link preview/>
|
<MkAvatar v-else-if="notification.type === 'achievementEarned'" :class="$style.icon" :user="$i" link preview/>
|
||||||
<div v-else-if="notification.type === 'reaction:grouped'" :class="[$style.icon, $style.icon_reactionGroup]"><i class="ti ti-plus" style="line-height: 1;"></i></div>
|
<div v-else-if="notification.type === 'reaction:grouped'" :class="[$style.icon, $style.icon_reactionGroup]"><i class="ti ti-plus" style="line-height: 1;"></i></div>
|
||||||
<div v-else-if="notification.type === 'renote:grouped'" :class="[$style.icon, $style.icon_renoteGroup]"><i class="ti ti-repeat" style="line-height: 1;"></i></div>
|
<div v-else-if="notification.type === 'renote:grouped'" :class="[$style.icon, $style.icon_renoteGroup]"><i class="ti ti-repeat" style="line-height: 1;"></i></div>
|
||||||
|
@ -38,6 +39,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<i v-else-if="notification.type === 'quote'" class="ti ti-quote"></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 === 'pollEnded'" class="ti ti-chart-arrows"></i>
|
||||||
<i v-else-if="notification.type === 'achievementEarned'" class="ti ti-medal"></i>
|
<i v-else-if="notification.type === 'achievementEarned'" class="ti ti-medal"></i>
|
||||||
|
<img v-else-if="notification.type === 'roleAssigned'" :src="notification.role.iconUrl" alt=""/>
|
||||||
<!-- notification.reaction が null になることはまずないが、ここでoptional chaining使うと一部ブラウザで刺さるので念の為 -->
|
<!-- notification.reaction が null になることはまずないが、ここでoptional chaining使うと一部ブラウザで刺さるので念の為 -->
|
||||||
<MkReactionIcon
|
<MkReactionIcon
|
||||||
v-else-if="notification.type === 'reaction'"
|
v-else-if="notification.type === 'reaction'"
|
||||||
|
@ -52,6 +54,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<header :class="$style.header">
|
<header :class="$style.header">
|
||||||
<span v-if="notification.type === 'pollEnded'">{{ i18n.ts._notification.pollEnded }}</span>
|
<span v-if="notification.type === 'pollEnded'">{{ i18n.ts._notification.pollEnded }}</span>
|
||||||
<span v-else-if="notification.type === 'note'">{{ i18n.ts._notification.newNote }}: <MkUserName :user="notification.note.user"/></span>
|
<span v-else-if="notification.type === 'note'">{{ i18n.ts._notification.newNote }}: <MkUserName :user="notification.note.user"/></span>
|
||||||
|
<span v-else-if="notification.type === 'roleAssigned'">{{ i18n.ts._notification.roleAssigned }}</span>
|
||||||
<span v-else-if="notification.type === 'achievementEarned'">{{ i18n.ts._notification.achievementEarned }}</span>
|
<span v-else-if="notification.type === 'achievementEarned'">{{ i18n.ts._notification.achievementEarned }}</span>
|
||||||
<span v-else-if="notification.type === 'test'">{{ i18n.ts._notification.testNotification }}</span>
|
<span v-else-if="notification.type === 'test'">{{ i18n.ts._notification.testNotification }}</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>
|
<MkA v-else-if="notification.user" v-user-preview="notification.user.id" :class="$style.headerName" :to="userPage(notification.user)"><MkUserName :user="notification.user"/></MkA>
|
||||||
|
@ -88,6 +91,9 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<Mfm :text="getNoteSummary(notification.note)" :plain="true" :nowrap="true" :author="notification.note.user"/>
|
<Mfm :text="getNoteSummary(notification.note)" :plain="true" :nowrap="true" :author="notification.note.user"/>
|
||||||
<i class="ti ti-quote" :class="$style.quote"></i>
|
<i class="ti ti-quote" :class="$style.quote"></i>
|
||||||
</MkA>
|
</MkA>
|
||||||
|
<div v-else-if="notification.type === 'roleAssigned'" :class="$style.text">
|
||||||
|
{{ notification.role.name }}
|
||||||
|
</div>
|
||||||
<MkA v-else-if="notification.type === 'achievementEarned'" :class="$style.text" to="/my/achievements">
|
<MkA v-else-if="notification.type === 'achievementEarned'" :class="$style.text" to="/my/achievements">
|
||||||
{{ i18n.ts._achievements._types['_' + notification.achievement].title }}
|
{{ i18n.ts._achievements._types['_' + notification.achievement].title }}
|
||||||
</MkA>
|
</MkA>
|
||||||
|
@ -139,7 +145,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, shallowRef } from 'vue';
|
import { ref } from 'vue';
|
||||||
import * as Misskey from 'cherrypick-js';
|
import * as Misskey from 'cherrypick-js';
|
||||||
import MkReactionIcon from '@/components/MkReactionIcon.vue';
|
import MkReactionIcon from '@/components/MkReactionIcon.vue';
|
||||||
import MkFollowButton from '@/components/MkFollowButton.vue';
|
import MkFollowButton from '@/components/MkFollowButton.vue';
|
||||||
|
|
|
@ -24,13 +24,12 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onUnmounted, onDeactivated, onMounted, computed, shallowRef, onActivated, watch } from 'vue';
|
import { onUnmounted, onDeactivated, onMounted, computed, shallowRef, onActivated } from 'vue';
|
||||||
import MkPagination, { Paging } from '@/components/MkPagination.vue';
|
import MkPagination from '@/components/MkPagination.vue';
|
||||||
import XNotification from '@/components/MkNotification.vue';
|
import XNotification from '@/components/MkNotification.vue';
|
||||||
import MkDateSeparatedList from '@/components/MkDateSeparatedList.vue';
|
import MkDateSeparatedList from '@/components/MkDateSeparatedList.vue';
|
||||||
import MkNote from '@/components/MkNote.vue';
|
import MkNote from '@/components/MkNote.vue';
|
||||||
import { useStream } from '@/stream.js';
|
import { useStream } from '@/stream.js';
|
||||||
import { $i } from '@/account.js';
|
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { notificationTypes } from '@/const.js';
|
import { notificationTypes } from '@/const.js';
|
||||||
import { infoImageUrl } from '@/instance.js';
|
import { infoImageUrl } from '@/instance.js';
|
||||||
|
|
|
@ -23,8 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, onUnmounted, watch, ref, shallowRef } from 'vue';
|
import { onMounted, onUnmounted, ref, shallowRef } from 'vue';
|
||||||
import { deviceKind } from '@/scripts/device-kind.js';
|
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { getScrollContainer } from '@/scripts/scroll.js';
|
import { getScrollContainer } from '@/scripts/scroll.js';
|
||||||
import { vibrate } from '@/scripts/vibrate.js';
|
import { vibrate } from '@/scripts/vibrate.js';
|
||||||
|
|
|
@ -83,7 +83,6 @@ import { ref, computed } from 'vue';
|
||||||
import { toUnicode } from 'punycode/';
|
import { toUnicode } from 'punycode/';
|
||||||
import MkButton from './MkButton.vue';
|
import MkButton from './MkButton.vue';
|
||||||
import MkInput from './MkInput.vue';
|
import MkInput from './MkInput.vue';
|
||||||
import MkSwitch from './MkSwitch.vue';
|
|
||||||
import MkCaptcha, { type Captcha } from '@/components/MkCaptcha.vue';
|
import MkCaptcha, { type Captcha } from '@/components/MkCaptcha.vue';
|
||||||
import * as config from '@/config.js';
|
import * as config from '@/config.js';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
|
|
|
@ -62,7 +62,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, onMounted, ref, watch } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
import { instance } from '@/instance.js';
|
import { instance } from '@/instance.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
|
|
|
@ -39,7 +39,6 @@ import XSignup from '@/components/MkSignupDialog.form.vue';
|
||||||
import XServerRules from '@/components/MkSignupDialog.rules.vue';
|
import XServerRules from '@/components/MkSignupDialog.rules.vue';
|
||||||
import MkModalWindow from '@/components/MkModalWindow.vue';
|
import MkModalWindow from '@/components/MkModalWindow.vue';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { instance } from '@/instance.js';
|
|
||||||
|
|
||||||
const props = withDefaults(defineProps<{
|
const props = withDefaults(defineProps<{
|
||||||
autoSet?: boolean;
|
autoSet?: boolean;
|
||||||
|
|
|
@ -34,15 +34,9 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, ref, watch } from 'vue';
|
|
||||||
import { instance } from '@/instance.js';
|
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
|
||||||
import MkFolder from '@/components/MkFolder.vue';
|
import MkFolder from '@/components/MkFolder.vue';
|
||||||
import XUser from '@/components/MkUserSetupDialog.User.vue';
|
import XUser from '@/components/MkUserSetupDialog.User.vue';
|
||||||
import MkInfo from '@/components/MkInfo.vue';
|
|
||||||
import * as os from '@/os.js';
|
|
||||||
import { $i } from '@/account.js';
|
|
||||||
import MkPagination from '@/components/MkPagination.vue';
|
import MkPagination from '@/components/MkPagination.vue';
|
||||||
|
|
||||||
const pinnedUsers = { endpoint: 'pinned-users', noPaging: true };
|
const pinnedUsers = { endpoint: 'pinned-users', noPaging: true };
|
||||||
|
|
|
@ -44,14 +44,12 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, ref, watch } from 'vue';
|
import { ref, watch } from 'vue';
|
||||||
import { instance } from '@/instance.js';
|
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import MkSwitch from '@/components/MkSwitch.vue';
|
import MkSwitch from '@/components/MkSwitch.vue';
|
||||||
import MkInfo from '@/components/MkInfo.vue';
|
import MkInfo from '@/components/MkInfo.vue';
|
||||||
import MkFolder from '@/components/MkFolder.vue';
|
import MkFolder from '@/components/MkFolder.vue';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { $i } from '@/account.js';
|
|
||||||
|
|
||||||
const isLocked = ref(false);
|
const isLocked = ref(false);
|
||||||
const hideOnlineStatus = ref(false);
|
const hideOnlineStatus = ref(false);
|
||||||
|
|
|
@ -30,8 +30,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, ref, watch } from 'vue';
|
import { ref, watch } from 'vue';
|
||||||
import { instance } from '@/instance.js';
|
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import MkInput from '@/components/MkInput.vue';
|
import MkInput from '@/components/MkInput.vue';
|
||||||
|
|
|
@ -29,7 +29,6 @@ import * as Misskey from 'cherrypick-js';
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { $i } from '@/account.js';
|
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
|
|
@ -54,7 +54,6 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import * as Misskey from 'cherrypick-js';
|
import * as Misskey from 'cherrypick-js';
|
||||||
import XTimeline from './welcome.timeline.vue';
|
|
||||||
import XSigninDialog from '@/components/MkSigninDialog.vue';
|
import XSigninDialog from '@/components/MkSigninDialog.vue';
|
||||||
import XSignupDialog from '@/components/MkSignupDialog.vue';
|
import XSignupDialog from '@/components/MkSignupDialog.vue';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
|
@ -65,7 +64,6 @@ import * as os from '@/os.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { instance } from '@/instance.js';
|
import { instance } from '@/instance.js';
|
||||||
import { mainRouter } from '@/router.js';
|
import { mainRouter } from '@/router.js';
|
||||||
import number from '@/filters/number.js';
|
|
||||||
import MkNumber from '@/components/MkNumber.vue';
|
import MkNumber from '@/components/MkNumber.vue';
|
||||||
import XActiveUsersChart from '@/components/MkVisitorDashboard.ActiveUsersChart.vue';
|
import XActiveUsersChart from '@/components/MkVisitorDashboard.ActiveUsersChart.vue';
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,6 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, watch } from 'vue';
|
import { ref, watch } from 'vue';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import { defaultStore } from '@/store.js';
|
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
|
|
@ -14,7 +14,6 @@ import { computed } from 'vue';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import copyToClipboard from '@/scripts/copy-to-clipboard.js';
|
import copyToClipboard from '@/scripts/copy-to-clipboard.js';
|
||||||
import { url } from '@/config.js';
|
import { url } from '@/config.js';
|
||||||
import { popout as popout_ } from '@/scripts/popout.js';
|
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { useRouter } from '@/router.js';
|
import { useRouter } from '@/router.js';
|
||||||
import { defaultStore } from '@/store.js';
|
import { defaultStore } from '@/store.js';
|
||||||
|
|
|
@ -4,11 +4,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||||
import { expect } from '@storybook/jest';
|
|
||||||
import { userEvent, waitFor, within } from '@storybook/testing-library';
|
|
||||||
import { StoryObj } from '@storybook/vue3';
|
import { StoryObj } from '@storybook/vue3';
|
||||||
import MkAd from './MkAd.vue';
|
import MkAd from './MkAd.vue';
|
||||||
import { i18n } from '@/i18n.js';
|
|
||||||
|
|
||||||
let lock: Promise<undefined> | undefined;
|
let lock: Promise<undefined> | undefined;
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||||
import { expect } from '@storybook/jest';
|
import { expect } from '@storybook/jest';
|
||||||
import { userEvent, within } from '@storybook/testing-library';
|
|
||||||
import { StoryObj } from '@storybook/vue3';
|
import { StoryObj } from '@storybook/vue3';
|
||||||
import { userDetailed } from '../../../.storybook/fakes';
|
import { userDetailed } from '../../../.storybook/fakes';
|
||||||
import MkUserName from './MkUserName.vue';
|
import MkUserName from './MkUserName.vue';
|
||||||
|
|
|
@ -16,7 +16,6 @@ import * as mfm from 'cherrypick-mfm-js';
|
||||||
import * as Misskey from 'cherrypick-js';
|
import * as Misskey from 'cherrypick-js';
|
||||||
import { TextBlock } from './block.type';
|
import { TextBlock } from './block.type';
|
||||||
import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm.js';
|
import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm.js';
|
||||||
import { $i } from '@/account.js';
|
|
||||||
|
|
||||||
const MkUrlPreview = defineAsyncComponent(() => import('@/components/MkUrlPreview.vue'));
|
const MkUrlPreview = defineAsyncComponent(() => import('@/components/MkUrlPreview.vue'));
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,6 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, nextTick } from 'vue';
|
|
||||||
import * as Misskey from 'cherrypick-js';
|
import * as Misskey from 'cherrypick-js';
|
||||||
import XBlock from './page.block.vue';
|
import XBlock from './page.block.vue';
|
||||||
|
|
||||||
|
|
|
@ -54,7 +54,22 @@ https://github.com/sindresorhus/file-type/blob/main/core.js
|
||||||
https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Containers
|
https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Containers
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export const notificationTypes = ['note', 'follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'achievementEarned', 'app'] as const;
|
export const notificationTypes = [
|
||||||
|
'note',
|
||||||
|
'follow',
|
||||||
|
'mention',
|
||||||
|
'reply',
|
||||||
|
'renote',
|
||||||
|
'quote',
|
||||||
|
'reaction',
|
||||||
|
'pollEnded',
|
||||||
|
'receiveFollowRequest',
|
||||||
|
'followRequestAccepted',
|
||||||
|
'roleAssigned',
|
||||||
|
'groupInvited',
|
||||||
|
'achievementEarned',
|
||||||
|
'app',
|
||||||
|
] as const;
|
||||||
export const obsoleteNotificationTypes = ['pollVote'/*, 'groupInvited'*/] as const;
|
export const obsoleteNotificationTypes = ['pollVote'/*, 'groupInvited'*/] as const;
|
||||||
|
|
||||||
export const ROLE_POLICIES = [
|
export const ROLE_POLICIES = [
|
||||||
|
|
|
@ -218,12 +218,12 @@ import MkFileListForAdmin from '@/components/MkFileListForAdmin.vue';
|
||||||
import MkInfo from '@/components/MkInfo.vue';
|
import MkInfo from '@/components/MkInfo.vue';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { url } from '@/config.js';
|
import { url } from '@/config.js';
|
||||||
import { userPage, acct } from '@/filters/user.js';
|
import { acct } from '@/filters/user.js';
|
||||||
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { iAmAdmin, $i } from '@/account.js';
|
import { iAmAdmin, $i } from '@/account.js';
|
||||||
import MkRolePreview from '@/components/MkRolePreview.vue';
|
import MkRolePreview from '@/components/MkRolePreview.vue';
|
||||||
import MkPagination, { Paging } from '@/components/MkPagination.vue';
|
import MkPagination from '@/components/MkPagination.vue';
|
||||||
import { globalEvents } from '@/events.js';
|
import { globalEvents } from '@/events.js';
|
||||||
|
|
||||||
const props = withDefaults(defineProps<{
|
const props = withDefaults(defineProps<{
|
||||||
|
|
|
@ -97,11 +97,8 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
import { ref, computed } from 'vue';
|
import { ref, computed } from 'vue';
|
||||||
import JSON5 from 'json5';
|
import JSON5 from 'json5';
|
||||||
import XHeader from './_header_.vue';
|
import XHeader from './_header_.vue';
|
||||||
import MkSwitch from '@/components/MkSwitch.vue';
|
|
||||||
import MkInput from '@/components/MkInput.vue';
|
import MkInput from '@/components/MkInput.vue';
|
||||||
import MkTextarea from '@/components/MkTextarea.vue';
|
import MkTextarea from '@/components/MkTextarea.vue';
|
||||||
import FormSection from '@/components/form/section.vue';
|
|
||||||
import FormSplit from '@/components/form/split.vue';
|
|
||||||
import FormSuspense from '@/components/form/suspense.vue';
|
import FormSuspense from '@/components/form/suspense.vue';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { instance, fetchInstance } from '@/instance.js';
|
import { instance, fetchInstance } from '@/instance.js';
|
||||||
|
|
|
@ -64,8 +64,6 @@ import XHeader from './_header_.vue';
|
||||||
import MkSwitch from '@/components/MkSwitch.vue';
|
import MkSwitch from '@/components/MkSwitch.vue';
|
||||||
import MkInput from '@/components/MkInput.vue';
|
import MkInput from '@/components/MkInput.vue';
|
||||||
import MkTextarea from '@/components/MkTextarea.vue';
|
import MkTextarea from '@/components/MkTextarea.vue';
|
||||||
import FormSection from '@/components/form/section.vue';
|
|
||||||
import FormSplit from '@/components/form/split.vue';
|
|
||||||
import FormSuspense from '@/components/form/suspense.vue';
|
import FormSuspense from '@/components/form/suspense.vue';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { fetchInstance } from '@/instance.js';
|
import { fetchInstance } from '@/instance.js';
|
||||||
|
|
|
@ -123,9 +123,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
import * as Misskey from 'cherrypick-js';
|
import * as Misskey from 'cherrypick-js';
|
||||||
import { CodeDiff } from 'v-code-diff';
|
import { CodeDiff } from 'v-code-diff';
|
||||||
import JSON5 from 'json5';
|
import JSON5 from 'json5';
|
||||||
import * as os from '@/os.js';
|
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { dateString } from '@/filters/date.js';
|
|
||||||
import MkFolder from '@/components/MkFolder.vue';
|
import MkFolder from '@/components/MkFolder.vue';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
|
|
@ -69,7 +69,7 @@ import { useRouter } from '@/router.js';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import MkUserCardMini from '@/components/MkUserCardMini.vue';
|
import MkUserCardMini from '@/components/MkUserCardMini.vue';
|
||||||
import MkInfo from '@/components/MkInfo.vue';
|
import MkInfo from '@/components/MkInfo.vue';
|
||||||
import MkPagination, { Paging } from '@/components/MkPagination.vue';
|
import MkPagination from '@/components/MkPagination.vue';
|
||||||
import { infoImageUrl } from '@/instance.js';
|
import { infoImageUrl } from '@/instance.js';
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
|
@ -16,8 +16,6 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, watch } from 'vue';
|
|
||||||
import * as os from '@/os.js';
|
|
||||||
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { instance } from '@/instance.js';
|
import { instance } from '@/instance.js';
|
||||||
|
|
|
@ -46,9 +46,6 @@ import { ref, computed } from 'vue';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import MkInput from '@/components/MkInput.vue';
|
import MkInput from '@/components/MkInput.vue';
|
||||||
import MkTextarea from '@/components/MkTextarea.vue';
|
import MkTextarea from '@/components/MkTextarea.vue';
|
||||||
import MkSwitch from '@/components/MkSwitch.vue';
|
|
||||||
import MkRadios from '@/components/MkRadios.vue';
|
|
||||||
import MkInfo from '@/components/MkInfo.vue';
|
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
||||||
|
|
|
@ -86,7 +86,7 @@ import * as os from '@/os.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { customEmojiCategories } from '@/custom-emojis.js';
|
import { customEmojiCategories } from '@/custom-emojis.js';
|
||||||
import MkSwitch from '@/components/MkSwitch.vue';
|
import MkSwitch from '@/components/MkSwitch.vue';
|
||||||
import { selectFile, selectFiles } from '@/scripts/select-file.js';
|
import { selectFile } from '@/scripts/select-file.js';
|
||||||
import MkRolePreview from '@/components/MkRolePreview.vue';
|
import MkRolePreview from '@/components/MkRolePreview.vue';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
|
|
|
@ -68,7 +68,7 @@ import MkInput from '@/components/MkInput.vue';
|
||||||
import { userListsCache } from '@/cache.js';
|
import { userListsCache } from '@/cache.js';
|
||||||
import { $i } from '@/account.js';
|
import { $i } from '@/account.js';
|
||||||
import { defaultStore } from '@/store.js';
|
import { defaultStore } from '@/store.js';
|
||||||
import MkPagination, { Paging } from '@/components/MkPagination.vue';
|
import MkPagination from '@/components/MkPagination.vue';
|
||||||
|
|
||||||
const {
|
const {
|
||||||
enableInfiniteScroll,
|
enableInfiniteScroll,
|
||||||
|
|
|
@ -29,7 +29,6 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { i18n } from '@/i18n.js';
|
|
||||||
|
|
||||||
const props = withDefaults(defineProps<{
|
const props = withDefaults(defineProps<{
|
||||||
expanded?: boolean;
|
expanded?: boolean;
|
||||||
|
|
|
@ -47,18 +47,14 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, onMounted, ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import MkNotes from '@/components/MkNotes.vue';
|
import MkNotes from '@/components/MkNotes.vue';
|
||||||
import MkInput from '@/components/MkInput.vue';
|
import MkInput from '@/components/MkInput.vue';
|
||||||
import MkRadios from '@/components/MkRadios.vue';
|
import MkRadios from '@/components/MkRadios.vue';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import MkSwitch from '@/components/MkSwitch.vue';
|
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import MkFoldableSection from '@/components/MkFoldableSection.vue';
|
import MkFoldableSection from '@/components/MkFoldableSection.vue';
|
||||||
import { $i } from '@/account.js';
|
|
||||||
import { instance } from '@/instance.js';
|
|
||||||
import MkInfo from '@/components/MkInfo.vue';
|
|
||||||
import { useRouter } from '@/router.js';
|
import { useRouter } from '@/router.js';
|
||||||
import MkFolder from '@/components/MkFolder.vue';
|
import MkFolder from '@/components/MkFolder.vue';
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue