Merge remote-tracking branch 'misskey-original/develop' into develop
# Conflicts: # package.json # packages/backend/src/server/api/endpoints.ts # packages/frontend/src/components/MkButton.vue # packages/frontend/src/components/MkDrive.vue # packages/frontend/src/components/MkEmojiPicker.section.vue # packages/frontend/src/components/MkEmojiPicker.vue # packages/frontend/src/components/MkFoldableSection.vue # packages/frontend/src/components/MkInput.vue # packages/frontend/src/components/MkMenu.vue # packages/frontend/src/components/MkNote.vue # packages/frontend/src/components/MkPostForm.vue # packages/frontend/src/components/MkSignupDialog.form.vue # packages/frontend/src/components/MkSwitch.button.vue # packages/frontend/src/components/MkTab.vue # packages/frontend/src/components/MkTimeline.vue # packages/frontend/src/components/MkUserSelectDialog.vue # packages/frontend/src/components/global/MkCustomEmoji.vue # packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts # packages/frontend/src/os.ts # packages/frontend/src/pages/admin/index.vue # packages/frontend/src/pages/user/home.vue # packages/frontend/src/ui/universal.vue
This commit is contained in:
commit
942b7f1b3c
66
.github/workflows/deploy-test-environment.yml
vendored
Normal file
66
.github/workflows/deploy-test-environment.yml
vendored
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
name: Deploy test environment
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
repository:
|
||||||
|
description: 'Repository to deploy (optional)'
|
||||||
|
required: false
|
||||||
|
branch:
|
||||||
|
description: 'Branch to deploy (optional)'
|
||||||
|
required: false
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy-test-environment:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Set environment variable (for tput command & pnpm)
|
||||||
|
run: |
|
||||||
|
echo "TERM=xterm" >> $GITHUB_ENV
|
||||||
|
REPOSITORY=${{ github.event.inputs.repository || github.repository }}
|
||||||
|
echo "REPOSITORY=$REPOSITORY" >> $GITHUB_ENV
|
||||||
|
BRANCH=${{ github.event.inputs.branch || github.ref_name }}
|
||||||
|
echo "BRANCH=$BRANCH" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
repository: ${{ env.REPOSITORY }}
|
||||||
|
ref: ${{ env.BRANCH }}
|
||||||
|
|
||||||
|
- name: Get the latest commit SHA
|
||||||
|
run: |
|
||||||
|
SHA=$(git log -1 --format="%H")
|
||||||
|
echo "SHA=$SHA" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Start cloudflare tunnel (quick)
|
||||||
|
run: |
|
||||||
|
wget https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
|
||||||
|
sudo dpkg -i cloudflared-linux-amd64.deb
|
||||||
|
sudo cloudflared tunnel --metrics localhost:55555 --url localhost:3000 > /dev/null 2>&1 &
|
||||||
|
sleep 15
|
||||||
|
TUNNEL_RESPONSE=$(curl http://localhost:55555/quicktunnel)
|
||||||
|
TUNNEL_DOMAIN=$(echo $TUNNEL_RESPONSE | grep -o '"hostname":"[^"]*' | grep -o '[^"]*$')
|
||||||
|
echo "::add-mask::$TUNNEL_DOMAIN"
|
||||||
|
echo "TUNNEL_DOMAIN=$TUNNEL_DOMAIN" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Install misskey
|
||||||
|
run: |
|
||||||
|
wget https://raw.githubusercontent.com/joinmisskey/bash-install/v4/misskey-install.sh
|
||||||
|
wget https://raw.githubusercontent.com/joinmisskey/bash-install/v4/testenv_githubactions.txt
|
||||||
|
sed -i "s/host=127.0.0.1/host=$TUNNEL_DOMAIN/g" testenv_githubactions.txt
|
||||||
|
sed -i "s|git_repository=https://github.com/misskey-dev/misskey|git_repository=https://github.com/$REPOSITORY|g" testenv_githubactions.txt
|
||||||
|
sed -i "s|git_branch=master|git_branch=$BRANCH|g" testenv_githubactions.txt
|
||||||
|
sudo chmod 555 ./misskey-install.sh
|
||||||
|
sudo bash -x ./misskey-install.sh -c ./testenv_githubactions.txt
|
||||||
|
|
||||||
|
- name: Post tunnel info to Discord
|
||||||
|
run: |
|
||||||
|
CURRENT_TIME=$(TZ=Asia/Tokyo date +'%Y-%m-%d %H:%M:%S JST')
|
||||||
|
COMMIT_URL="https://github.com/$REPOSITORY/commit/$SHA"
|
||||||
|
curl -X POST -H "Content-Type: application/json" -d "{\"content\": \"==============================\nURL: https://$TUNNEL_DOMAIN\nRepository: $REPOSITORY\nBranch: $BRANCH\nCommit: $COMMIT_URL\nTime: $CURRENT_TIME\n==============================\"}" ${{ secrets.DISCORD_WEBHOOK_URL }}
|
||||||
|
|
||||||
|
- name: Wait
|
||||||
|
run: |
|
||||||
|
timeout 3600 tail -f /var/log/syslog || true
|
|
@ -50,7 +50,14 @@
|
||||||
- Fix: v2023.12.0で追加された「モデレーターがユーザーのアイコンもしくはバナー画像を未設定状態にできる機能」が管理画面上で正しく表示されていない問題を修正
|
- Fix: v2023.12.0で追加された「モデレーターがユーザーのアイコンもしくはバナー画像を未設定状態にできる機能」が管理画面上で正しく表示されていない問題を修正
|
||||||
- Fix: AiScriptの`readline`関数が不正な値を返すことがある問題のv2023.12.0時点での修正がPlay以外に適用されていないのを修正
|
- Fix: AiScriptの`readline`関数が不正な値を返すことがある問題のv2023.12.0時点での修正がPlay以外に適用されていないのを修正
|
||||||
- Fix: v2023.12.1で追加された`$[clickable ...]`および`onClickEv`が正しく機能していないのを修正
|
- Fix: v2023.12.1で追加された`$[clickable ...]`および`onClickEv`が正しく機能していないのを修正
|
||||||
|
- Fix: Renoteのキーボードショートカットが機能していなかった問題を修正
|
||||||
|
- Fix: 投稿フォームでアンケートの日時指定をした状態で再読み込みをすると期日が復元されない問題を修正
|
||||||
|
- Fix: アンケートを設定したノートを「削除して編集」をするとアンケートの期日が引き継がれず、リセットされてしまう問題を修正
|
||||||
|
- Fix: デッキのプロファイル作成時に名前を空にできる問題を修正
|
||||||
|
- Fix: テーマ作成時に名称が空欄でも作成できてしまう問題を修正
|
||||||
|
- Fix: プラグインで`Plugin:register_note_post_interruptor`を使用すると、ノートが投稿できなくなる問題を修正
|
||||||
- Enhance: ページ遷移時にPlayerを閉じるように
|
- Enhance: ページ遷移時にPlayerを閉じるように
|
||||||
|
- Fix: iOSで大きな画像を変換してアップロードできない問題を修正
|
||||||
|
|
||||||
### Server
|
### Server
|
||||||
- Enhance: 連合先のレートリミットに引っかかった際にリトライするようになりました
|
- Enhance: 連合先のレートリミットに引っかかった際にリトライするようになりました
|
||||||
|
|
2
locales/index.d.ts
vendored
2
locales/index.d.ts
vendored
|
@ -5098,7 +5098,7 @@ export interface Locale extends ILocale {
|
||||||
*/
|
*/
|
||||||
"readConfirmText": ParameterizedString<"title">;
|
"readConfirmText": ParameterizedString<"title">;
|
||||||
/**
|
/**
|
||||||
* 特に新規ユーザーのUXを損ねる可能性が高いため、ストック情報ではなくフロー情報の掲示にお知らせを使用することを推奨します。
|
* 特に新規ユーザーのUXを損ねる可能性が高いため、常時掲示するための情報ではなく、即時性が求められる情報の掲示のためにお知らせを使用することを推奨します。
|
||||||
*/
|
*/
|
||||||
"shouldNotBeUsedToPresentPermanentInfo": string;
|
"shouldNotBeUsedToPresentPermanentInfo": string;
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1274,7 +1274,7 @@ _announcement:
|
||||||
tooManyActiveAnnouncementDescription: "アクティブなお知らせが多いため、UXが低下する可能性があります。終了したお知らせはアーカイブすることを検討してください。"
|
tooManyActiveAnnouncementDescription: "アクティブなお知らせが多いため、UXが低下する可能性があります。終了したお知らせはアーカイブすることを検討してください。"
|
||||||
readConfirmTitle: "既読にしますか?"
|
readConfirmTitle: "既読にしますか?"
|
||||||
readConfirmText: "「{title}」の内容を読み、既読にします。"
|
readConfirmText: "「{title}」の内容を読み、既読にします。"
|
||||||
shouldNotBeUsedToPresentPermanentInfo: "特に新規ユーザーのUXを損ねる可能性が高いため、ストック情報ではなくフロー情報の掲示にお知らせを使用することを推奨します。"
|
shouldNotBeUsedToPresentPermanentInfo: "特に新規ユーザーのUXを損ねる可能性が高いため、常時掲示するための情報ではなく、即時性が求められる情報の掲示のためにお知らせを使用することを推奨します。"
|
||||||
dialogAnnouncementUxWarn: "ダイアログ形式のお知らせが同時に2つ以上ある場合、UXに悪影響を及ぼす可能性が非常に高いため、使用は慎重に行うことを推奨します。"
|
dialogAnnouncementUxWarn: "ダイアログ形式のお知らせが同時に2つ以上ある場合、UXに悪影響を及ぼす可能性が非常に高いため、使用は慎重に行うことを推奨します。"
|
||||||
silence: "非通知"
|
silence: "非通知"
|
||||||
silenceDescription: "オンにすると、このお知らせは通知されず、既読にする必要もなくなります。"
|
silenceDescription: "オンにすると、このお知らせは通知されず、既読にする必要もなくなります。"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "misskey",
|
"name": "misskey",
|
||||||
"version": "2024.2.0-beta.7-PrisMisskey.1",
|
"version": "2024.2.0-beta.8-PrisMisskey.1",
|
||||||
"codename": "nasubi",
|
"codename": "nasubi",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|
|
@ -67,9 +67,9 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-s3": "3.412.0",
|
"@aws-sdk/client-s3": "3.412.0",
|
||||||
"@aws-sdk/lib-storage": "3.412.0",
|
"@aws-sdk/lib-storage": "3.412.0",
|
||||||
"@bull-board/api": "5.10.2",
|
"@bull-board/api": "5.14.0",
|
||||||
"@bull-board/fastify": "5.10.2",
|
"@bull-board/fastify": "5.14.0",
|
||||||
"@bull-board/ui": "5.10.2",
|
"@bull-board/ui": "5.14.0",
|
||||||
"@discordapp/twemoji": "15.0.2",
|
"@discordapp/twemoji": "15.0.2",
|
||||||
"@fastify/accepts": "4.3.0",
|
"@fastify/accepts": "4.3.0",
|
||||||
"@fastify/cookie": "9.3.1",
|
"@fastify/cookie": "9.3.1",
|
||||||
|
@ -85,11 +85,11 @@
|
||||||
"@nestjs/core": "10.2.10",
|
"@nestjs/core": "10.2.10",
|
||||||
"@nestjs/testing": "10.2.10",
|
"@nestjs/testing": "10.2.10",
|
||||||
"@peertube/http-signature": "1.7.0",
|
"@peertube/http-signature": "1.7.0",
|
||||||
"@simplewebauthn/server": "9.0.0",
|
"@simplewebauthn/server": "9.0.1",
|
||||||
"@sinonjs/fake-timers": "11.2.2",
|
"@sinonjs/fake-timers": "11.2.2",
|
||||||
"@smithy/node-http-handler": "2.1.10",
|
"@smithy/node-http-handler": "2.1.10",
|
||||||
"@swc/cli": "0.1.63",
|
"@swc/cli": "0.1.63",
|
||||||
"@swc/core": "1.3.105",
|
"@swc/core": "1.3.107",
|
||||||
"@twemoji/parser": "15.0.0",
|
"@twemoji/parser": "15.0.0",
|
||||||
"accepts": "1.3.8",
|
"accepts": "1.3.8",
|
||||||
"ajv": "8.12.0",
|
"ajv": "8.12.0",
|
||||||
|
@ -98,7 +98,7 @@
|
||||||
"bcryptjs": "2.4.3",
|
"bcryptjs": "2.4.3",
|
||||||
"blurhash": "2.0.5",
|
"blurhash": "2.0.5",
|
||||||
"body-parser": "1.20.2",
|
"body-parser": "1.20.2",
|
||||||
"bullmq": "5.1.4",
|
"bullmq": "5.1.5",
|
||||||
"cacheable-lookup": "7.0.0",
|
"cacheable-lookup": "7.0.0",
|
||||||
"cbor": "9.0.1",
|
"cbor": "9.0.1",
|
||||||
"chalk": "5.3.0",
|
"chalk": "5.3.0",
|
||||||
|
@ -115,7 +115,7 @@
|
||||||
"file-type": "19.0.0",
|
"file-type": "19.0.0",
|
||||||
"fluent-ffmpeg": "2.1.2",
|
"fluent-ffmpeg": "2.1.2",
|
||||||
"form-data": "4.0.0",
|
"form-data": "4.0.0",
|
||||||
"got": "14.0.0",
|
"got": "14.1.0",
|
||||||
"happy-dom": "10.0.3",
|
"happy-dom": "10.0.3",
|
||||||
"hpagent": "1.2.0",
|
"hpagent": "1.2.0",
|
||||||
"http-link-header": "1.1.1",
|
"http-link-header": "1.1.1",
|
||||||
|
@ -147,7 +147,7 @@
|
||||||
"otpauth": "9.2.2",
|
"otpauth": "9.2.2",
|
||||||
"parse5": "7.1.2",
|
"parse5": "7.1.2",
|
||||||
"pg": "8.11.3",
|
"pg": "8.11.3",
|
||||||
"pkce-challenge": "4.0.1",
|
"pkce-challenge": "4.1.0",
|
||||||
"probe-image-size": "7.2.3",
|
"probe-image-size": "7.2.3",
|
||||||
"promise-limit": "2.7.0",
|
"promise-limit": "2.7.0",
|
||||||
"pug": "3.0.2",
|
"pug": "3.0.2",
|
||||||
|
@ -168,12 +168,12 @@
|
||||||
"slacc": "0.0.10",
|
"slacc": "0.0.10",
|
||||||
"strict-event-emitter-types": "2.0.0",
|
"strict-event-emitter-types": "2.0.0",
|
||||||
"stringz": "2.1.0",
|
"stringz": "2.1.0",
|
||||||
"systeminformation": "5.21.23",
|
"systeminformation": "5.21.24",
|
||||||
"tinycolor2": "1.6.0",
|
"tinycolor2": "1.6.0",
|
||||||
"tmp": "0.2.1",
|
"tmp": "0.2.1",
|
||||||
"tsc-alias": "1.8.8",
|
"tsc-alias": "1.8.8",
|
||||||
"tsconfig-paths": "4.2.0",
|
"tsconfig-paths": "4.2.0",
|
||||||
"typeorm": "0.3.19",
|
"typeorm": "0.3.20",
|
||||||
"typescript": "5.3.3",
|
"typescript": "5.3.3",
|
||||||
"ulid": "2.3.0",
|
"ulid": "2.3.0",
|
||||||
"vary": "1.1.2",
|
"vary": "1.1.2",
|
||||||
|
@ -184,7 +184,7 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@jest/globals": "29.7.0",
|
"@jest/globals": "29.7.0",
|
||||||
"@misskey-dev/eslint-plugin": "1.0.0",
|
"@misskey-dev/eslint-plugin": "1.0.0",
|
||||||
"@nestjs/platform-express": "10.3.0",
|
"@nestjs/platform-express": "10.3.1",
|
||||||
"@simplewebauthn/typescript-types": "8.3.4",
|
"@simplewebauthn/typescript-types": "8.3.4",
|
||||||
"@swc/jest": "0.2.31",
|
"@swc/jest": "0.2.31",
|
||||||
"@types/accepts": "1.3.7",
|
"@types/accepts": "1.3.7",
|
||||||
|
@ -203,13 +203,13 @@
|
||||||
"@types/jsrsasign": "10.5.12",
|
"@types/jsrsasign": "10.5.12",
|
||||||
"@types/mime-types": "2.1.4",
|
"@types/mime-types": "2.1.4",
|
||||||
"@types/ms": "0.7.34",
|
"@types/ms": "0.7.34",
|
||||||
"@types/node": "20.11.5",
|
"@types/node": "20.11.10",
|
||||||
"@types/node-fetch": "3.0.3",
|
"@types/node-fetch": "3.0.3",
|
||||||
"@types/nodemailer": "6.4.14",
|
"@types/nodemailer": "6.4.14",
|
||||||
"@types/oauth": "0.9.4",
|
"@types/oauth": "0.9.4",
|
||||||
"@types/oauth2orize": "1.11.3",
|
"@types/oauth2orize": "1.11.3",
|
||||||
"@types/oauth2orize-pkce": "0.1.2",
|
"@types/oauth2orize-pkce": "0.1.2",
|
||||||
"@types/pg": "8.10.9",
|
"@types/pg": "8.11.0",
|
||||||
"@types/pug": "2.0.10",
|
"@types/pug": "2.0.10",
|
||||||
"@types/punycode": "2.1.3",
|
"@types/punycode": "2.1.3",
|
||||||
"@types/qrcode": "1.5.5",
|
"@types/qrcode": "1.5.5",
|
||||||
|
|
|
@ -96,7 +96,7 @@ export class AccountMoveService {
|
||||||
await this.apDeliverManagerService.deliverToFollowers(src, moveAct);
|
await this.apDeliverManagerService.deliverToFollowers(src, moveAct);
|
||||||
|
|
||||||
// Publish meUpdated event
|
// Publish meUpdated event
|
||||||
const iObj = await this.userEntityService.pack<true, true>(src.id, src, { detail: true, includeSecrets: true });
|
const iObj = await this.userEntityService.pack(src.id, src, { schema: 'MeDetailed', includeSecrets: true });
|
||||||
this.globalEventService.publishMainStream(src.id, 'meUpdated', iObj);
|
this.globalEventService.publishMainStream(src.id, 'meUpdated', iObj);
|
||||||
|
|
||||||
// Unfollow after 24 hours
|
// Unfollow after 24 hours
|
||||||
|
|
|
@ -54,15 +54,15 @@ export interface MainEventTypes {
|
||||||
reply: Packed<'Note'>;
|
reply: Packed<'Note'>;
|
||||||
renote: Packed<'Note'>;
|
renote: Packed<'Note'>;
|
||||||
follow: Packed<'UserDetailedNotMe'>;
|
follow: Packed<'UserDetailedNotMe'>;
|
||||||
followed: Packed<'User'>;
|
followed: Packed<'UserLite'>;
|
||||||
unfollow: Packed<'User'>;
|
unfollow: Packed<'UserDetailedNotMe'>;
|
||||||
meUpdated: Packed<'User'>;
|
meUpdated: Packed<'MeDetailed'>;
|
||||||
pageEvent: {
|
pageEvent: {
|
||||||
pageId: MiPage['id'];
|
pageId: MiPage['id'];
|
||||||
event: string;
|
event: string;
|
||||||
var: any;
|
var: any;
|
||||||
userId: MiUser['id'];
|
userId: MiUser['id'];
|
||||||
user: Packed<'User'>;
|
user: Packed<'UserDetailed'>;
|
||||||
};
|
};
|
||||||
urlUploadFinished: {
|
urlUploadFinished: {
|
||||||
marker?: string | null;
|
marker?: string | null;
|
||||||
|
@ -92,7 +92,7 @@ export interface MainEventTypes {
|
||||||
};
|
};
|
||||||
driveFileCreated: Packed<'DriveFile'>;
|
driveFileCreated: Packed<'DriveFile'>;
|
||||||
readAntenna: MiAntenna;
|
readAntenna: MiAntenna;
|
||||||
receiveFollowRequest: Packed<'User'>;
|
receiveFollowRequest: Packed<'UserLite'>;
|
||||||
announcementCreated: {
|
announcementCreated: {
|
||||||
announcement: Packed<'Announcement'>;
|
announcement: Packed<'Announcement'>;
|
||||||
};
|
};
|
||||||
|
@ -140,8 +140,8 @@ type NoteStreamEventTypes = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface UserListEventTypes {
|
export interface UserListEventTypes {
|
||||||
userAdded: Packed<'User'>;
|
userAdded: Packed<'UserLite'>;
|
||||||
userRemoved: Packed<'User'>;
|
userRemoved: Packed<'UserLite'>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AntennaEventTypes {
|
export interface AntennaEventTypes {
|
||||||
|
|
|
@ -109,13 +109,13 @@ export class UserBlockingService implements OnModuleInit {
|
||||||
|
|
||||||
if (this.userEntityService.isLocalUser(followee)) {
|
if (this.userEntityService.isLocalUser(followee)) {
|
||||||
this.userEntityService.pack(followee, followee, {
|
this.userEntityService.pack(followee, followee, {
|
||||||
detail: true,
|
schema: 'MeDetailed',
|
||||||
}).then(packed => this.globalEventService.publishMainStream(followee.id, 'meUpdated', packed));
|
}).then(packed => this.globalEventService.publishMainStream(followee.id, 'meUpdated', packed));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.userEntityService.isLocalUser(follower) && !silent) {
|
if (this.userEntityService.isLocalUser(follower) && !silent) {
|
||||||
this.userEntityService.pack(followee, follower, {
|
this.userEntityService.pack(followee, follower, {
|
||||||
detail: true,
|
schema: 'UserDetailedNotMe',
|
||||||
}).then(async packed => {
|
}).then(async packed => {
|
||||||
this.globalEventService.publishMainStream(follower.id, 'unfollow', packed);
|
this.globalEventService.publishMainStream(follower.id, 'unfollow', packed);
|
||||||
|
|
||||||
|
|
|
@ -293,9 +293,9 @@ export class UserFollowingService implements OnModuleInit {
|
||||||
if (this.userEntityService.isLocalUser(follower) && !silent) {
|
if (this.userEntityService.isLocalUser(follower) && !silent) {
|
||||||
// Publish follow event
|
// Publish follow event
|
||||||
this.userEntityService.pack(followee.id, follower, {
|
this.userEntityService.pack(followee.id, follower, {
|
||||||
detail: true,
|
schema: 'UserDetailedNotMe',
|
||||||
}).then(async packed => {
|
}).then(async packed => {
|
||||||
this.globalEventService.publishMainStream(follower.id, 'follow', packed as Packed<'UserDetailedNotMe'>);
|
this.globalEventService.publishMainStream(follower.id, 'follow', packed);
|
||||||
|
|
||||||
const webhooks = (await this.webhookService.getActiveWebhooks()).filter(x => x.userId === follower.id && x.on.includes('follow'));
|
const webhooks = (await this.webhookService.getActiveWebhooks()).filter(x => x.userId === follower.id && x.on.includes('follow'));
|
||||||
for (const webhook of webhooks) {
|
for (const webhook of webhooks) {
|
||||||
|
@ -360,7 +360,7 @@ export class UserFollowingService implements OnModuleInit {
|
||||||
if (!silent && this.userEntityService.isLocalUser(follower)) {
|
if (!silent && this.userEntityService.isLocalUser(follower)) {
|
||||||
// Publish unfollow event
|
// Publish unfollow event
|
||||||
this.userEntityService.pack(followee.id, follower, {
|
this.userEntityService.pack(followee.id, follower, {
|
||||||
detail: true,
|
schema: 'UserDetailedNotMe',
|
||||||
}).then(async packed => {
|
}).then(async packed => {
|
||||||
this.globalEventService.publishMainStream(follower.id, 'unfollow', packed);
|
this.globalEventService.publishMainStream(follower.id, 'unfollow', packed);
|
||||||
|
|
||||||
|
@ -500,7 +500,7 @@ export class UserFollowingService implements OnModuleInit {
|
||||||
this.userEntityService.pack(follower.id, followee).then(packed => this.globalEventService.publishMainStream(followee.id, 'receiveFollowRequest', packed));
|
this.userEntityService.pack(follower.id, followee).then(packed => this.globalEventService.publishMainStream(followee.id, 'receiveFollowRequest', packed));
|
||||||
|
|
||||||
this.userEntityService.pack(followee.id, followee, {
|
this.userEntityService.pack(followee.id, followee, {
|
||||||
detail: true,
|
schema: 'MeDetailed',
|
||||||
}).then(packed => this.globalEventService.publishMainStream(followee.id, 'meUpdated', packed));
|
}).then(packed => this.globalEventService.publishMainStream(followee.id, 'meUpdated', packed));
|
||||||
|
|
||||||
// 通知を作成
|
// 通知を作成
|
||||||
|
@ -548,7 +548,7 @@ export class UserFollowingService implements OnModuleInit {
|
||||||
});
|
});
|
||||||
|
|
||||||
this.userEntityService.pack(followee.id, followee, {
|
this.userEntityService.pack(followee.id, followee, {
|
||||||
detail: true,
|
schema: 'MeDetailed',
|
||||||
}).then(packed => this.globalEventService.publishMainStream(followee.id, 'meUpdated', packed));
|
}).then(packed => this.globalEventService.publishMainStream(followee.id, 'meUpdated', packed));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -576,7 +576,7 @@ export class UserFollowingService implements OnModuleInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.userEntityService.pack(followee.id, followee, {
|
this.userEntityService.pack(followee.id, followee, {
|
||||||
detail: true,
|
schema: 'MeDetailed',
|
||||||
}).then(packed => this.globalEventService.publishMainStream(followee.id, 'meUpdated', packed));
|
}).then(packed => this.globalEventService.publishMainStream(followee.id, 'meUpdated', packed));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -696,7 +696,7 @@ export class UserFollowingService implements OnModuleInit {
|
||||||
@bindThis
|
@bindThis
|
||||||
private async publishUnfollow(followee: Both, follower: Local): Promise<void> {
|
private async publishUnfollow(followee: Both, follower: Local): Promise<void> {
|
||||||
const packedFollowee = await this.userEntityService.pack(followee.id, follower, {
|
const packedFollowee = await this.userEntityService.pack(followee.id, follower, {
|
||||||
detail: true,
|
schema: 'UserDetailedNotMe',
|
||||||
});
|
});
|
||||||
|
|
||||||
this.globalEventService.publishMainStream(follower.id, 'unfollow', packedFollowee);
|
this.globalEventService.publishMainStream(follower.id, 'unfollow', packedFollowee);
|
||||||
|
|
|
@ -94,6 +94,29 @@ type ToJsonSchema<S> = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export function getJsonSchema<S extends Schema>(schema: S): ToJsonSchema<Unflatten<ChartResult<S>>> {
|
export function getJsonSchema<S extends Schema>(schema: S): ToJsonSchema<Unflatten<ChartResult<S>>> {
|
||||||
|
const unflatten = (str: string, parent: Record<string, any>) => {
|
||||||
|
const keys = str.split('.');
|
||||||
|
const key = keys.shift();
|
||||||
|
const nextKey = keys[0];
|
||||||
|
|
||||||
|
if (key == null) return;
|
||||||
|
|
||||||
|
if (parent.properties[key] == null) {
|
||||||
|
parent.properties[key] = nextKey ? {
|
||||||
|
type: 'object',
|
||||||
|
properties: {},
|
||||||
|
required: [],
|
||||||
|
} : {
|
||||||
|
type: 'array',
|
||||||
|
items: {
|
||||||
|
type: 'number',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nextKey) unflatten(keys.join('.'), parent.properties[key] as Record<string, any>);
|
||||||
|
};
|
||||||
|
|
||||||
const jsonSchema = {
|
const jsonSchema = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {} as Record<string, unknown>,
|
properties: {} as Record<string, unknown>,
|
||||||
|
@ -101,10 +124,7 @@ export function getJsonSchema<S extends Schema>(schema: S): ToJsonSchema<Unflatt
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const k in schema) {
|
for (const k in schema) {
|
||||||
jsonSchema.properties[k] = {
|
unflatten(k, jsonSchema);
|
||||||
type: 'array',
|
|
||||||
items: { type: 'number' },
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return jsonSchema as ToJsonSchema<Unflatten<ChartResult<S>>>;
|
return jsonSchema as ToJsonSchema<Unflatten<ChartResult<S>>>;
|
||||||
|
|
|
@ -60,13 +60,13 @@ export class AbuseUserReportEntityService {
|
||||||
targetUserId: report.targetUserId,
|
targetUserId: report.targetUserId,
|
||||||
assigneeId: report.assigneeId,
|
assigneeId: report.assigneeId,
|
||||||
reporter: this.userEntityService.pack(report.reporter ?? report.reporterId, null, {
|
reporter: this.userEntityService.pack(report.reporter ?? report.reporterId, null, {
|
||||||
detail: true,
|
schema: 'UserDetailedNotMe',
|
||||||
}),
|
}),
|
||||||
targetUser: this.userEntityService.pack(report.targetUser ?? report.targetUserId, null, {
|
targetUser: this.userEntityService.pack(report.targetUser ?? report.targetUserId, null, {
|
||||||
detail: true,
|
schema: 'UserDetailedNotMe',
|
||||||
}),
|
}),
|
||||||
assignee: report.assigneeId ? this.userEntityService.pack(report.assignee ?? report.assigneeId, null, {
|
assignee: report.assigneeId ? this.userEntityService.pack(report.assignee ?? report.assigneeId, null, {
|
||||||
detail: true,
|
schema: 'UserDetailedNotMe',
|
||||||
}) : null,
|
}) : null,
|
||||||
forwarded: report.forwarded,
|
forwarded: report.forwarded,
|
||||||
});
|
});
|
||||||
|
|
|
@ -37,7 +37,7 @@ export class BlockingEntityService {
|
||||||
createdAt: this.idService.parse(blocking.id).date.toISOString(),
|
createdAt: this.idService.parse(blocking.id).date.toISOString(),
|
||||||
blockeeId: blocking.blockeeId,
|
blockeeId: blocking.blockeeId,
|
||||||
blockee: this.userEntityService.pack(blocking.blockeeId, me, {
|
blockee: this.userEntityService.pack(blocking.blockeeId, me, {
|
||||||
detail: true,
|
schema: 'UserDetailedNotMe',
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,7 @@ export class FlashEntityService {
|
||||||
createdAt: this.idService.parse(flash.id).date.toISOString(),
|
createdAt: this.idService.parse(flash.id).date.toISOString(),
|
||||||
updatedAt: flash.updatedAt.toISOString(),
|
updatedAt: flash.updatedAt.toISOString(),
|
||||||
userId: flash.userId,
|
userId: flash.userId,
|
||||||
user: this.userEntityService.pack(flash.user ?? flash.userId, me), // { detail: true } すると無限ループするので注意
|
user: this.userEntityService.pack(flash.user ?? flash.userId, me), // { schema: 'UserDetailed' } すると無限ループするので注意
|
||||||
title: flash.title,
|
title: flash.title,
|
||||||
summary: flash.summary,
|
summary: flash.summary,
|
||||||
script: flash.script,
|
script: flash.script,
|
||||||
|
|
|
@ -89,10 +89,10 @@ export class FollowingEntityService {
|
||||||
followeeId: following.followeeId,
|
followeeId: following.followeeId,
|
||||||
followerId: following.followerId,
|
followerId: following.followerId,
|
||||||
followee: opts.populateFollowee ? this.userEntityService.pack(following.followee ?? following.followeeId, me, {
|
followee: opts.populateFollowee ? this.userEntityService.pack(following.followee ?? following.followeeId, me, {
|
||||||
detail: true,
|
schema: 'UserDetailedNotMe',
|
||||||
}) : undefined,
|
}) : undefined,
|
||||||
follower: opts.populateFollower ? this.userEntityService.pack(following.follower ?? following.followerId, me, {
|
follower: opts.populateFollower ? this.userEntityService.pack(following.follower ?? following.followerId, me, {
|
||||||
detail: true,
|
schema: 'UserDetailedNotMe',
|
||||||
}) : undefined,
|
}) : undefined,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ export class ModerationLogEntityService {
|
||||||
info: log.info,
|
info: log.info,
|
||||||
userId: log.userId,
|
userId: log.userId,
|
||||||
user: this.userEntityService.pack(log.user ?? log.userId, null, {
|
user: this.userEntityService.pack(log.user ?? log.userId, null, {
|
||||||
detail: true,
|
schema: 'UserDetailedNotMe',
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,7 @@ export class MutingEntityService {
|
||||||
expiresAt: muting.expiresAt ? muting.expiresAt.toISOString() : null,
|
expiresAt: muting.expiresAt ? muting.expiresAt.toISOString() : null,
|
||||||
muteeId: muting.muteeId,
|
muteeId: muting.muteeId,
|
||||||
mutee: this.userEntityService.pack(muting.muteeId, me, {
|
mutee: this.userEntityService.pack(muting.muteeId, me, {
|
||||||
detail: true,
|
schema: 'UserDetailedNotMe',
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -164,7 +164,7 @@ export class NoteEntityService implements OnModuleInit {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
multiple: poll.multiple,
|
multiple: poll.multiple,
|
||||||
expiresAt: poll.expiresAt,
|
expiresAt: poll.expiresAt?.toISOString() ?? null,
|
||||||
choices,
|
choices,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -325,9 +325,7 @@ export class NoteEntityService implements OnModuleInit {
|
||||||
createdAt: this.idService.parse(note.id).date.toISOString(),
|
createdAt: this.idService.parse(note.id).date.toISOString(),
|
||||||
updatedAt: note.updatedAt ? note.updatedAt.toISOString() : undefined,
|
updatedAt: note.updatedAt ? note.updatedAt.toISOString() : undefined,
|
||||||
userId: note.userId,
|
userId: note.userId,
|
||||||
user: this.userEntityService.pack(note.user ?? note.userId, me, {
|
user: this.userEntityService.pack(note.user ?? note.userId, me),
|
||||||
detail: false,
|
|
||||||
}),
|
|
||||||
text: text,
|
text: text,
|
||||||
cw: note.cw,
|
cw: note.cw,
|
||||||
visibility: note.visibility,
|
visibility: note.visibility,
|
||||||
|
|
|
@ -62,7 +62,7 @@ export class NotificationEntityService implements OnModuleInit {
|
||||||
},
|
},
|
||||||
hint?: {
|
hint?: {
|
||||||
packedNotes: Map<MiNote['id'], Packed<'Note'>>;
|
packedNotes: Map<MiNote['id'], Packed<'Note'>>;
|
||||||
packedUsers: Map<MiUser['id'], Packed<'User'>>;
|
packedUsers: Map<MiUser['id'], Packed<'UserLite'>>;
|
||||||
},
|
},
|
||||||
): Promise<Packed<'Notification'>> {
|
): Promise<Packed<'Notification'>> {
|
||||||
const notification = src;
|
const notification = src;
|
||||||
|
@ -76,9 +76,7 @@ export class NotificationEntityService implements OnModuleInit {
|
||||||
const userIfNeed = 'notifierId' in notification ? (
|
const userIfNeed = 'notifierId' in notification ? (
|
||||||
hint?.packedUsers != null
|
hint?.packedUsers != null
|
||||||
? hint.packedUsers.get(notification.notifierId)
|
? hint.packedUsers.get(notification.notifierId)
|
||||||
: this.userEntityService.pack(notification.notifierId, { id: meId }, {
|
: this.userEntityService.pack(notification.notifierId, { id: meId })
|
||||||
detail: false,
|
|
||||||
})
|
|
||||||
) : undefined;
|
) : undefined;
|
||||||
const role = notification.type === 'roleAssigned' ? await this.roleEntityService.pack(notification.roleId) : undefined;
|
const role = notification.type === 'roleAssigned' ? await this.roleEntityService.pack(notification.roleId) : undefined;
|
||||||
|
|
||||||
|
@ -131,9 +129,7 @@ export class NotificationEntityService implements OnModuleInit {
|
||||||
const users = userIds.length > 0 ? await this.usersRepository.find({
|
const users = userIds.length > 0 ? await this.usersRepository.find({
|
||||||
where: { id: In(userIds) },
|
where: { id: In(userIds) },
|
||||||
}) : [];
|
}) : [];
|
||||||
const packedUsersArray = await this.userEntityService.packMany(users, { id: meId }, {
|
const packedUsersArray = await this.userEntityService.packMany(users, { id: meId });
|
||||||
detail: false,
|
|
||||||
});
|
|
||||||
const packedUsers = new Map(packedUsersArray.map(p => [p.id, p]));
|
const packedUsers = new Map(packedUsersArray.map(p => [p.id, p]));
|
||||||
|
|
||||||
// 既に解決されたフォローリクエストの通知を除外
|
// 既に解決されたフォローリクエストの通知を除外
|
||||||
|
@ -161,7 +157,7 @@ export class NotificationEntityService implements OnModuleInit {
|
||||||
},
|
},
|
||||||
hint?: {
|
hint?: {
|
||||||
packedNotes: Map<MiNote['id'], Packed<'Note'>>;
|
packedNotes: Map<MiNote['id'], Packed<'Note'>>;
|
||||||
packedUsers: Map<MiUser['id'], Packed<'User'>>;
|
packedUsers: Map<MiUser['id'], Packed<'UserLite'>>;
|
||||||
},
|
},
|
||||||
): Promise<Packed<'Notification'>> {
|
): Promise<Packed<'Notification'>> {
|
||||||
const notification = src;
|
const notification = src;
|
||||||
|
@ -175,18 +171,14 @@ export class NotificationEntityService implements OnModuleInit {
|
||||||
const userIfNeed = 'notifierId' in notification ? (
|
const userIfNeed = 'notifierId' in notification ? (
|
||||||
hint?.packedUsers != null
|
hint?.packedUsers != null
|
||||||
? hint.packedUsers.get(notification.notifierId)
|
? hint.packedUsers.get(notification.notifierId)
|
||||||
: this.userEntityService.pack(notification.notifierId, { id: meId }, {
|
: this.userEntityService.pack(notification.notifierId, { id: meId })
|
||||||
detail: false,
|
|
||||||
})
|
|
||||||
) : undefined;
|
) : undefined;
|
||||||
|
|
||||||
if (notification.type === 'reaction:grouped') {
|
if (notification.type === 'reaction:grouped') {
|
||||||
const reactions = await Promise.all(notification.reactions.map(async reaction => {
|
const reactions = await Promise.all(notification.reactions.map(async reaction => {
|
||||||
const user = hint?.packedUsers != null
|
const user = hint?.packedUsers != null
|
||||||
? hint.packedUsers.get(reaction.userId)!
|
? hint.packedUsers.get(reaction.userId)!
|
||||||
: await this.userEntityService.pack(reaction.userId, { id: meId }, {
|
: await this.userEntityService.pack(reaction.userId, { id: meId });
|
||||||
detail: false,
|
|
||||||
});
|
|
||||||
return {
|
return {
|
||||||
user,
|
user,
|
||||||
reaction: reaction.reaction,
|
reaction: reaction.reaction,
|
||||||
|
@ -206,9 +198,7 @@ export class NotificationEntityService implements OnModuleInit {
|
||||||
return packedUser;
|
return packedUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.userEntityService.pack(userId, { id: meId }, {
|
return this.userEntityService.pack(userId, { id: meId });
|
||||||
detail: false,
|
|
||||||
});
|
|
||||||
}));
|
}));
|
||||||
return await awaitAll({
|
return await awaitAll({
|
||||||
id: notification.id,
|
id: notification.id,
|
||||||
|
@ -275,9 +265,7 @@ export class NotificationEntityService implements OnModuleInit {
|
||||||
const users = userIds.length > 0 ? await this.usersRepository.find({
|
const users = userIds.length > 0 ? await this.usersRepository.find({
|
||||||
where: { id: In(userIds) },
|
where: { id: In(userIds) },
|
||||||
}) : [];
|
}) : [];
|
||||||
const packedUsersArray = await this.userEntityService.packMany(users, { id: meId }, {
|
const packedUsersArray = await this.userEntityService.packMany(users, { id: meId });
|
||||||
detail: false,
|
|
||||||
});
|
|
||||||
const packedUsers = new Map(packedUsersArray.map(p => [p.id, p]));
|
const packedUsers = new Map(packedUsersArray.map(p => [p.id, p]));
|
||||||
|
|
||||||
// 既に解決されたフォローリクエストの通知を除外
|
// 既に解決されたフォローリクエストの通知を除外
|
||||||
|
|
|
@ -90,7 +90,7 @@ export class PageEntityService {
|
||||||
createdAt: this.idService.parse(page.id).date.toISOString(),
|
createdAt: this.idService.parse(page.id).date.toISOString(),
|
||||||
updatedAt: page.updatedAt.toISOString(),
|
updatedAt: page.updatedAt.toISOString(),
|
||||||
userId: page.userId,
|
userId: page.userId,
|
||||||
user: this.userEntityService.pack(page.user ?? page.userId, me), // { detail: true } すると無限ループするので注意
|
user: this.userEntityService.pack(page.user ?? page.userId, me), // { schema: 'UserDetailed' } すると無限ループするので注意
|
||||||
content: page.content,
|
content: page.content,
|
||||||
variables: page.variables,
|
variables: page.variables,
|
||||||
title: page.title,
|
title: page.title,
|
||||||
|
|
|
@ -38,7 +38,7 @@ export class RenoteMutingEntityService {
|
||||||
createdAt: this.idService.parse(muting.id).date.toISOString(),
|
createdAt: this.idService.parse(muting.id).date.toISOString(),
|
||||||
muteeId: muting.muteeId,
|
muteeId: muting.muteeId,
|
||||||
mutee: this.userEntityService.pack(muting.muteeId, me, {
|
mutee: this.userEntityService.pack(muting.muteeId, me, {
|
||||||
detail: true,
|
schema: 'UserDetailedNotMe',
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,14 +30,6 @@ import type { NoteEntityService } from './NoteEntityService.js';
|
||||||
import type { DriveFileEntityService } from './DriveFileEntityService.js';
|
import type { DriveFileEntityService } from './DriveFileEntityService.js';
|
||||||
import type { PageEntityService } from './PageEntityService.js';
|
import type { PageEntityService } from './PageEntityService.js';
|
||||||
|
|
||||||
type IsUserDetailed<Detailed extends boolean> = Detailed extends true ? Packed<'UserDetailed'> : Packed<'UserLite'>;
|
|
||||||
type IsMeAndIsUserDetailed<ExpectsMe extends boolean | null, Detailed extends boolean> =
|
|
||||||
Detailed extends true ?
|
|
||||||
ExpectsMe extends true ? Packed<'MeDetailed'> :
|
|
||||||
ExpectsMe extends false ? Packed<'UserDetailedNotMe'> :
|
|
||||||
Packed<'UserDetailed'> :
|
|
||||||
Packed<'UserLite'>;
|
|
||||||
|
|
||||||
const Ajv = _Ajv.default;
|
const Ajv = _Ajv.default;
|
||||||
const ajv = new Ajv();
|
const ajv = new Ajv();
|
||||||
|
|
||||||
|
@ -303,33 +295,34 @@ export class UserEntityService implements OnModuleInit {
|
||||||
return `${this.config.url}/users/${userId}`;
|
return `${this.config.url}/users/${userId}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async pack<ExpectsMe extends boolean | null = null, D extends boolean = false>(
|
public async pack<S extends 'MeDetailed' | 'UserDetailedNotMe' | 'UserDetailed' | 'UserLite' = 'UserLite'>(
|
||||||
src: MiUser['id'] | MiUser,
|
src: MiUser['id'] | MiUser,
|
||||||
me?: { id: MiUser['id']; } | null | undefined,
|
me?: { id: MiUser['id']; } | null | undefined,
|
||||||
options?: {
|
options?: {
|
||||||
detail?: D,
|
schema?: S,
|
||||||
includeSecrets?: boolean,
|
includeSecrets?: boolean,
|
||||||
userProfile?: MiUserProfile,
|
userProfile?: MiUserProfile,
|
||||||
},
|
},
|
||||||
): Promise<IsMeAndIsUserDetailed<ExpectsMe, D>> {
|
): Promise<Packed<S>> {
|
||||||
const opts = Object.assign({
|
const opts = Object.assign({
|
||||||
detail: false,
|
schema: 'UserLite',
|
||||||
includeSecrets: false,
|
includeSecrets: false,
|
||||||
}, options);
|
}, options);
|
||||||
|
|
||||||
const user = typeof src === 'object' ? src : await this.usersRepository.findOneByOrFail({ id: src });
|
const user = typeof src === 'object' ? src : await this.usersRepository.findOneByOrFail({ id: src });
|
||||||
|
|
||||||
|
const isDetailed = opts.schema !== 'UserLite';
|
||||||
const meId = me ? me.id : null;
|
const meId = me ? me.id : null;
|
||||||
const isMe = meId === user.id;
|
const isMe = meId === user.id;
|
||||||
const iAmModerator = me ? await this.roleService.isModerator(me as MiUser) : false;
|
const iAmModerator = me ? await this.roleService.isModerator(me as MiUser) : false;
|
||||||
|
|
||||||
const relation = meId && !isMe && opts.detail ? await this.getRelation(meId, user.id) : null;
|
const relation = meId && !isMe && isDetailed ? await this.getRelation(meId, user.id) : null;
|
||||||
const pins = opts.detail ? await this.userNotePiningsRepository.createQueryBuilder('pin')
|
const pins = isDetailed ? await this.userNotePiningsRepository.createQueryBuilder('pin')
|
||||||
.where('pin.userId = :userId', { userId: user.id })
|
.where('pin.userId = :userId', { userId: user.id })
|
||||||
.innerJoinAndSelect('pin.note', 'note')
|
.innerJoinAndSelect('pin.note', 'note')
|
||||||
.orderBy('pin.id', 'DESC')
|
.orderBy('pin.id', 'DESC')
|
||||||
.getMany() : [];
|
.getMany() : [];
|
||||||
const profile = opts.detail ? (opts.userProfile ?? await this.userProfilesRepository.findOneByOrFail({ userId: user.id })) : null;
|
const profile = isDetailed ? (opts.userProfile ?? await this.userProfilesRepository.findOneByOrFail({ userId: user.id })) : null;
|
||||||
|
|
||||||
const followingCount = profile == null ? null :
|
const followingCount = profile == null ? null :
|
||||||
(profile.followingVisibility === 'public') || isMe ? user.followingCount :
|
(profile.followingVisibility === 'public') || isMe ? user.followingCount :
|
||||||
|
@ -341,15 +334,15 @@ export class UserEntityService implements OnModuleInit {
|
||||||
(profile.followersVisibility === 'followers') && (relation && relation.isFollowing) ? user.followersCount :
|
(profile.followersVisibility === 'followers') && (relation && relation.isFollowing) ? user.followersCount :
|
||||||
null;
|
null;
|
||||||
|
|
||||||
const isModerator = isMe && opts.detail ? this.roleService.isModerator(user) : null;
|
const isModerator = isMe && isDetailed ? this.roleService.isModerator(user) : null;
|
||||||
const isAdmin = isMe && opts.detail ? this.roleService.isAdministrator(user) : null;
|
const isAdmin = isMe && isDetailed ? this.roleService.isAdministrator(user) : null;
|
||||||
const unreadAnnouncements = isMe && opts.detail ?
|
const unreadAnnouncements = isMe && isDetailed ?
|
||||||
(await this.announcementService.getUnreadAnnouncements(user)).map((announcement) => ({
|
(await this.announcementService.getUnreadAnnouncements(user)).map((announcement) => ({
|
||||||
createdAt: this.idService.parse(announcement.id).date.toISOString(),
|
createdAt: this.idService.parse(announcement.id).date.toISOString(),
|
||||||
...announcement,
|
...announcement,
|
||||||
})) : null;
|
})) : null;
|
||||||
|
|
||||||
const notificationsInfo = isMe && opts.detail ? await this.getNotificationsInfo(user.id) : null;
|
const notificationsInfo = isMe && isDetailed ? await this.getNotificationsInfo(user.id) : null;
|
||||||
|
|
||||||
const packed = {
|
const packed = {
|
||||||
id: user.id,
|
id: user.id,
|
||||||
|
@ -386,7 +379,7 @@ export class UserEntityService implements OnModuleInit {
|
||||||
displayOrder: r.displayOrder,
|
displayOrder: r.displayOrder,
|
||||||
}))) : undefined,
|
}))) : undefined,
|
||||||
|
|
||||||
...(opts.detail ? {
|
...(isDetailed ? {
|
||||||
url: profile!.url,
|
url: profile!.url,
|
||||||
uri: user.uri,
|
uri: user.uri,
|
||||||
movedTo: user.movedToUri ? this.apPersonService.resolvePerson(user.movedToUri).then(user => user.id).catch(() => null) : null,
|
movedTo: user.movedToUri ? this.apPersonService.resolvePerson(user.movedToUri).then(user => user.id).catch(() => null) : null,
|
||||||
|
@ -444,7 +437,7 @@ export class UserEntityService implements OnModuleInit {
|
||||||
moderationNote: iAmModerator ? (profile!.moderationNote ?? '') : undefined,
|
moderationNote: iAmModerator ? (profile!.moderationNote ?? '') : undefined,
|
||||||
} : {}),
|
} : {}),
|
||||||
|
|
||||||
...(opts.detail && isMe ? {
|
...(isDetailed && isMe ? {
|
||||||
avatarId: user.avatarId,
|
avatarId: user.avatarId,
|
||||||
bannerId: user.bannerId,
|
bannerId: user.bannerId,
|
||||||
isModerator: isModerator,
|
isModerator: isModerator,
|
||||||
|
@ -516,19 +509,19 @@ export class UserEntityService implements OnModuleInit {
|
||||||
notify: relation.following?.notify ?? 'none',
|
notify: relation.following?.notify ?? 'none',
|
||||||
withReplies: relation.following?.withReplies ?? false,
|
withReplies: relation.following?.withReplies ?? false,
|
||||||
} : {}),
|
} : {}),
|
||||||
} as Promiseable<Packed<'User'>> as Promiseable<IsMeAndIsUserDetailed<ExpectsMe, D>>;
|
} as Promiseable<Packed<S>>;
|
||||||
|
|
||||||
return await awaitAll(packed);
|
return await awaitAll(packed);
|
||||||
}
|
}
|
||||||
|
|
||||||
public packMany<D extends boolean = false>(
|
public packMany<S extends 'MeDetailed' | 'UserDetailedNotMe' | 'UserDetailed' | 'UserLite' = 'UserLite'>(
|
||||||
users: (MiUser['id'] | MiUser)[],
|
users: (MiUser['id'] | MiUser)[],
|
||||||
me?: { id: MiUser['id'] } | null | undefined,
|
me?: { id: MiUser['id'] } | null | undefined,
|
||||||
options?: {
|
options?: {
|
||||||
detail?: D,
|
schema?: S,
|
||||||
includeSecrets?: boolean,
|
includeSecrets?: boolean,
|
||||||
},
|
},
|
||||||
): Promise<IsUserDetailed<D>[]> {
|
): Promise<Packed<S>[]> {
|
||||||
return Promise.all(users.map(u => this.pack(u, me, options)));
|
return Promise.all(users.map(u => this.pack(u, me, options)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
// structredCloneが遅いため
|
// structredCloneが遅いため
|
||||||
// SEE: http://var.blog.jp/archives/86038606.html
|
// SEE: http://var.blog.jp/archives/86038606.html
|
||||||
|
|
||||||
type Cloneable = string | number | boolean | null | { [key: string]: Cloneable } | Cloneable[];
|
type Cloneable = string | number | boolean | null | undefined | { [key: string]: Cloneable } | Cloneable[];
|
||||||
|
|
||||||
export function deepClone<T extends Cloneable>(x: T): T {
|
export function deepClone<T extends Cloneable>(x: T): T {
|
||||||
if (typeof x === 'object') {
|
if (typeof x === 'object') {
|
||||||
|
@ -14,7 +14,7 @@ export function deepClone<T extends Cloneable>(x: T): T {
|
||||||
if (Array.isArray(x)) return x.map(deepClone) as T;
|
if (Array.isArray(x)) return x.map(deepClone) as T;
|
||||||
const obj = {} as Record<string, Cloneable>;
|
const obj = {} as Record<string, Cloneable>;
|
||||||
for (const [k, v] of Object.entries(x)) {
|
for (const [k, v] of Object.entries(x)) {
|
||||||
obj[k] = deepClone(v);
|
obj[k] = v === undefined ? undefined : deepClone(v);
|
||||||
}
|
}
|
||||||
return obj as T;
|
return obj as T;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -25,7 +25,7 @@ import { packedBlockingSchema } from '@/models/json-schema/blocking.js';
|
||||||
import { packedNoteReactionSchema } from '@/models/json-schema/note-reaction.js';
|
import { packedNoteReactionSchema } from '@/models/json-schema/note-reaction.js';
|
||||||
import { packedHashtagSchema } from '@/models/json-schema/hashtag.js';
|
import { packedHashtagSchema } from '@/models/json-schema/hashtag.js';
|
||||||
import { packedInviteCodeSchema } from '@/models/json-schema/invite-code.js';
|
import { packedInviteCodeSchema } from '@/models/json-schema/invite-code.js';
|
||||||
import { packedPageSchema } from '@/models/json-schema/page.js';
|
import { packedPageSchema, packedPageBlockSchema } from '@/models/json-schema/page.js';
|
||||||
import { packedNoteFavoriteSchema } from '@/models/json-schema/note-favorite.js';
|
import { packedNoteFavoriteSchema } from '@/models/json-schema/note-favorite.js';
|
||||||
import { packedChannelSchema } from '@/models/json-schema/channel.js';
|
import { packedChannelSchema } from '@/models/json-schema/channel.js';
|
||||||
import { packedAntennaSchema } from '@/models/json-schema/antenna.js';
|
import { packedAntennaSchema } from '@/models/json-schema/antenna.js';
|
||||||
|
@ -37,7 +37,7 @@ import { packedEmojiDetailedSchema, packedEmojiRequestSimpleSchema, packedEmojiS
|
||||||
import { packedFlashSchema } from '@/models/json-schema/flash.js';
|
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, packedRolePoliciesSchema } from '@/models/json-schema/role.js';
|
||||||
import { packedAdSchema } from '@/models/json-schema/ad.js';
|
import { packedAdSchema } from '@/models/json-schema/ad.js';
|
||||||
import { packedReversiGameLiteSchema, packedReversiGameDetailedSchema } from '@/models/json-schema/reversi-game.js';
|
import { packedReversiGameLiteSchema, packedReversiGameDetailedSchema } from '@/models/json-schema/reversi-game.js';
|
||||||
|
|
||||||
|
@ -67,6 +67,7 @@ export const refs = {
|
||||||
Hashtag: packedHashtagSchema,
|
Hashtag: packedHashtagSchema,
|
||||||
InviteCode: packedInviteCodeSchema,
|
InviteCode: packedInviteCodeSchema,
|
||||||
Page: packedPageSchema,
|
Page: packedPageSchema,
|
||||||
|
PageBlock: packedPageBlockSchema,
|
||||||
Channel: packedChannelSchema,
|
Channel: packedChannelSchema,
|
||||||
QueueCount: packedQueueCountSchema,
|
QueueCount: packedQueueCountSchema,
|
||||||
Antenna: packedAntennaSchema,
|
Antenna: packedAntennaSchema,
|
||||||
|
@ -81,12 +82,16 @@ export const refs = {
|
||||||
Signin: packedSigninSchema,
|
Signin: packedSigninSchema,
|
||||||
RoleLite: packedRoleLiteSchema,
|
RoleLite: packedRoleLiteSchema,
|
||||||
Role: packedRoleSchema,
|
Role: packedRoleSchema,
|
||||||
|
RolePolicies: packedRolePoliciesSchema,
|
||||||
ReversiGameLite: packedReversiGameLiteSchema,
|
ReversiGameLite: packedReversiGameLiteSchema,
|
||||||
ReversiGameDetailed: packedReversiGameDetailedSchema,
|
ReversiGameDetailed: packedReversiGameDetailedSchema,
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Packed<x extends keyof typeof refs> = SchemaType<typeof refs[x]>;
|
export type Packed<x extends keyof typeof refs> = SchemaType<typeof refs[x]>;
|
||||||
|
|
||||||
|
export type KeyOf<x extends keyof typeof refs> = PropertiesToUnion<typeof refs[x]>;
|
||||||
|
type PropertiesToUnion<p extends Schema> = p['properties'] extends NonNullable<Obj> ? keyof p['properties'] : never;
|
||||||
|
|
||||||
type TypeStringef = 'null' | 'boolean' | 'integer' | 'number' | 'string' | 'array' | 'object' | 'any';
|
type TypeStringef = 'null' | 'boolean' | 'integer' | 'number' | 'string' | 'array' | 'object' | 'any';
|
||||||
type StringDefToType<T extends TypeStringef> =
|
type StringDefToType<T extends TypeStringef> =
|
||||||
T extends 'null' ? null :
|
T extends 'null' ? null :
|
||||||
|
|
|
@ -38,7 +38,7 @@ export class MiAnnouncement {
|
||||||
length: 256, nullable: false,
|
length: 256, nullable: false,
|
||||||
default: 'info',
|
default: 'info',
|
||||||
})
|
})
|
||||||
public icon: string;
|
public icon: 'info' | 'warning' | 'error' | 'success';
|
||||||
|
|
||||||
// normal ... お知らせページ掲載
|
// normal ... お知らせページ掲載
|
||||||
// banner ... お知らせページ掲載 + バナー表示
|
// banner ... お知らせページ掲載 + バナー表示
|
||||||
|
@ -47,7 +47,7 @@ export class MiAnnouncement {
|
||||||
length: 256, nullable: false,
|
length: 256, nullable: false,
|
||||||
default: 'normal',
|
default: 'normal',
|
||||||
})
|
})
|
||||||
public display: string;
|
public display: 'normal' | 'banner' | 'dialog';
|
||||||
|
|
||||||
@Column('boolean', {
|
@Column('boolean', {
|
||||||
default: false,
|
default: false,
|
||||||
|
|
|
@ -37,10 +37,12 @@ export const packedAnnouncementSchema = {
|
||||||
icon: {
|
icon: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
|
enum: ['info', 'warning', 'error', 'success'],
|
||||||
},
|
},
|
||||||
display: {
|
display: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
|
enum: ['dialog', 'normal', 'banner'],
|
||||||
},
|
},
|
||||||
needConfirmationToRead: {
|
needConfirmationToRead: {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
|
|
|
@ -25,7 +25,7 @@ export const packedBlockingSchema = {
|
||||||
blockee: {
|
blockee: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
ref: 'UserDetailed',
|
ref: 'UserDetailedNotMe',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
|
@ -30,12 +30,12 @@ export const packedFollowingSchema = {
|
||||||
followee: {
|
followee: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
optional: true, nullable: false,
|
optional: true, nullable: false,
|
||||||
ref: 'UserDetailed',
|
ref: 'UserDetailedNotMe',
|
||||||
},
|
},
|
||||||
follower: {
|
follower: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
optional: true, nullable: false,
|
optional: true, nullable: false,
|
||||||
ref: 'UserDetailed',
|
ref: 'UserDetailedNotMe',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
|
@ -30,7 +30,7 @@ export const packedMutingSchema = {
|
||||||
mutee: {
|
mutee: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
ref: 'UserDetailed',
|
ref: 'UserDetailedNotMe',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
|
@ -74,6 +74,7 @@ export const packedNoteSchema = {
|
||||||
visibility: {
|
visibility: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
|
enum: ['public', 'home', 'followers', 'specified'],
|
||||||
},
|
},
|
||||||
mentions: {
|
mentions: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
|
@ -122,6 +123,48 @@ export const packedNoteSchema = {
|
||||||
poll: {
|
poll: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
optional: true, nullable: true,
|
optional: true, nullable: true,
|
||||||
|
properties: {
|
||||||
|
expiresAt: {
|
||||||
|
type: 'string',
|
||||||
|
optional: true, nullable: true,
|
||||||
|
format: 'date-time',
|
||||||
|
},
|
||||||
|
multiple: {
|
||||||
|
type: 'boolean',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
choices: {
|
||||||
|
type: 'array',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
properties: {
|
||||||
|
isVoted: {
|
||||||
|
type: 'boolean',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
votes: {
|
||||||
|
type: 'number',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
emojis: {
|
||||||
|
type: 'object',
|
||||||
|
optional: true, nullable: false,
|
||||||
|
additionalProperties: {
|
||||||
|
anyOf: [{
|
||||||
|
type: 'string',
|
||||||
|
}],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
channelId: {
|
channelId: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
@ -167,9 +210,23 @@ export const packedNoteSchema = {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
optional: false, nullable: true,
|
optional: false, nullable: true,
|
||||||
},
|
},
|
||||||
|
reactionEmojis: {
|
||||||
|
type: 'object',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
additionalProperties: {
|
||||||
|
anyOf: [{
|
||||||
|
type: 'string',
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
},
|
||||||
reactions: {
|
reactions: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
|
additionalProperties: {
|
||||||
|
anyOf: [{
|
||||||
|
type: 'number',
|
||||||
|
}],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
renoteCount: {
|
renoteCount: {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
|
@ -201,7 +258,7 @@ export const packedNoteSchema = {
|
||||||
},
|
},
|
||||||
|
|
||||||
myReaction: {
|
myReaction: {
|
||||||
type: 'object',
|
type: 'string',
|
||||||
optional: true, nullable: true,
|
optional: true, nullable: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
import { notificationTypes } from '@/types.js';
|
import { notificationTypes } from '@/types.js';
|
||||||
|
|
||||||
export const packedNotificationSchema = {
|
const baseSchema = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
id: {
|
id: {
|
||||||
|
@ -23,68 +23,368 @@ export const packedNotificationSchema = {
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
enum: [...notificationTypes, 'reaction:grouped', 'renote:grouped'],
|
enum: [...notificationTypes, 'reaction:grouped', 'renote:grouped'],
|
||||||
},
|
},
|
||||||
user: {
|
},
|
||||||
type: 'object',
|
} as const;
|
||||||
ref: 'UserLite',
|
|
||||||
optional: true, nullable: true,
|
export const packedNotificationSchema = {
|
||||||
},
|
type: 'object',
|
||||||
userId: {
|
oneOf: [{
|
||||||
type: 'string',
|
type: 'object',
|
||||||
optional: true, nullable: true,
|
properties: {
|
||||||
format: 'id',
|
...baseSchema.properties,
|
||||||
},
|
type: {
|
||||||
note: {
|
type: 'string',
|
||||||
type: 'object',
|
optional: false, nullable: false,
|
||||||
ref: 'Note',
|
enum: ['note'],
|
||||||
optional: true, nullable: true,
|
|
||||||
},
|
|
||||||
reaction: {
|
|
||||||
type: 'string',
|
|
||||||
optional: true, nullable: true,
|
|
||||||
},
|
|
||||||
achievement: {
|
|
||||||
type: 'string',
|
|
||||||
optional: true, nullable: false,
|
|
||||||
},
|
|
||||||
body: {
|
|
||||||
type: 'string',
|
|
||||||
optional: true, nullable: true,
|
|
||||||
},
|
|
||||||
header: {
|
|
||||||
type: 'string',
|
|
||||||
optional: true, nullable: true,
|
|
||||||
},
|
|
||||||
icon: {
|
|
||||||
type: 'string',
|
|
||||||
optional: true, nullable: true,
|
|
||||||
},
|
|
||||||
reactions: {
|
|
||||||
type: 'array',
|
|
||||||
optional: true, nullable: true,
|
|
||||||
items: {
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
user: {
|
|
||||||
type: 'object',
|
|
||||||
ref: 'UserLite',
|
|
||||||
optional: false, nullable: false,
|
|
||||||
},
|
|
||||||
reaction: {
|
|
||||||
type: 'string',
|
|
||||||
optional: false, nullable: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
required: ['user', 'reaction'],
|
|
||||||
},
|
},
|
||||||
},
|
user: {
|
||||||
users: {
|
|
||||||
type: 'array',
|
|
||||||
optional: true, nullable: true,
|
|
||||||
items: {
|
|
||||||
type: 'object',
|
type: 'object',
|
||||||
ref: 'UserLite',
|
ref: 'UserLite',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
},
|
},
|
||||||
|
userId: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
format: 'id',
|
||||||
|
},
|
||||||
|
note: {
|
||||||
|
type: 'object',
|
||||||
|
ref: 'Note',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
}, {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
...baseSchema.properties,
|
||||||
|
type: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
enum: ['mention'],
|
||||||
|
},
|
||||||
|
user: {
|
||||||
|
type: 'object',
|
||||||
|
ref: 'UserLite',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
userId: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
format: 'id',
|
||||||
|
},
|
||||||
|
note: {
|
||||||
|
type: 'object',
|
||||||
|
ref: 'Note',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
...baseSchema.properties,
|
||||||
|
type: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
enum: ['reply'],
|
||||||
|
},
|
||||||
|
user: {
|
||||||
|
type: 'object',
|
||||||
|
ref: 'UserLite',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
userId: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
format: 'id',
|
||||||
|
},
|
||||||
|
note: {
|
||||||
|
type: 'object',
|
||||||
|
ref: 'Note',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
...baseSchema.properties,
|
||||||
|
type: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
enum: ['renote'],
|
||||||
|
},
|
||||||
|
user: {
|
||||||
|
type: 'object',
|
||||||
|
ref: 'UserLite',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
userId: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
format: 'id',
|
||||||
|
},
|
||||||
|
note: {
|
||||||
|
type: 'object',
|
||||||
|
ref: 'Note',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
...baseSchema.properties,
|
||||||
|
type: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
enum: ['quote'],
|
||||||
|
},
|
||||||
|
user: {
|
||||||
|
type: 'object',
|
||||||
|
ref: 'UserLite',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
userId: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
format: 'id',
|
||||||
|
},
|
||||||
|
note: {
|
||||||
|
type: 'object',
|
||||||
|
ref: 'Note',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
...baseSchema.properties,
|
||||||
|
type: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
enum: ['reaction'],
|
||||||
|
},
|
||||||
|
user: {
|
||||||
|
type: 'object',
|
||||||
|
ref: 'UserLite',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
userId: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
format: 'id',
|
||||||
|
},
|
||||||
|
note: {
|
||||||
|
type: 'object',
|
||||||
|
ref: 'Note',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
reaction: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
...baseSchema.properties,
|
||||||
|
type: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
enum: ['pollEnded'],
|
||||||
|
},
|
||||||
|
user: {
|
||||||
|
type: 'object',
|
||||||
|
ref: 'UserLite',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
userId: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
format: 'id',
|
||||||
|
},
|
||||||
|
note: {
|
||||||
|
type: 'object',
|
||||||
|
ref: 'Note',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
...baseSchema.properties,
|
||||||
|
type: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
enum: ['follow'],
|
||||||
|
},
|
||||||
|
user: {
|
||||||
|
type: 'object',
|
||||||
|
ref: 'UserLite',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
userId: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
format: 'id',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
...baseSchema.properties,
|
||||||
|
type: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
enum: ['receiveFollowRequest'],
|
||||||
|
},
|
||||||
|
user: {
|
||||||
|
type: 'object',
|
||||||
|
ref: 'UserLite',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
userId: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
format: 'id',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
...baseSchema.properties,
|
||||||
|
type: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
enum: ['followRequestAccepted'],
|
||||||
|
},
|
||||||
|
user: {
|
||||||
|
type: 'object',
|
||||||
|
ref: 'UserLite',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
userId: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
format: 'id',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
...baseSchema.properties,
|
||||||
|
type: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
enum: ['roleAssigned'],
|
||||||
|
},
|
||||||
|
role: {
|
||||||
|
type: 'object',
|
||||||
|
ref: 'Role',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
...baseSchema.properties,
|
||||||
|
type: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
enum: ['achievementEarned'],
|
||||||
|
},
|
||||||
|
achievement: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
...baseSchema.properties,
|
||||||
|
type: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
enum: ['app'],
|
||||||
|
},
|
||||||
|
body: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
header: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
icon: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
...baseSchema.properties,
|
||||||
|
type: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
enum: ['reaction:grouped'],
|
||||||
|
},
|
||||||
|
note: {
|
||||||
|
type: 'object',
|
||||||
|
ref: 'Note',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
reactions: {
|
||||||
|
type: 'array',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
user: {
|
||||||
|
type: 'object',
|
||||||
|
ref: 'UserLite',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
reaction: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
required: ['user', 'reaction'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
...baseSchema.properties,
|
||||||
|
type: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
enum: ['renote:grouped'],
|
||||||
|
},
|
||||||
|
note: {
|
||||||
|
type: 'object',
|
||||||
|
ref: 'Note',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
users: {
|
||||||
|
type: 'array',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
ref: 'UserLite',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
...baseSchema.properties,
|
||||||
|
type: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
enum: ['test'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
|
@ -3,6 +3,107 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
const blockBaseSchema = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
id: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
const textBlockSchema = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
...blockBaseSchema.properties,
|
||||||
|
type: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
enum: ['text'],
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
const sectionBlockSchema = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
...blockBaseSchema.properties,
|
||||||
|
type: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
enum: ['section'],
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
children: {
|
||||||
|
type: 'array',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
ref: 'PageBlock',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
const imageBlockSchema = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
...blockBaseSchema.properties,
|
||||||
|
type: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
enum: ['image'],
|
||||||
|
},
|
||||||
|
fileId: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
const noteBlockSchema = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
...blockBaseSchema.properties,
|
||||||
|
type: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
enum: ['note'],
|
||||||
|
},
|
||||||
|
detailed: {
|
||||||
|
type: 'boolean',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
note: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const packedPageBlockSchema = {
|
||||||
|
type: 'object',
|
||||||
|
oneOf: [
|
||||||
|
textBlockSchema,
|
||||||
|
sectionBlockSchema,
|
||||||
|
imageBlockSchema,
|
||||||
|
noteBlockSchema,
|
||||||
|
],
|
||||||
|
} as const;
|
||||||
|
|
||||||
export const packedPageSchema = {
|
export const packedPageSchema = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
|
@ -38,6 +139,7 @@ export const packedPageSchema = {
|
||||||
items: {
|
items: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
|
ref: 'PageBlock',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
variables: {
|
variables: {
|
||||||
|
|
|
@ -25,7 +25,7 @@ export const packedRenoteMutingSchema = {
|
||||||
mutee: {
|
mutee: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
ref: 'UserDetailed',
|
ref: 'UserDetailedNotMe',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
|
@ -1,26 +1,103 @@
|
||||||
const rolePolicyValue = {
|
export const packedRolePoliciesSchema = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
|
optional: false, nullable: false,
|
||||||
properties: {
|
properties: {
|
||||||
value: {
|
gtlAvailable: {
|
||||||
oneOf: [
|
type: 'boolean',
|
||||||
{
|
optional: false, nullable: false,
|
||||||
type: 'integer',
|
|
||||||
optional: false, nullable: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'boolean',
|
|
||||||
optional: false, nullable: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
priority: {
|
ltlAvailable: {
|
||||||
|
type: 'boolean',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
canPublicNote: {
|
||||||
|
type: 'boolean',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
canInvite: {
|
||||||
|
type: 'boolean',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
inviteLimit: {
|
||||||
type: 'integer',
|
type: 'integer',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
},
|
},
|
||||||
useDefault: {
|
inviteLimitCycle: {
|
||||||
|
type: 'integer',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
inviteExpirationTime: {
|
||||||
|
type: 'integer',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
canManageCustomEmojis: {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
},
|
},
|
||||||
|
canManageAvatarDecorations: {
|
||||||
|
type: 'boolean',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
canSearchNotes: {
|
||||||
|
type: 'boolean',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
canUseTranslator: {
|
||||||
|
type: 'boolean',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
canHideAds: {
|
||||||
|
type: 'boolean',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
driveCapacityMb: {
|
||||||
|
type: 'integer',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
alwaysMarkNsfw: {
|
||||||
|
type: 'boolean',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
pinLimit: {
|
||||||
|
type: 'integer',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
antennaLimit: {
|
||||||
|
type: 'integer',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
wordMuteLimit: {
|
||||||
|
type: 'integer',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
webhookLimit: {
|
||||||
|
type: 'integer',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
clipLimit: {
|
||||||
|
type: 'integer',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
noteEachClipsLimit: {
|
||||||
|
type: 'integer',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
userListLimit: {
|
||||||
|
type: 'integer',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
userEachUserListsLimit: {
|
||||||
|
type: 'integer',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
rateLimitFactor: {
|
||||||
|
type: 'integer',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
avatarDecorationLimit: {
|
||||||
|
type: 'integer',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
@ -121,31 +198,28 @@ export const packedRoleSchema = {
|
||||||
policies: {
|
policies: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
properties: {
|
additionalProperties: {
|
||||||
pinLimit: rolePolicyValue,
|
anyOf: [{
|
||||||
canInvite: rolePolicyValue,
|
type: 'object',
|
||||||
clipLimit: rolePolicyValue,
|
properties: {
|
||||||
canHideAds: rolePolicyValue,
|
value: {
|
||||||
inviteLimit: rolePolicyValue,
|
oneOf: [
|
||||||
antennaLimit: rolePolicyValue,
|
{
|
||||||
gtlAvailable: rolePolicyValue,
|
type: 'integer',
|
||||||
ltlAvailable: rolePolicyValue,
|
},
|
||||||
webhookLimit: rolePolicyValue,
|
{
|
||||||
canPublicNote: rolePolicyValue,
|
type: 'boolean',
|
||||||
userListLimit: rolePolicyValue,
|
},
|
||||||
wordMuteLimit: rolePolicyValue,
|
],
|
||||||
alwaysMarkNsfw: rolePolicyValue,
|
},
|
||||||
canSearchNotes: rolePolicyValue,
|
priority: {
|
||||||
driveCapacityMb: rolePolicyValue,
|
type: 'integer',
|
||||||
rateLimitFactor: rolePolicyValue,
|
},
|
||||||
inviteLimitCycle: rolePolicyValue,
|
useDefault: {
|
||||||
noteEachClipsLimit: rolePolicyValue,
|
type: 'boolean',
|
||||||
inviteExpirationTime: rolePolicyValue,
|
},
|
||||||
canManageCustomEmojis: rolePolicyValue,
|
},
|
||||||
userEachUserListsLimit: rolePolicyValue,
|
}],
|
||||||
canManageAvatarDecorations: rolePolicyValue,
|
|
||||||
canUseTranslator: rolePolicyValue,
|
|
||||||
avatarDecorationLimit: rolePolicyValue,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
usersCount: {
|
usersCount: {
|
||||||
|
|
|
@ -599,104 +599,7 @@ export const packedMeDetailedOnlySchema = {
|
||||||
policies: {
|
policies: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
nullable: false, optional: false,
|
nullable: false, optional: false,
|
||||||
properties: {
|
ref: 'RolePolicies',
|
||||||
gtlAvailable: {
|
|
||||||
type: 'boolean',
|
|
||||||
nullable: false, optional: false,
|
|
||||||
},
|
|
||||||
ltlAvailable: {
|
|
||||||
type: 'boolean',
|
|
||||||
nullable: false, optional: false,
|
|
||||||
},
|
|
||||||
canPublicNote: {
|
|
||||||
type: 'boolean',
|
|
||||||
nullable: false, optional: false,
|
|
||||||
},
|
|
||||||
canInvite: {
|
|
||||||
type: 'boolean',
|
|
||||||
nullable: false, optional: false,
|
|
||||||
},
|
|
||||||
inviteLimit: {
|
|
||||||
type: 'number',
|
|
||||||
nullable: false, optional: false,
|
|
||||||
},
|
|
||||||
inviteLimitCycle: {
|
|
||||||
type: 'number',
|
|
||||||
nullable: false, optional: false,
|
|
||||||
},
|
|
||||||
inviteExpirationTime: {
|
|
||||||
type: 'number',
|
|
||||||
nullable: false, optional: false,
|
|
||||||
},
|
|
||||||
canManageCustomEmojis: {
|
|
||||||
type: 'boolean',
|
|
||||||
nullable: false, optional: false,
|
|
||||||
},
|
|
||||||
canManageAvatarDecorations: {
|
|
||||||
type: 'boolean',
|
|
||||||
nullable: false, optional: false,
|
|
||||||
},
|
|
||||||
canSearchNotes: {
|
|
||||||
type: 'boolean',
|
|
||||||
nullable: false, optional: false,
|
|
||||||
},
|
|
||||||
canUseTranslator: {
|
|
||||||
type: 'boolean',
|
|
||||||
nullable: false, optional: false,
|
|
||||||
},
|
|
||||||
canHideAds: {
|
|
||||||
type: 'boolean',
|
|
||||||
nullable: false, optional: false,
|
|
||||||
},
|
|
||||||
driveCapacityMb: {
|
|
||||||
type: 'number',
|
|
||||||
nullable: false, optional: false,
|
|
||||||
},
|
|
||||||
alwaysMarkNsfw: {
|
|
||||||
type: 'boolean',
|
|
||||||
nullable: false, optional: false,
|
|
||||||
},
|
|
||||||
pinLimit: {
|
|
||||||
type: 'number',
|
|
||||||
nullable: false, optional: false,
|
|
||||||
},
|
|
||||||
antennaLimit: {
|
|
||||||
type: 'number',
|
|
||||||
nullable: false, optional: false,
|
|
||||||
},
|
|
||||||
wordMuteLimit: {
|
|
||||||
type: 'number',
|
|
||||||
nullable: false, optional: false,
|
|
||||||
},
|
|
||||||
webhookLimit: {
|
|
||||||
type: 'number',
|
|
||||||
nullable: false, optional: false,
|
|
||||||
},
|
|
||||||
clipLimit: {
|
|
||||||
type: 'number',
|
|
||||||
nullable: false, optional: false,
|
|
||||||
},
|
|
||||||
noteEachClipsLimit: {
|
|
||||||
type: 'number',
|
|
||||||
nullable: false, optional: false,
|
|
||||||
},
|
|
||||||
userListLimit: {
|
|
||||||
type: 'number',
|
|
||||||
nullable: false, optional: false,
|
|
||||||
},
|
|
||||||
userEachUserListsLimit: {
|
|
||||||
type: 'number',
|
|
||||||
nullable: false, optional: false,
|
|
||||||
},
|
|
||||||
rateLimitFactor: {
|
|
||||||
type: 'number',
|
|
||||||
nullable: false, optional: false,
|
|
||||||
},
|
|
||||||
avatarDecorationLimit: {
|
|
||||||
type: 'number',
|
|
||||||
nullable: false, optional: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
//#region secrets
|
//#region secrets
|
||||||
email: {
|
email: {
|
||||||
|
@ -791,13 +694,5 @@ export const packedUserSchema = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
ref: 'UserDetailed',
|
ref: 'UserDetailed',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
type: 'object',
|
|
||||||
ref: 'UserDetailedNotMe',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'object',
|
|
||||||
ref: 'MeDetailed',
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
|
@ -204,7 +204,7 @@ export class ServerService implements OnApplicationShutdown {
|
||||||
});
|
});
|
||||||
|
|
||||||
this.globalEventService.publishMainStream(profile.userId, 'meUpdated', await this.userEntityService.pack(profile.userId, { id: profile.userId }, {
|
this.globalEventService.publishMainStream(profile.userId, 'meUpdated', await this.userEntityService.pack(profile.userId, { id: profile.userId }, {
|
||||||
detail: true,
|
schema: 'MeDetailed',
|
||||||
includeSecrets: true,
|
includeSecrets: true,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
|
@ -157,7 +157,7 @@ export class ApiServerService {
|
||||||
return {
|
return {
|
||||||
ok: true,
|
ok: true,
|
||||||
token: token.token,
|
token: token.token,
|
||||||
user: await this.userEntityService.pack(token.userId, null, { detail: true }),
|
user: await this.userEntityService.pack(token.userId, null, { schema: 'UserDetailedNotMe' }),
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -213,7 +213,7 @@ export class SignupApiService {
|
||||||
});
|
});
|
||||||
|
|
||||||
const res = await this.userEntityService.pack(account, account, {
|
const res = await this.userEntityService.pack(account, account, {
|
||||||
detail: true,
|
schema: 'MeDetailed',
|
||||||
includeSecrets: true,
|
includeSecrets: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { permissions } from 'misskey-js';
|
import { permissions } from 'misskey-js';
|
||||||
import type { Schema } from '@/misc/json-schema.js';
|
import type { KeyOf,Schema } from '@/misc/json-schema.js';
|
||||||
import { RolePolicies } from '@/core/RoleService.js';
|
import { RolePolicies } from '@/core/RoleService.js';
|
||||||
import * as ep___admin_emoji_setlocalOnlyBulk from './endpoints/admin/emoji/set-localonly-bulk.js';
|
import * as ep___admin_emoji_setlocalOnlyBulk from './endpoints/admin/emoji/set-localonly-bulk.js';
|
||||||
import * as ep___admin_emoji_setisSensitiveBulk from './endpoints/admin/emoji/set-issensitive-bulk.js';
|
import * as ep___admin_emoji_setisSensitiveBulk from './endpoints/admin/emoji/set-issensitive-bulk.js';
|
||||||
|
@ -799,7 +799,7 @@ interface IEndpointMetaBase {
|
||||||
*/
|
*/
|
||||||
readonly requireAdmin?: boolean;
|
readonly requireAdmin?: boolean;
|
||||||
|
|
||||||
readonly requireRolePolicy?: keyof RolePolicies;
|
readonly requireRolePolicy?: KeyOf<'RolePolicies'>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 引っ越し済みのユーザーによるリクエストを禁止するか
|
* 引っ越し済みのユーザーによるリクエストを禁止するか
|
||||||
|
|
|
@ -62,17 +62,17 @@ export const meta = {
|
||||||
reporter: {
|
reporter: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
nullable: false, optional: false,
|
nullable: false, optional: false,
|
||||||
ref: 'User',
|
ref: 'UserDetailedNotMe',
|
||||||
},
|
},
|
||||||
targetUser: {
|
targetUser: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
nullable: false, optional: false,
|
nullable: false, optional: false,
|
||||||
ref: 'User',
|
ref: 'UserDetailedNotMe',
|
||||||
},
|
},
|
||||||
assignee: {
|
assignee: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
nullable: true, optional: true,
|
nullable: true, optional: true,
|
||||||
ref: 'User',
|
ref: 'UserDetailedNotMe',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -11,6 +11,7 @@ import { SignupService } from '@/core/SignupService.js';
|
||||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||||
import { localUsernameSchema, passwordSchema } from '@/models/User.js';
|
import { localUsernameSchema, passwordSchema } from '@/models/User.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
|
import { Packed } from '@/misc/json-schema.js';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ['admin'],
|
tags: ['admin'],
|
||||||
|
@ -18,7 +19,7 @@ export const meta = {
|
||||||
res: {
|
res: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
ref: 'User',
|
ref: 'MeDetailed',
|
||||||
properties: {
|
properties: {
|
||||||
token: {
|
token: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
@ -60,11 +61,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
});
|
});
|
||||||
|
|
||||||
const res = await this.userEntityService.pack(account, account, {
|
const res = await this.userEntityService.pack(account, account, {
|
||||||
detail: true,
|
schema: 'MeDetailed',
|
||||||
includeSecrets: true,
|
includeSecrets: true,
|
||||||
});
|
}) as Packed<'MeDetailed'> & { token: string };
|
||||||
|
|
||||||
(res as any).token = secret;
|
res.token = secret;
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
});
|
});
|
||||||
|
|
|
@ -27,7 +27,7 @@ export const meta = {
|
||||||
res: {
|
res: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
ref: 'User',
|
ref: 'UserDetailedNotMe',
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
}
|
}
|
||||||
|
|
||||||
const res = await this.userEntityService.pack(profile.user!, null, {
|
const res = await this.userEntityService.pack(profile.user!, null, {
|
||||||
detail: true,
|
schema: 'UserDetailedNotMe',
|
||||||
});
|
});
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
|
|
|
@ -40,7 +40,7 @@ export const meta = {
|
||||||
},
|
},
|
||||||
required: ['id', 'createdAt', 'user'],
|
required: ['id', 'createdAt', 'user'],
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
@ -92,7 +92,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
return await Promise.all(assigns.map(async assign => ({
|
return await Promise.all(assigns.map(async assign => ({
|
||||||
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, { schema: 'UserDetailed' }),
|
||||||
expiresAt: assign.expiresAt?.toISOString() ?? null,
|
expiresAt: assign.expiresAt?.toISOString() ?? null,
|
||||||
})));
|
})));
|
||||||
});
|
});
|
||||||
|
|
|
@ -50,7 +50,7 @@ export const meta = {
|
||||||
user: {
|
user: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
ref: 'UserDetailed',
|
ref: 'UserDetailedNotMe',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -114,7 +114,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
|
|
||||||
const users = await query.getMany();
|
const users = await query.getMany();
|
||||||
|
|
||||||
return await this.userEntityService.packMany(users, me, { detail: true });
|
return await this.userEntityService.packMany(users, me, { schema: 'UserDetailed' });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -148,7 +148,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
return {
|
return {
|
||||||
type: 'User',
|
type: 'User',
|
||||||
object: await this.userEntityService.pack(user, me, { detail: true }),
|
object: await this.userEntityService.pack(user, me, { schema: 'UserDetailedNotMe' }),
|
||||||
};
|
};
|
||||||
} else if (note != null) {
|
} else if (note != null) {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -112,7 +112,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
return {
|
return {
|
||||||
accessToken: accessToken.token,
|
accessToken: accessToken.token,
|
||||||
user: await this.userEntityService.pack(session.userId, null, {
|
user: await this.userEntityService.pack(session.userId, null, {
|
||||||
detail: true,
|
schema: 'UserDetailedNotMe',
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -102,7 +102,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
await this.userBlockingService.block(blocker, blockee);
|
await this.userBlockingService.block(blocker, blockee);
|
||||||
|
|
||||||
return await this.userEntityService.pack(blockee.id, blocker, {
|
return await this.userEntityService.pack(blockee.id, blocker, {
|
||||||
detail: true,
|
schema: 'UserDetailedNotMe',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -103,7 +103,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
await this.userBlockingService.unblock(blocker, blockee);
|
await this.userBlockingService.unblock(blocker, blockee);
|
||||||
|
|
||||||
return await this.userEntityService.pack(blockee.id, blocker, {
|
return await this.userEntityService.pack(blockee.id, blocker, {
|
||||||
detail: true,
|
schema: 'UserDetailedNotMe',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,7 +61,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
relations: ['user'],
|
relations: ['user'],
|
||||||
});
|
});
|
||||||
|
|
||||||
const users = await this.userEntityService.packMany(records.map(r => r.user!), null, { detail: false });
|
const users = await this.userEntityService.packMany(records.map(r => r.user!), null);
|
||||||
|
|
||||||
return records.map(r => ({
|
return records.map(r => ({
|
||||||
id: r.id,
|
id: r.id,
|
||||||
|
|
|
@ -54,7 +54,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
.limit(ps.limit)
|
.limit(ps.limit)
|
||||||
.getMany();
|
.getMany();
|
||||||
|
|
||||||
return await this.userEntityService.packMany(users, me, { detail: true });
|
return await this.userEntityService.packMany(users, me, { schema: 'UserDetailedNotMe' });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,7 +76,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
|
|
||||||
const users = await query.limit(ps.limit).getMany();
|
const users = await query.limit(ps.limit).getMany();
|
||||||
|
|
||||||
return await this.userEntityService.packMany(users, me, { detail: true });
|
return await this.userEntityService.packMany(users, me, { schema: 'UserDetailed' });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,8 +71,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
userProfile.loggedInDates = [...userProfile.loggedInDates, today];
|
userProfile.loggedInDates = [...userProfile.loggedInDates, today];
|
||||||
}
|
}
|
||||||
|
|
||||||
return await this.userEntityService.pack<true, true>(userProfile.user!, userProfile.user!, {
|
return await this.userEntityService.pack(userProfile.user!, userProfile.user!, {
|
||||||
detail: true,
|
schema: 'MeDetailed',
|
||||||
includeSecrets: isSecure,
|
includeSecrets: isSecure,
|
||||||
userProfile,
|
userProfile,
|
||||||
});
|
});
|
||||||
|
|
|
@ -64,7 +64,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
|
|
||||||
// Publish meUpdated event
|
// Publish meUpdated event
|
||||||
this.globalEventService.publishMainStream(me.id, 'meUpdated', await this.userEntityService.pack(me.id, me, {
|
this.globalEventService.publishMainStream(me.id, 'meUpdated', await this.userEntityService.pack(me.id, me, {
|
||||||
detail: true,
|
schema: 'MeDetailed',
|
||||||
includeSecrets: true,
|
includeSecrets: true,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
|
@ -111,7 +111,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
|
|
||||||
// Publish meUpdated event
|
// Publish meUpdated event
|
||||||
this.globalEventService.publishMainStream(me.id, 'meUpdated', await this.userEntityService.pack(me.id, me, {
|
this.globalEventService.publishMainStream(me.id, 'meUpdated', await this.userEntityService.pack(me.id, me, {
|
||||||
detail: true,
|
schema: 'MeDetailed',
|
||||||
includeSecrets: true,
|
includeSecrets: true,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
|
@ -74,7 +74,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
|
|
||||||
// Publish meUpdated event
|
// Publish meUpdated event
|
||||||
this.globalEventService.publishMainStream(me.id, 'meUpdated', await this.userEntityService.pack(me.id, me, {
|
this.globalEventService.publishMainStream(me.id, 'meUpdated', await this.userEntityService.pack(me.id, me, {
|
||||||
detail: true,
|
schema: 'MeDetailed',
|
||||||
includeSecrets: true,
|
includeSecrets: true,
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
|
@ -97,7 +97,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
|
|
||||||
// Publish meUpdated event
|
// Publish meUpdated event
|
||||||
this.globalEventService.publishMainStream(me.id, 'meUpdated', await this.userEntityService.pack(me.id, me, {
|
this.globalEventService.publishMainStream(me.id, 'meUpdated', await this.userEntityService.pack(me.id, me, {
|
||||||
detail: true,
|
schema: 'MeDetailed',
|
||||||
includeSecrets: true,
|
includeSecrets: true,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
|
@ -76,7 +76,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
|
|
||||||
// Publish meUpdated event
|
// Publish meUpdated event
|
||||||
this.globalEventService.publishMainStream(me.id, 'meUpdated', await this.userEntityService.pack(me.id, me, {
|
this.globalEventService.publishMainStream(me.id, 'meUpdated', await this.userEntityService.pack(me.id, me, {
|
||||||
detail: true,
|
schema: 'MeDetailed',
|
||||||
includeSecrets: true,
|
includeSecrets: true,
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
|
@ -69,7 +69,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
|
|
||||||
// Publish meUpdated event
|
// Publish meUpdated event
|
||||||
this.globalEventService.publishMainStream(me.id, 'meUpdated', await this.userEntityService.pack(me.id, me, {
|
this.globalEventService.publishMainStream(me.id, 'meUpdated', await this.userEntityService.pack(me.id, me, {
|
||||||
detail: true,
|
schema: 'MeDetailed',
|
||||||
includeSecrets: true,
|
includeSecrets: true,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
|
@ -66,8 +66,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
throw err;
|
throw err;
|
||||||
});
|
});
|
||||||
|
|
||||||
return await this.userEntityService.pack<true, true>(me.id, me, {
|
return await this.userEntityService.pack(me.id, me, {
|
||||||
detail: true,
|
schema: 'MeDetailed',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,8 +51,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
throw err;
|
throw err;
|
||||||
});
|
});
|
||||||
|
|
||||||
return await this.userEntityService.pack<true, true>(me.id, me, {
|
return await this.userEntityService.pack(me.id, me, {
|
||||||
detail: true,
|
schema: 'MeDetailed',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ export const meta = {
|
||||||
|
|
||||||
res: {
|
res: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
ref: 'UserDetailed',
|
ref: 'MeDetailed',
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
@ -106,7 +106,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
});
|
});
|
||||||
|
|
||||||
const iObj = await this.userEntityService.pack(me.id, me, {
|
const iObj = await this.userEntityService.pack(me.id, me, {
|
||||||
detail: true,
|
schema: 'MeDetailed',
|
||||||
includeSecrets: true,
|
includeSecrets: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -451,8 +451,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
verifiedLinks: [],
|
verifiedLinks: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
const iObj = await this.userEntityService.pack<true, true>(user.id, user, {
|
const iObj = await this.userEntityService.pack(user.id, user, {
|
||||||
detail: true,
|
schema: 'MeDetailed',
|
||||||
includeSecrets: isSecure,
|
includeSecrets: isSecure,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -303,6 +303,11 @@ export const meta = {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
optional: false, nullable: true,
|
optional: false, nullable: true,
|
||||||
},
|
},
|
||||||
|
policies: {
|
||||||
|
type: 'object',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
ref: 'RolePolicies',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
|
@ -55,7 +55,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
var: ps.var,
|
var: ps.var,
|
||||||
userId: me.id,
|
userId: me.id,
|
||||||
user: await this.userEntityService.pack(me.id, { id: page.userId }, {
|
user: await this.userEntityService.pack(me.id, { id: page.userId }, {
|
||||||
detail: true,
|
schema: 'UserDetailed',
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -52,7 +52,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
host: acct.host ?? IsNull(),
|
host: acct.host ?? IsNull(),
|
||||||
})));
|
})));
|
||||||
|
|
||||||
return await this.userEntityService.packMany(users.filter(x => x !== null) as MiUser[], me, { detail: true });
|
return await this.userEntityService.packMany(users.filter(x => x !== null) as MiUser[], me, { schema: 'UserDetailed' });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,32 @@ export const meta = {
|
||||||
requireCredential: false,
|
requireCredential: false,
|
||||||
|
|
||||||
res: {
|
res: {
|
||||||
|
type: 'array',
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
createdAt: {
|
||||||
|
type: 'string',
|
||||||
|
format: 'date-time',
|
||||||
|
},
|
||||||
|
users: {
|
||||||
|
type: 'number',
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
type: 'object',
|
||||||
|
additionalProperties: {
|
||||||
|
anyOf: [{
|
||||||
|
type: 'number',
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
required: [
|
||||||
|
'createdAt',
|
||||||
|
'users',
|
||||||
|
'data',
|
||||||
|
],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
allowGet: true,
|
allowGet: true,
|
||||||
|
|
|
@ -33,11 +33,11 @@ export const meta = {
|
||||||
properties: {
|
properties: {
|
||||||
id: {
|
id: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
format: 'misskey:id'
|
format: 'misskey:id',
|
||||||
},
|
},
|
||||||
user: {
|
user: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
ref: 'User'
|
ref: 'UserDetailed',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
required: ['id', 'user'],
|
required: ['id', 'user'],
|
||||||
|
@ -94,7 +94,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
|
|
||||||
return await Promise.all(assigns.map(async assign => ({
|
return await Promise.all(assigns.map(async assign => ({
|
||||||
id: assign.id,
|
id: assign.id,
|
||||||
user: await this.userEntityService.pack(assign.user!, me, { detail: true }),
|
user: await this.userEntityService.pack(assign.user!, me, { schema: 'UserDetailed' }),
|
||||||
})));
|
})));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,7 +89,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
|
|
||||||
const users = await query.getMany();
|
const users = await query.getMany();
|
||||||
|
|
||||||
return await this.userEntityService.packMany(users, me, { detail: true });
|
return await this.userEntityService.packMany(users, me, { schema: 'UserDetailed' });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -122,7 +122,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
|
|
||||||
// Make replies object (includes weights)
|
// Make replies object (includes weights)
|
||||||
const repliesObj = await Promise.all(topRepliedUsers.map(async (user) => ({
|
const repliesObj = await Promise.all(topRepliedUsers.map(async (user) => ({
|
||||||
user: await this.userEntityService.pack(user, me, { detail: true }),
|
user: await this.userEntityService.pack(user, me, { schema: 'UserDetailed' }),
|
||||||
weight: repliedUsers[user] / peak,
|
weight: repliedUsers[user] / peak,
|
||||||
})));
|
})));
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,7 @@ export const meta = {
|
||||||
},
|
},
|
||||||
user: {
|
user: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
ref: 'User',
|
ref: 'UserLite',
|
||||||
},
|
},
|
||||||
withReplies: {
|
withReplies: {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
|
|
|
@ -76,7 +76,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
|
|
||||||
const users = await query.limit(ps.limit).offset(ps.offset).getMany();
|
const users = await query.limit(ps.limit).offset(ps.offset).getMany();
|
||||||
|
|
||||||
return await this.userEntityService.packMany(users, me, { detail: true });
|
return await this.userEntityService.packMany(users, me, { schema: 'UserDetailed' });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -131,7 +131,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
.getMany();
|
.getMany();
|
||||||
}
|
}
|
||||||
|
|
||||||
return await this.userEntityService.packMany(users, me, { detail: !!ps.detail });
|
return await this.userEntityService.packMany(users, me, { schema: ps.detail ? 'UserDetailed' : 'UserLite' });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -141,7 +141,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return await this.userEntityService.packMany(users, me, { detail: ps.detail });
|
return await this.userEntityService.packMany(users, me, { schema: ps.detail ? 'UserDetailed' : 'UserLite' });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -116,7 +116,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
}
|
}
|
||||||
|
|
||||||
return await Promise.all(_users.map(u => this.userEntityService.pack(u, me, {
|
return await Promise.all(_users.map(u => this.userEntityService.pack(u, me, {
|
||||||
detail: true,
|
schema: 'UserDetailed',
|
||||||
})));
|
})));
|
||||||
} else {
|
} else {
|
||||||
// Lookup user
|
// Lookup user
|
||||||
|
@ -146,7 +146,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
}
|
}
|
||||||
|
|
||||||
return await this.userEntityService.pack(user, me, {
|
return await this.userEntityService.pack(user, me, {
|
||||||
detail: true,
|
schema: 'UserDetailed',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
5
packages/frontend/@types/global.d.ts
vendored
5
packages/frontend/@types/global.d.ts
vendored
|
@ -16,3 +16,8 @@ declare const _DATA_TRANSFER_DECK_COLUMN_: string;
|
||||||
|
|
||||||
// for dev-mode
|
// for dev-mode
|
||||||
declare const _LANGS_FULL_: string[][];
|
declare const _LANGS_FULL_: string[][];
|
||||||
|
|
||||||
|
// TagCanvas
|
||||||
|
interface Window {
|
||||||
|
TagCanvas: any;
|
||||||
|
}
|
||||||
|
|
|
@ -20,15 +20,15 @@
|
||||||
"@discordapp/twemoji": "15.0.2",
|
"@discordapp/twemoji": "15.0.2",
|
||||||
"@github/webauthn-json": "2.1.1",
|
"@github/webauthn-json": "2.1.1",
|
||||||
"@mcaptcha/vanilla-glue": "0.1.0-alpha-3",
|
"@mcaptcha/vanilla-glue": "0.1.0-alpha-3",
|
||||||
"@misskey-dev/browser-image-resizer": "2.2.1-misskey.10",
|
"@misskey-dev/browser-image-resizer": "2024.1.0",
|
||||||
"@rollup/plugin-json": "6.1.0",
|
"@rollup/plugin-json": "6.1.0",
|
||||||
"@rollup/plugin-replace": "5.0.5",
|
"@rollup/plugin-replace": "5.0.5",
|
||||||
"@rollup/pluginutils": "5.1.0",
|
"@rollup/pluginutils": "5.1.0",
|
||||||
"@syuilo/aiscript": "0.17.0",
|
"@syuilo/aiscript": "0.17.0",
|
||||||
"@tabler/icons-webfont": "2.44.0",
|
"@tabler/icons-webfont": "2.44.0",
|
||||||
"@twemoji/parser": "15.0.0",
|
"@twemoji/parser": "15.0.0",
|
||||||
"@vitejs/plugin-vue": "5.0.2",
|
"@vitejs/plugin-vue": "5.0.3",
|
||||||
"@vue/compiler-sfc": "3.4.3",
|
"@vue/compiler-sfc": "3.4.15",
|
||||||
"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.0.6",
|
"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.0.6",
|
||||||
"astring": "1.8.6",
|
"astring": "1.8.6",
|
||||||
"broadcast-channel": "7.0.0",
|
"broadcast-channel": "7.0.0",
|
||||||
|
@ -39,7 +39,7 @@
|
||||||
"chartjs-chart-matrix": "2.0.1",
|
"chartjs-chart-matrix": "2.0.1",
|
||||||
"chartjs-plugin-gradient": "0.6.1",
|
"chartjs-plugin-gradient": "0.6.1",
|
||||||
"chartjs-plugin-zoom": "2.0.1",
|
"chartjs-plugin-zoom": "2.0.1",
|
||||||
"chromatic": "10.3.1",
|
"chromatic": "10.6.1",
|
||||||
"compare-versions": "6.1.0",
|
"compare-versions": "6.1.0",
|
||||||
"cropperjs": "2.0.0-beta.4",
|
"cropperjs": "2.0.0-beta.4",
|
||||||
"date-fns": "2.30.0",
|
"date-fns": "2.30.0",
|
||||||
|
@ -63,7 +63,7 @@
|
||||||
"shiki": "0.14.7",
|
"shiki": "0.14.7",
|
||||||
"strict-event-emitter-types": "2.0.0",
|
"strict-event-emitter-types": "2.0.0",
|
||||||
"textarea-caret": "3.1.0",
|
"textarea-caret": "3.1.0",
|
||||||
"three": "0.160.0",
|
"three": "0.160.1",
|
||||||
"throttle-debounce": "5.0.0",
|
"throttle-debounce": "5.0.0",
|
||||||
"tinycolor2": "1.6.0",
|
"tinycolor2": "1.6.0",
|
||||||
"tsc-alias": "1.8.8",
|
"tsc-alias": "1.8.8",
|
||||||
|
@ -76,8 +76,8 @@
|
||||||
"vuedraggable": "next"
|
"vuedraggable": "next"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@misskey-dev/eslint-plugin": "^1.0.0",
|
"@misskey-dev/eslint-plugin": "1.0.0",
|
||||||
"@misskey-dev/summaly": "^5.0.3",
|
"@misskey-dev/summaly": "5.0.3",
|
||||||
"@storybook/addon-actions": "7.6.10",
|
"@storybook/addon-actions": "7.6.10",
|
||||||
"@storybook/addon-essentials": "7.6.10",
|
"@storybook/addon-essentials": "7.6.10",
|
||||||
"@storybook/addon-interactions": "7.6.10",
|
"@storybook/addon-interactions": "7.6.10",
|
||||||
|
@ -101,12 +101,12 @@
|
||||||
"@types/estree": "1.0.5",
|
"@types/estree": "1.0.5",
|
||||||
"@types/matter-js": "0.19.6",
|
"@types/matter-js": "0.19.6",
|
||||||
"@types/micromatch": "4.0.6",
|
"@types/micromatch": "4.0.6",
|
||||||
"@types/node": "20.11.5",
|
"@types/node": "20.11.10",
|
||||||
"@types/punycode": "2.1.3",
|
"@types/punycode": "2.1.3",
|
||||||
"@types/sanitize-html": "2.9.5",
|
"@types/sanitize-html": "2.9.5",
|
||||||
"@types/throttle-debounce": "5.0.2",
|
"@types/throttle-debounce": "5.0.2",
|
||||||
"@types/tinycolor2": "1.4.6",
|
"@types/tinycolor2": "1.4.6",
|
||||||
"@types/uuid": "9.0.7",
|
"@types/uuid": "9.0.8",
|
||||||
"@types/ws": "8.5.10",
|
"@types/ws": "8.5.10",
|
||||||
"@typescript-eslint/eslint-plugin": "6.18.1",
|
"@typescript-eslint/eslint-plugin": "6.18.1",
|
||||||
"@typescript-eslint/parser": "6.18.1",
|
"@typescript-eslint/parser": "6.18.1",
|
||||||
|
@ -134,7 +134,7 @@
|
||||||
"vite-plugin-turbosnap": "1.0.3",
|
"vite-plugin-turbosnap": "1.0.3",
|
||||||
"vitest": "0.34.6",
|
"vitest": "0.34.6",
|
||||||
"vitest-fetch-mock": "0.2.2",
|
"vitest-fetch-mock": "0.2.2",
|
||||||
"vue-eslint-parser": "9.4.0",
|
"vue-eslint-parser": "9.4.2",
|
||||||
"vue-tsc": "1.8.27"
|
"vue-tsc": "1.8.27"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ import { getAccountFromId } from '@/scripts/get-account-from-id.js';
|
||||||
import { deckStore } from '@/ui/deck/deck-store.js';
|
import { deckStore } from '@/ui/deck/deck-store.js';
|
||||||
import { miLocalStorage } from '@/local-storage.js';
|
import { miLocalStorage } from '@/local-storage.js';
|
||||||
import { fetchCustomEmojis } from '@/custom-emojis.js';
|
import { fetchCustomEmojis } from '@/custom-emojis.js';
|
||||||
import { setupRouter } from '@/global/router/definition.js';
|
import { setupRouter } from '@/router/definition.js';
|
||||||
|
|
||||||
export async function common(createVue: () => App<Element>) {
|
export async function common(createVue: () => App<Element>) {
|
||||||
console.info(`Misskey v${version}`);
|
console.info(`Misskey v${version}`);
|
||||||
|
|
|
@ -19,7 +19,7 @@ import { claimAchievement, claimedAchievements } from '@/scripts/achievements.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 { mainRouter } from '@/global/router/main.js';
|
import { mainRouter } from '@/router/main.js';
|
||||||
|
|
||||||
export async function mainBoot() {
|
export async function mainBoot() {
|
||||||
const { isClientUpdated } = await common(() => createApp(
|
const { isClientUpdated } = await common(() => createApp(
|
||||||
|
|
|
@ -68,7 +68,7 @@ import MkNoteSimple from '@/components/MkNoteSimple.vue';
|
||||||
import MkSwitch from '@/components/MkSwitch.vue';
|
import MkSwitch from '@/components/MkSwitch.vue';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
user: Misskey.entities.User;
|
user: Misskey.entities.UserDetailed;
|
||||||
initialComment?: string;
|
initialComment?: string;
|
||||||
initialNoteId?: Misskey.entities.Note['id'];
|
initialNoteId?: Misskey.entities.Note['id'];
|
||||||
}>();
|
}>();
|
||||||
|
|
|
@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div v-if="achievements" :class="$style.root">
|
<div v-if="achievements" :class="$style.root">
|
||||||
<div v-for="achievement in achievements" :key="achievement" :class="$style.achievement" class="_panel">
|
<div v-for="achievement in achievements" :key="achievement.name" :class="$style.achievement" class="_panel">
|
||||||
<div :class="$style.icon">
|
<div :class="$style.icon">
|
||||||
<div
|
<div
|
||||||
:class="[$style.iconFrame, {
|
:class="[$style.iconFrame, {
|
||||||
|
|
|
@ -49,7 +49,7 @@ async function ok() {
|
||||||
if (confirm.canceled) return;
|
if (confirm.canceled) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
modal.value.close();
|
modal.value?.close();
|
||||||
misskeyApi('i/read-announcement', { announcementId: props.announcement.id });
|
misskeyApi('i/read-announcement', { announcementId: props.announcement.id });
|
||||||
updateAccount({
|
updateAccount({
|
||||||
unreadAnnouncements: $i!.unreadAnnouncements.filter(a => a.id !== props.announcement.id),
|
unreadAnnouncements: $i!.unreadAnnouncements.filter(a => a.id !== props.announcement.id),
|
||||||
|
@ -57,7 +57,7 @@ async function ok() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function onBgClick() {
|
function onBgClick() {
|
||||||
rootEl.value.animate([{
|
rootEl.value?.animate([{
|
||||||
offset: 0,
|
offset: 0,
|
||||||
transform: 'scale(1)',
|
transform: 'scale(1)',
|
||||||
}, {
|
}, {
|
||||||
|
|
|
@ -10,8 +10,8 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<MkAsUi v-if="!g(child).hidden" :component="g(child)" :components="props.components" :size="size"/>
|
<MkAsUi v-if="!g(child).hidden" :component="g(child)" :components="props.components" :size="size"/>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<span v-else-if="c.type === 'text'" :class="{ [$style.fontSerif]: c.font === 'serif', [$style.fontMonospace]: c.font === 'monospace' }" :style="{ fontSize: c.size ? `${c.size * 100}%` : null, fontWeight: c.bold ? 'bold' : null, color: c.color ?? null }">{{ c.text }}</span>
|
<span v-else-if="c.type === 'text'" :class="{ [$style.fontSerif]: c.font === 'serif', [$style.fontMonospace]: c.font === 'monospace' }" :style="{ fontSize: c.size ? `${c.size * 100}%` : undefined, fontWeight: c.bold ? 'bold' : undefined, color: c.color }">{{ c.text }}</span>
|
||||||
<Mfm v-else-if="c.type === 'mfm'" :class="{ [$style.fontSerif]: c.font === 'serif', [$style.fontMonospace]: c.font === 'monospace' }" :style="{ fontSize: c.size ? `${c.size * 100}%` : null, fontWeight: c.bold ? 'bold' : null, color: c.color ?? null }" :text="c.text" @clickEv="c.onClickEv"/>
|
<Mfm v-else-if="c.type === 'mfm'" :class="{ [$style.fontSerif]: c.font === 'serif', [$style.fontMonospace]: c.font === 'monospace' }" :style="{ fontSize: c.size ? `${c.size * 100}%` : null, fontWeight: c.bold ? 'bold' : null, color: c.color ?? null }" :text="c.text ?? ''" @clickEv="c.onClickEv"/>
|
||||||
<MkButton v-else-if="c.type === 'button'" :primary="c.primary" :rounded="c.rounded" :disabled="c.disabled" :small="size === 'small'" inline @click="c.onClick">{{ c.text }}</MkButton>
|
<MkButton v-else-if="c.type === 'button'" :primary="c.primary" :rounded="c.rounded" :disabled="c.disabled" :small="size === 'small'" inline @click="c.onClick">{{ c.text }}</MkButton>
|
||||||
<div v-else-if="c.type === 'buttons'" class="_buttons" :style="{ justifyContent: align }">
|
<div v-else-if="c.type === 'buttons'" class="_buttons" :style="{ justifyContent: align }">
|
||||||
<MkButton v-for="button in c.buttons" :primary="button.primary" :rounded="button.rounded" :disabled="button.disabled" inline :small="size === 'small'" @click="button.onClick">{{ button.text }}</MkButton>
|
<MkButton v-for="button in c.buttons" :primary="button.primary" :rounded="button.rounded" :disabled="button.disabled" inline :small="size === 'small'" @click="button.onClick">{{ button.text }}</MkButton>
|
||||||
|
@ -20,19 +20,19 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<template v-if="c.label" #label>{{ c.label }}</template>
|
<template v-if="c.label" #label>{{ c.label }}</template>
|
||||||
<template v-if="c.caption" #caption>{{ c.caption }}</template>
|
<template v-if="c.caption" #caption>{{ c.caption }}</template>
|
||||||
</MkSwitch>
|
</MkSwitch>
|
||||||
<MkTextarea v-else-if="c.type === 'textarea'" :modelValue="c.default" @update:modelValue="c.onInput">
|
<MkTextarea v-else-if="c.type === 'textarea'" :modelValue="c.default ?? null" @update:modelValue="c.onInput">
|
||||||
<template v-if="c.label" #label>{{ c.label }}</template>
|
<template v-if="c.label" #label>{{ c.label }}</template>
|
||||||
<template v-if="c.caption" #caption>{{ c.caption }}</template>
|
<template v-if="c.caption" #caption>{{ c.caption }}</template>
|
||||||
</MkTextarea>
|
</MkTextarea>
|
||||||
<MkInput v-else-if="c.type === 'textInput'" :small="size === 'small'" :modelValue="c.default" @update:modelValue="c.onInput">
|
<MkInput v-else-if="c.type === 'textInput'" :small="size === 'small'" :modelValue="c.default ?? null" @update:modelValue="c.onInput">
|
||||||
<template v-if="c.label" #label>{{ c.label }}</template>
|
<template v-if="c.label" #label>{{ c.label }}</template>
|
||||||
<template v-if="c.caption" #caption>{{ c.caption }}</template>
|
<template v-if="c.caption" #caption>{{ c.caption }}</template>
|
||||||
</MkInput>
|
</MkInput>
|
||||||
<MkInput v-else-if="c.type === 'numberInput'" :small="size === 'small'" :modelValue="c.default" type="number" @update:modelValue="c.onInput">
|
<MkInput v-else-if="c.type === 'numberInput'" :small="size === 'small'" :modelValue="c.default ?? null" type="number" @update:modelValue="c.onInput">
|
||||||
<template v-if="c.label" #label>{{ c.label }}</template>
|
<template v-if="c.label" #label>{{ c.label }}</template>
|
||||||
<template v-if="c.caption" #caption>{{ c.caption }}</template>
|
<template v-if="c.caption" #caption>{{ c.caption }}</template>
|
||||||
</MkInput>
|
</MkInput>
|
||||||
<MkSelect v-else-if="c.type === 'select'" :small="size === 'small'" :modelValue="c.default" @update:modelValue="c.onChange">
|
<MkSelect v-else-if="c.type === 'select'" :small="size === 'small'" :modelValue="c.default ?? null" @update:modelValue="c.onChange">
|
||||||
<template v-if="c.label" #label>{{ c.label }}</template>
|
<template v-if="c.label" #label>{{ c.label }}</template>
|
||||||
<template v-if="c.caption" #caption>{{ c.caption }}</template>
|
<template v-if="c.caption" #caption>{{ c.caption }}</template>
|
||||||
<option v-for="item in c.items" :key="item.value" :value="item.value">{{ item.text }}</option>
|
<option v-for="item in c.items" :key="item.value" :value="item.value">{{ item.text }}</option>
|
||||||
|
@ -42,8 +42,8 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<MkPostForm
|
<MkPostForm
|
||||||
fixed
|
fixed
|
||||||
:instant="true"
|
:instant="true"
|
||||||
:initialText="c.form.text"
|
:initialText="c.form?.text"
|
||||||
:initialCw="c.form.cw"
|
:initialCw="c.form?.cw"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<MkFolder v-else-if="c.type === 'folder'" :defaultOpen="c.opened">
|
<MkFolder v-else-if="c.type === 'folder'" :defaultOpen="c.opened">
|
||||||
|
@ -52,7 +52,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<MkAsUi v-if="!g(child).hidden" :component="g(child)" :components="props.components" :size="size"/>
|
<MkAsUi v-if="!g(child).hidden" :component="g(child)" :components="props.components" :size="size"/>
|
||||||
</template>
|
</template>
|
||||||
</MkFolder>
|
</MkFolder>
|
||||||
<div v-else-if="c.type === 'container'" :class="[$style.container, { [$style.fontSerif]: c.font === 'serif', [$style.fontMonospace]: c.font === 'monospace' }]" :style="{ textAlign: c.align ?? null, backgroundColor: c.bgColor ?? null, color: c.fgColor ?? null, borderWidth: c.borderWidth ? `${c.borderWidth}px` : 0, borderColor: c.borderColor ?? 'var(--divider)', padding: c.padding ? `${c.padding}px` : 0, borderRadius: c.rounded ? '8px' : 0 }">
|
<div v-else-if="c.type === 'container'" :class="[$style.container, { [$style.fontSerif]: c.font === 'serif', [$style.fontMonospace]: c.font === 'monospace' }]" :style="{ textAlign: c.align, backgroundColor: c.bgColor, color: c.fgColor, borderWidth: c.borderWidth ? `${c.borderWidth}px` : 0, borderColor: c.borderColor ?? 'var(--divider)', padding: c.padding ? `${c.padding}px` : 0, borderRadius: c.rounded ? '8px' : 0 }">
|
||||||
<template v-for="child in c.children" :key="child">
|
<template v-for="child in c.children" :key="child">
|
||||||
<MkAsUi v-if="!g(child).hidden" :component="g(child)" :components="props.components" :size="size" :align="c.align"/>
|
<MkAsUi v-if="!g(child).hidden" :component="g(child)" :components="props.components" :size="size" :align="c.align"/>
|
||||||
</template>
|
</template>
|
||||||
|
@ -68,7 +68,7 @@ import MkInput from '@/components/MkInput.vue';
|
||||||
import MkSwitch from '@/components/MkSwitch.vue';
|
import MkSwitch from '@/components/MkSwitch.vue';
|
||||||
import MkTextarea from '@/components/MkTextarea.vue';
|
import MkTextarea from '@/components/MkTextarea.vue';
|
||||||
import MkSelect from '@/components/MkSelect.vue';
|
import MkSelect from '@/components/MkSelect.vue';
|
||||||
import { AsUiComponent } from '@/scripts/aiscript/ui.js';
|
import { AsUiComponent, AsUiRoot, AsUiPostFormButton } from '@/scripts/aiscript/ui.js';
|
||||||
import MkFolder from '@/components/MkFolder.vue';
|
import MkFolder from '@/components/MkFolder.vue';
|
||||||
import MkPostForm from '@/components/MkPostForm.vue';
|
import MkPostForm from '@/components/MkPostForm.vue';
|
||||||
|
|
||||||
|
@ -85,20 +85,32 @@ const props = withDefaults(defineProps<{
|
||||||
const c = props.component;
|
const c = props.component;
|
||||||
|
|
||||||
function g(id) {
|
function g(id) {
|
||||||
return props.components.find(x => x.value.id === id).value;
|
const v = props.components.find(x => x.value.id === id)?.value;
|
||||||
|
if (v) return v;
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: 'dummy',
|
||||||
|
type: 'root',
|
||||||
|
children: [],
|
||||||
|
} as AsUiRoot;
|
||||||
}
|
}
|
||||||
|
|
||||||
const valueForSwitch = ref(c.default ?? false);
|
const valueForSwitch = ref('default' in c && typeof c.default === 'boolean' ? c.default : false);
|
||||||
|
|
||||||
function onSwitchUpdate(v) {
|
function onSwitchUpdate(v) {
|
||||||
valueForSwitch.value = v;
|
valueForSwitch.value = v;
|
||||||
if (c.onChange) c.onChange(v);
|
if ('onChange' in c && c.onChange) {
|
||||||
|
c.onChange(v as never);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function openPostForm() {
|
function openPostForm() {
|
||||||
|
const form = (c as AsUiPostFormButton).form;
|
||||||
|
if (!form) return;
|
||||||
|
|
||||||
os.post({
|
os.post({
|
||||||
initialText: c.form.text,
|
initialText: form.text,
|
||||||
initialCw: c.form.cw,
|
initialCw: form.cw,
|
||||||
instant: true,
|
instant: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,7 +54,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
[$style.gamingLight]: gaming === 'light',
|
[$style.gamingLight]: gaming === 'light',
|
||||||
}
|
}
|
||||||
]"
|
]"
|
||||||
:to="to"
|
:to="to ?? '#'"
|
||||||
@mousedown="onMousedown"
|
@mousedown="onMousedown"
|
||||||
>
|
>
|
||||||
<div ref="ripples" :class="$style.ripples" :data-children-class="$style.ripple"></div>
|
<div ref="ripples" :class="$style.ripples" :data-children-class="$style.ripple"></div>
|
||||||
|
|
|
@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<span v-if="!available">{{ i18n.ts.waiting }}<MkEllipsis/></span>
|
<span v-if="!available">Loading<MkEllipsis/></span>
|
||||||
<div v-if="props.provider == 'mcaptcha'">
|
<div v-if="props.provider == 'mcaptcha'">
|
||||||
<div id="mcaptcha__widget-container" class="m-captcha-style"></div>
|
<div id="mcaptcha__widget-container" class="m-captcha-style"></div>
|
||||||
<div ref="captchaEl"></div>
|
<div ref="captchaEl"></div>
|
||||||
|
@ -17,7 +17,6 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, shallowRef, computed, onMounted, onBeforeUnmount, watch, onUnmounted } from 'vue';
|
import { ref, shallowRef, computed, onMounted, onBeforeUnmount, watch, onUnmounted } from 'vue';
|
||||||
import { defaultStore } from '@/store.js';
|
import { defaultStore } from '@/store.js';
|
||||||
import { i18n } from '@/i18n.js';
|
|
||||||
|
|
||||||
// APIs provided by Captcha services
|
// APIs provided by Captcha services
|
||||||
export type Captcha = {
|
export type Captcha = {
|
||||||
|
|
|
@ -21,13 +21,13 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
import { onMounted, ref, shallowRef, watch, PropType } from 'vue';
|
import { onMounted, ref, shallowRef, watch, PropType } from 'vue';
|
||||||
import { Chart } from 'chart.js';
|
import { Chart } from 'chart.js';
|
||||||
import gradient from 'chartjs-plugin-gradient';
|
|
||||||
import { misskeyApiGet } from '@/scripts/misskey-api.js';
|
import { misskeyApiGet } from '@/scripts/misskey-api.js';
|
||||||
import { defaultStore } from '@/store.js';
|
import { defaultStore } from '@/store.js';
|
||||||
import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
|
import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
|
||||||
import { chartVLine } from '@/scripts/chart-vline.js';
|
import { chartVLine } from '@/scripts/chart-vline.js';
|
||||||
import { alpha } from '@/scripts/color.js';
|
import { alpha } from '@/scripts/color.js';
|
||||||
import date from '@/filters/date.js';
|
import date from '@/filters/date.js';
|
||||||
|
import bytes from '@/filters/bytes.js';
|
||||||
import { initChart } from '@/scripts/init-chart.js';
|
import { initChart } from '@/scripts/init-chart.js';
|
||||||
import { chartLegend } from '@/scripts/chart-legend.js';
|
import { chartLegend } from '@/scripts/chart-legend.js';
|
||||||
import MkChartLegend from '@/components/MkChartLegend.vue';
|
import MkChartLegend from '@/components/MkChartLegend.vue';
|
||||||
|
@ -95,7 +95,7 @@ const getColor = (i) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
let chartInstance: Chart = null;
|
let chartInstance: Chart | null = null;
|
||||||
let chartData: {
|
let chartData: {
|
||||||
series: {
|
series: {
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -108,9 +108,10 @@ let chartData: {
|
||||||
y: number;
|
y: number;
|
||||||
}[];
|
}[];
|
||||||
}[];
|
}[];
|
||||||
} = null;
|
bytes?: boolean;
|
||||||
|
} | null = null;
|
||||||
|
|
||||||
const chartEl = shallowRef<HTMLCanvasElement>(null);
|
const chartEl = shallowRef<HTMLCanvasElement | null>(null);
|
||||||
const fetching = ref(true);
|
const fetching = ref(true);
|
||||||
|
|
||||||
const getDate = (ago: number) => {
|
const getDate = (ago: number) => {
|
||||||
|
@ -132,6 +133,7 @@ const format = (arr) => {
|
||||||
const { handler: externalTooltipHandler } = useChartTooltip();
|
const { handler: externalTooltipHandler } = useChartTooltip();
|
||||||
|
|
||||||
const render = () => {
|
const render = () => {
|
||||||
|
if (chartData == null || chartEl.value == null) return;
|
||||||
if (chartInstance) {
|
if (chartInstance) {
|
||||||
chartInstance.destroy();
|
chartInstance.destroy();
|
||||||
}
|
}
|
||||||
|
@ -188,7 +190,6 @@ const render = () => {
|
||||||
stacked: props.stacked,
|
stacked: props.stacked,
|
||||||
offset: false,
|
offset: false,
|
||||||
time: {
|
time: {
|
||||||
stepSize: 1,
|
|
||||||
unit: props.span === 'day' ? 'month' : 'day',
|
unit: props.span === 'day' ? 'month' : 'day',
|
||||||
displayFormats: {
|
displayFormats: {
|
||||||
day: 'M/d',
|
day: 'M/d',
|
||||||
|
@ -198,6 +199,7 @@ const render = () => {
|
||||||
grid: {
|
grid: {
|
||||||
},
|
},
|
||||||
ticks: {
|
ticks: {
|
||||||
|
stepSize: 1,
|
||||||
display: props.detailed,
|
display: props.detailed,
|
||||||
maxRotation: 0,
|
maxRotation: 0,
|
||||||
autoSkipPadding: 16,
|
autoSkipPadding: 16,
|
||||||
|
@ -237,6 +239,9 @@ const render = () => {
|
||||||
duration: 0,
|
duration: 0,
|
||||||
},
|
},
|
||||||
external: externalTooltipHandler,
|
external: externalTooltipHandler,
|
||||||
|
callbacks: {
|
||||||
|
label: (item) => chartData?.bytes ? bytes(item.parsed.y * 1000, 1) : item.parsed.y.toString(),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
zoom: props.detailed ? {
|
zoom: props.detailed ? {
|
||||||
pan: {
|
pan: {
|
||||||
|
@ -265,10 +270,9 @@ const render = () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} : undefined,
|
} : undefined,
|
||||||
gradient,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: [chartVLine(vLineColor), ...(props.detailed ? [chartLegend(legendEl.value)] : [])],
|
plugins: [chartVLine(vLineColor), ...(props.detailed && legendEl.value ? [chartLegend(legendEl.value)] : [])],
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -566,7 +570,7 @@ const fetchDriveFilesChart = async (): Promise<typeof chartData> => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchInstanceRequestsChart = async (): Promise<typeof chartData> => {
|
const fetchInstanceRequestsChart = async (): Promise<typeof chartData> => {
|
||||||
const raw = await misskeyApiGet('charts/instance', { host: props.args.host, limit: props.limit, span: props.span });
|
const raw = await misskeyApiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span });
|
||||||
return {
|
return {
|
||||||
series: [{
|
series: [{
|
||||||
name: 'In',
|
name: 'In',
|
||||||
|
@ -588,7 +592,7 @@ const fetchInstanceRequestsChart = async (): Promise<typeof chartData> => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchInstanceUsersChart = async (total: boolean): Promise<typeof chartData> => {
|
const fetchInstanceUsersChart = async (total: boolean): Promise<typeof chartData> => {
|
||||||
const raw = await misskeyApiGet('charts/instance', { host: props.args.host, limit: props.limit, span: props.span });
|
const raw = await misskeyApiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span });
|
||||||
return {
|
return {
|
||||||
series: [{
|
series: [{
|
||||||
name: 'Users',
|
name: 'Users',
|
||||||
|
@ -603,7 +607,7 @@ const fetchInstanceUsersChart = async (total: boolean): Promise<typeof chartData
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchInstanceNotesChart = async (total: boolean): Promise<typeof chartData> => {
|
const fetchInstanceNotesChart = async (total: boolean): Promise<typeof chartData> => {
|
||||||
const raw = await misskeyApiGet('charts/instance', { host: props.args.host, limit: props.limit, span: props.span });
|
const raw = await misskeyApiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span });
|
||||||
return {
|
return {
|
||||||
series: [{
|
series: [{
|
||||||
name: 'Notes',
|
name: 'Notes',
|
||||||
|
@ -618,7 +622,7 @@ const fetchInstanceNotesChart = async (total: boolean): Promise<typeof chartData
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchInstanceFfChart = async (total: boolean): Promise<typeof chartData> => {
|
const fetchInstanceFfChart = async (total: boolean): Promise<typeof chartData> => {
|
||||||
const raw = await misskeyApiGet('charts/instance', { host: props.args.host, limit: props.limit, span: props.span });
|
const raw = await misskeyApiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span });
|
||||||
return {
|
return {
|
||||||
series: [{
|
series: [{
|
||||||
name: 'Following',
|
name: 'Following',
|
||||||
|
@ -641,7 +645,7 @@ const fetchInstanceFfChart = async (total: boolean): Promise<typeof chartData> =
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchInstanceDriveUsageChart = async (total: boolean): Promise<typeof chartData> => {
|
const fetchInstanceDriveUsageChart = async (total: boolean): Promise<typeof chartData> => {
|
||||||
const raw = await misskeyApiGet('charts/instance', { host: props.args.host, limit: props.limit, span: props.span });
|
const raw = await misskeyApiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span });
|
||||||
return {
|
return {
|
||||||
bytes: true,
|
bytes: true,
|
||||||
series: [{
|
series: [{
|
||||||
|
@ -649,7 +653,7 @@ const fetchInstanceDriveUsageChart = async (total: boolean): Promise<typeof char
|
||||||
type: 'area',
|
type: 'area',
|
||||||
color: '#008FFB',
|
color: '#008FFB',
|
||||||
data: format(total
|
data: format(total
|
||||||
? raw.drive.totalUsage
|
? sum(raw.drive.incUsage)
|
||||||
: sum(raw.drive.incUsage, negate(raw.drive.decUsage)),
|
: sum(raw.drive.incUsage, negate(raw.drive.decUsage)),
|
||||||
),
|
),
|
||||||
}],
|
}],
|
||||||
|
@ -657,7 +661,7 @@ const fetchInstanceDriveUsageChart = async (total: boolean): Promise<typeof char
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchInstanceDriveFilesChart = async (total: boolean): Promise<typeof chartData> => {
|
const fetchInstanceDriveFilesChart = async (total: boolean): Promise<typeof chartData> => {
|
||||||
const raw = await misskeyApiGet('charts/instance', { host: props.args.host, limit: props.limit, span: props.span });
|
const raw = await misskeyApiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span });
|
||||||
return {
|
return {
|
||||||
series: [{
|
series: [{
|
||||||
name: 'Drive files',
|
name: 'Drive files',
|
||||||
|
@ -672,11 +676,11 @@ const fetchInstanceDriveFilesChart = async (total: boolean): Promise<typeof char
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchPerUserNotesChart = async (): Promise<typeof chartData> => {
|
const fetchPerUserNotesChart = async (): Promise<typeof chartData> => {
|
||||||
const raw = await misskeyApiGet('charts/user/notes', { userId: props.args.user.id, limit: props.limit, span: props.span });
|
const raw = await misskeyApiGet('charts/user/notes', { userId: props.args?.user?.id, limit: props.limit, span: props.span });
|
||||||
return {
|
return {
|
||||||
series: [...(props.args.withoutAll ? [] : [{
|
series: [...(props.args?.withoutAll ? [] : [{
|
||||||
name: 'All',
|
name: 'All',
|
||||||
type: 'line',
|
type: 'line' as const,
|
||||||
data: format(sum(raw.inc, negate(raw.dec))),
|
data: format(sum(raw.inc, negate(raw.dec))),
|
||||||
color: '#888888',
|
color: '#888888',
|
||||||
}]), {
|
}]), {
|
||||||
|
@ -704,7 +708,7 @@ const fetchPerUserNotesChart = async (): Promise<typeof chartData> => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchPerUserPvChart = async (): Promise<typeof chartData> => {
|
const fetchPerUserPvChart = async (): Promise<typeof chartData> => {
|
||||||
const raw = await misskeyApiGet('charts/user/pv', { userId: props.args.user.id, limit: props.limit, span: props.span });
|
const raw = await misskeyApiGet('charts/user/pv', { userId: props.args?.user?.id, limit: props.limit, span: props.span });
|
||||||
return {
|
return {
|
||||||
series: [{
|
series: [{
|
||||||
name: 'Unique PV (user)',
|
name: 'Unique PV (user)',
|
||||||
|
@ -731,7 +735,7 @@ const fetchPerUserPvChart = async (): Promise<typeof chartData> => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchPerUserFollowingChart = async (): Promise<typeof chartData> => {
|
const fetchPerUserFollowingChart = async (): Promise<typeof chartData> => {
|
||||||
const raw = await misskeyApiGet('charts/user/following', { userId: props.args.user.id, limit: props.limit, span: props.span });
|
const raw = await misskeyApiGet('charts/user/following', { userId: props.args?.user?.id, limit: props.limit, span: props.span });
|
||||||
return {
|
return {
|
||||||
series: [{
|
series: [{
|
||||||
name: 'Local',
|
name: 'Local',
|
||||||
|
@ -746,7 +750,7 @@ const fetchPerUserFollowingChart = async (): Promise<typeof chartData> => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchPerUserFollowersChart = async (): Promise<typeof chartData> => {
|
const fetchPerUserFollowersChart = async (): Promise<typeof chartData> => {
|
||||||
const raw = await misskeyApiGet('charts/user/following', { userId: props.args.user.id, limit: props.limit, span: props.span });
|
const raw = await misskeyApiGet('charts/user/following', { userId: props.args?.user?.id, limit: props.limit, span: props.span });
|
||||||
return {
|
return {
|
||||||
series: [{
|
series: [{
|
||||||
name: 'Local',
|
name: 'Local',
|
||||||
|
@ -761,8 +765,9 @@ const fetchPerUserFollowersChart = async (): Promise<typeof chartData> => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchPerUserDriveChart = async (): Promise<typeof chartData> => {
|
const fetchPerUserDriveChart = async (): Promise<typeof chartData> => {
|
||||||
const raw = await misskeyApiGet('charts/user/drive', { userId: props.args.user.id, limit: props.limit, span: props.span });
|
const raw = await misskeyApiGet('charts/user/drive', { userId: props.args?.user?.id, limit: props.limit, span: props.span });
|
||||||
return {
|
return {
|
||||||
|
bytes: true,
|
||||||
series: [{
|
series: [{
|
||||||
name: 'Inc',
|
name: 'Inc',
|
||||||
type: 'area',
|
type: 'area',
|
||||||
|
@ -806,6 +811,8 @@ const fetchAndRender = async () => {
|
||||||
case 'per-user-following': return fetchPerUserFollowingChart();
|
case 'per-user-following': return fetchPerUserFollowingChart();
|
||||||
case 'per-user-followers': return fetchPerUserFollowersChart();
|
case 'per-user-followers': return fetchPerUserFollowersChart();
|
||||||
case 'per-user-drive': return fetchPerUserDriveChart();
|
case 'per-user-drive': return fetchPerUserDriveChart();
|
||||||
|
|
||||||
|
default: return null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
fetching.value = true;
|
fetching.value = true;
|
||||||
|
|
|
@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<template>
|
<template>
|
||||||
<div :class="$style.root">
|
<div :class="$style.root">
|
||||||
<button v-for="item in items" class="_button item" :class="{ disabled: item.hidden }" @click="onClick(item)">
|
<button v-for="item in items" class="_button item" :class="{ disabled: item.hidden }" @click="onClick(item)">
|
||||||
<span class="box" :style="{ background: chart.config.type === 'line' ? item.strokeStyle?.toString() : item.fillStyle?.toString() }"></span>
|
<span class="box" :style="{ background: type === 'line' ? item.strokeStyle?.toString() : item.fillStyle?.toString() }"></span>
|
||||||
{{ item.text }}
|
{{ item.text }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -16,25 +16,23 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
import { shallowRef } from 'vue';
|
import { shallowRef } from 'vue';
|
||||||
import { Chart, LegendItem } from 'chart.js';
|
import { Chart, LegendItem } from 'chart.js';
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
});
|
|
||||||
|
|
||||||
const chart = shallowRef<Chart>();
|
const chart = shallowRef<Chart>();
|
||||||
|
const type = shallowRef<string>();
|
||||||
const items = shallowRef<LegendItem[]>([]);
|
const items = shallowRef<LegendItem[]>([]);
|
||||||
|
|
||||||
function update(_chart: Chart, _items: LegendItem[]) {
|
function update(_chart: Chart, _items: LegendItem[]) {
|
||||||
chart.value = _chart,
|
chart.value = _chart,
|
||||||
items.value = _items;
|
items.value = _items;
|
||||||
|
if ('type' in _chart.config) type.value = _chart.config.type;
|
||||||
}
|
}
|
||||||
|
|
||||||
function onClick(item: LegendItem) {
|
function onClick(item: LegendItem) {
|
||||||
if (chart.value == null) return;
|
if (chart.value == null) return;
|
||||||
const { type } = chart.value.config;
|
if (type.value === 'pie' || type.value === 'doughnut') {
|
||||||
if (type === 'pie' || type === 'doughnut') {
|
|
||||||
// Pie and doughnut charts only have a single dataset and visibility is per item
|
// Pie and doughnut charts only have a single dataset and visibility is per item
|
||||||
chart.value.toggleDataVisibility(item.index);
|
if (item.index) chart.value.toggleDataVisibility(item.index);
|
||||||
} else {
|
} else {
|
||||||
chart.value.setDatasetVisibility(item.datasetIndex, !chart.value.isDatasetVisible(item.datasetIndex));
|
if (item.datasetIndex) chart.value.setDatasetVisibility(item.datasetIndex, !chart.value.isDatasetVisible(item.datasetIndex));
|
||||||
}
|
}
|
||||||
chart.value.update();
|
chart.value.update();
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,8 +41,8 @@ const { modelValue } = toRefs(props);
|
||||||
const v = ref(modelValue.value);
|
const v = ref(modelValue.value);
|
||||||
const inputEl = shallowRef<HTMLElement>();
|
const inputEl = shallowRef<HTMLElement>();
|
||||||
|
|
||||||
const onInput = (ev: KeyboardEvent) => {
|
const onInput = () => {
|
||||||
emit('update:modelValue', v.value);
|
emit('update:modelValue', v.value ?? '');
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -44,8 +44,8 @@ onMounted(() => {
|
||||||
let left = props.ev.pageX + 1; // 間違って右ダブルクリックした場合に意図せずアイテムがクリックされるのを防ぐため + 1
|
let left = props.ev.pageX + 1; // 間違って右ダブルクリックした場合に意図せずアイテムがクリックされるのを防ぐため + 1
|
||||||
let top = props.ev.pageY + 1; // 間違って右ダブルクリックした場合に意図せずアイテムがクリックされるのを防ぐため + 1
|
let top = props.ev.pageY + 1; // 間違って右ダブルクリックした場合に意図せずアイテムがクリックされるのを防ぐため + 1
|
||||||
|
|
||||||
const width = rootEl.value.offsetWidth;
|
const width = rootEl.value!.offsetWidth;
|
||||||
const height = rootEl.value.offsetHeight;
|
const height = rootEl.value!.offsetHeight;
|
||||||
|
|
||||||
if (left + width - window.pageXOffset >= (window.innerWidth - SCROLLBAR_THICKNESS)) {
|
if (left + width - window.pageXOffset >= (window.innerWidth - SCROLLBAR_THICKNESS)) {
|
||||||
left = (window.innerWidth - SCROLLBAR_THICKNESS) - width + window.pageXOffset;
|
left = (window.innerWidth - SCROLLBAR_THICKNESS) - width + window.pageXOffset;
|
||||||
|
@ -63,8 +63,10 @@ onMounted(() => {
|
||||||
left = 0;
|
left = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
rootEl.value.style.top = `${top}px`;
|
if (rootEl.value) {
|
||||||
rootEl.value.style.left = `${left}px`;
|
rootEl.value.style.top = `${top}px`;
|
||||||
|
rootEl.value.style.left = `${left}px`;
|
||||||
|
}
|
||||||
|
|
||||||
document.body.addEventListener('mousedown', onMousedown);
|
document.body.addEventListener('mousedown', onMousedown);
|
||||||
});
|
});
|
||||||
|
|
|
@ -10,6 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
|
import type { PollEditorModelValue } from '@/components/MkPollEditor.vue';
|
||||||
import { concat } from '@/scripts/array.js';
|
import { concat } from '@/scripts/array.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
|
@ -17,22 +18,9 @@ import MkButton from '@/components/MkButton.vue';
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
modelValue: boolean;
|
modelValue: boolean;
|
||||||
text: string | null;
|
text: string | null;
|
||||||
renote: Misskey.entities.Note | null;
|
renote?: Misskey.entities.Note | null;
|
||||||
files: Misskey.entities.DriveFile[];
|
files?: Misskey.entities.DriveFile[];
|
||||||
poll?: {
|
poll?: Misskey.entities.Note['poll'] | PollEditorModelValue | null;
|
||||||
expiresAt: string | null;
|
|
||||||
multiple: boolean;
|
|
||||||
choices: {
|
|
||||||
isVoted: boolean;
|
|
||||||
text: string;
|
|
||||||
votes: number;
|
|
||||||
}[];
|
|
||||||
} | {
|
|
||||||
choices: string[];
|
|
||||||
multiple: boolean;
|
|
||||||
expiresAt: string | null;
|
|
||||||
expiredAfter: string | null;
|
|
||||||
};
|
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
|
@ -43,7 +31,7 @@ const label = computed(() => {
|
||||||
return concat([
|
return concat([
|
||||||
props.text ? [i18n.tsx._cw.chars({ count: props.text.length })] : [],
|
props.text ? [i18n.tsx._cw.chars({ count: props.text.length })] : [],
|
||||||
props.renote ? [i18n.ts.quote] : [],
|
props.renote ? [i18n.ts.quote] : [],
|
||||||
props.files.length !== 0 ? [i18n.tsx._cw.files({ count: props.files.length })] : [],
|
props.files && props.files.length !== 0 ? [i18n.tsx._cw.files({ count: props.files.length })] : [],
|
||||||
props.poll != null ? [i18n.ts.poll] : [],
|
props.poll != null ? [i18n.ts.poll] : [],
|
||||||
] as string[][]).join(' / ');
|
] as string[][]).join(' / ');
|
||||||
});
|
});
|
||||||
|
|
|
@ -118,34 +118,36 @@ export default defineComponent({
|
||||||
return children;
|
return children;
|
||||||
};
|
};
|
||||||
|
|
||||||
function onBeforeLeave(el: HTMLElement) {
|
function onBeforeLeave(element: Element) {
|
||||||
|
const el = element as HTMLElement;
|
||||||
el.style.top = `${el.offsetTop}px`;
|
el.style.top = `${el.offsetTop}px`;
|
||||||
el.style.left = `${el.offsetLeft}px`;
|
el.style.left = `${el.offsetLeft}px`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function onLeaveCanceled(el: HTMLElement) {
|
function onLeaveCancelled(element: Element) {
|
||||||
|
const el = element as HTMLElement;
|
||||||
el.style.top = '';
|
el.style.top = '';
|
||||||
el.style.left = '';
|
el.style.left = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => h(
|
// eslint-disable-next-line vue/no-setup-props-destructure
|
||||||
defaultStore.state.animation ? TransitionGroup : 'div',
|
const classes = {
|
||||||
{
|
[$style['date-separated-list']]: true,
|
||||||
class: {
|
[$style['date-separated-list-nogap']]: props.noGap,
|
||||||
[$style['date-separated-list']]: true,
|
[$style['reversed']]: props.reversed,
|
||||||
[$style['date-separated-list-nogap']]: props.noGap,
|
[$style['direction-down']]: props.direction === 'down',
|
||||||
[$style['reversed']]: props.reversed,
|
[$style['direction-up']]: props.direction === 'up',
|
||||||
[$style['direction-down']]: props.direction === 'down',
|
};
|
||||||
[$style['direction-up']]: props.direction === 'up',
|
|
||||||
},
|
return () => defaultStore.state.animation ? h(TransitionGroup, {
|
||||||
...(defaultStore.state.animation ? {
|
class: classes,
|
||||||
name: 'list',
|
name: 'list',
|
||||||
tag: 'div',
|
tag: 'div',
|
||||||
onBeforeLeave,
|
onBeforeLeave,
|
||||||
onLeaveCanceled,
|
onLeaveCancelled,
|
||||||
} : {}),
|
}, { default: renderChildren }) : h('div', {
|
||||||
},
|
class: classes,
|
||||||
{ default: renderChildren });
|
}, { default: renderChildren });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -31,8 +31,8 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<MkInput v-if="input" v-model="inputValue" autofocus :type="input.type || 'text'" :placeholder="input.placeholder || undefined" :autocomplete="input.autocomplete" @keydown="onInputKeydown">
|
<MkInput v-if="input" v-model="inputValue" autofocus :type="input.type || 'text'" :placeholder="input.placeholder || undefined" :autocomplete="input.autocomplete" @keydown="onInputKeydown">
|
||||||
<template v-if="input.type === 'password'" #prefix><i class="ti ti-lock"></i></template>
|
<template v-if="input.type === 'password'" #prefix><i class="ti ti-lock"></i></template>
|
||||||
<template #caption>
|
<template #caption>
|
||||||
<span v-if="okButtonDisabledReason === 'charactersExceeded'" v-text="i18n.tsx._dialog.charactersExceeded({ current: (inputValue as string).length, max: input.maxLength ?? 'NaN' })"/>
|
<span v-if="okButtonDisabledReason === 'charactersExceeded'" v-text="i18n.tsx._dialog.charactersExceeded({ current: (inputValue as string)?.length ?? 0, max: input.maxLength ?? 'NaN' })"/>
|
||||||
<span v-else-if="okButtonDisabledReason === 'charactersBelow'" v-text="i18n.tsx._dialog.charactersBelow({ current: (inputValue as string).length, min: input.minLength ?? 'NaN' })"/>
|
<span v-else-if="okButtonDisabledReason === 'charactersBelow'" v-text="i18n.tsx._dialog.charactersBelow({ current: (inputValue as string)?.length ?? 0, min: input.minLength ?? 'NaN' })"/>
|
||||||
</template>
|
</template>
|
||||||
</MkInput>
|
</MkInput>
|
||||||
<MkSelect v-if="select" v-model="selectedValue" autofocus>
|
<MkSelect v-if="select" v-model="selectedValue" autofocus>
|
||||||
|
@ -128,7 +128,7 @@ const mkresult= ref(false)
|
||||||
const okButtonDisabledReason = computed<null | 'charactersExceeded' | 'charactersBelow'>(() => {
|
const okButtonDisabledReason = computed<null | 'charactersExceeded' | 'charactersBelow'>(() => {
|
||||||
if (props.input) {
|
if (props.input) {
|
||||||
if (props.input.minLength) {
|
if (props.input.minLength) {
|
||||||
if ((inputValue.value || inputValue.value === '') && (inputValue.value as string).length < props.input.minLength) {
|
if (inputValue.value == null || (inputValue.value as string).length < props.input.minLength) {
|
||||||
return 'charactersBelow';
|
return 'charactersBelow';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,7 @@ import { $i } from '@/account.js';
|
||||||
import { getDriveFileMenu, getDriveMultiFileMenu } from '@/scripts/get-drive-file-menu.js';
|
import { getDriveFileMenu, getDriveMultiFileMenu } from '@/scripts/get-drive-file-menu.js';
|
||||||
import { isTouchUsing } from '@/scripts/touch.js';
|
import { isTouchUsing } from '@/scripts/touch.js';
|
||||||
import { deviceKind } from '@/scripts/device-kind.js';
|
import { deviceKind } from '@/scripts/device-kind.js';
|
||||||
import { useRouter } from '@/global/router/supplier.js';
|
import { useRouter } from '@/router/supplier.js';
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
|
|
|
@ -215,7 +215,7 @@ function onDragend() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function go() {
|
function go() {
|
||||||
emit('move', props.folder.id);
|
emit('move', props.folder);
|
||||||
}
|
}
|
||||||
|
|
||||||
function rename() {
|
function rename() {
|
||||||
|
|
|
@ -123,6 +123,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
import { nextTick, onActivated, onBeforeUnmount, onMounted, ref, shallowRef, watch } from 'vue';
|
import { nextTick, onActivated, onBeforeUnmount, onMounted, ref, shallowRef, watch } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import MkButton from './MkButton.vue';
|
import MkButton from './MkButton.vue';
|
||||||
|
import type { MenuItem } from '@/types/menu.js';
|
||||||
import XNavFolder from '@/components/MkDrive.navFolder.vue';
|
import XNavFolder from '@/components/MkDrive.navFolder.vue';
|
||||||
import XFolder from '@/components/MkDrive.folder.vue';
|
import XFolder from '@/components/MkDrive.folder.vue';
|
||||||
import XFile from '@/components/MkDrive.file.vue';
|
import XFile from '@/components/MkDrive.file.vue';
|
||||||
|
@ -455,7 +456,7 @@ function chooseFolder(folderToChoose: Misskey.entities.DriveFolder) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function move(target?: Misskey.entities.DriveFolder) {
|
function move(target?: Misskey.entities.DriveFolder | Misskey.entities.DriveFolder['id' | 'parentId']) {
|
||||||
if (!target) {
|
if (!target) {
|
||||||
goRoot();
|
goRoot();
|
||||||
return;
|
return;
|
||||||
|
@ -642,7 +643,7 @@ function fetchMoreFiles() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMenu() {
|
function getMenu() {
|
||||||
return [{
|
const menu: MenuItem[] = [{
|
||||||
type: 'switch',
|
type: 'switch',
|
||||||
text: i18n.ts.keepOriginalUploading,
|
text: i18n.ts.keepOriginalUploading,
|
||||||
ref: keepOriginal,
|
ref: keepOriginal,
|
||||||
|
@ -668,7 +669,7 @@ function getMenu() {
|
||||||
text: i18n.ts.renameFolder,
|
text: i18n.ts.renameFolder,
|
||||||
icon: 'ti ti-forms',
|
icon: 'ti ti-forms',
|
||||||
action: () => {
|
action: () => {
|
||||||
renameFolder(folder.value);
|
if (folder.value)renameFolder(folder.value);
|
||||||
},
|
},
|
||||||
} : undefined, folder.value ? {
|
} : undefined, folder.value ? {
|
||||||
text: i18n.ts.deleteFolder,
|
text: i18n.ts.deleteFolder,
|
||||||
|
@ -683,6 +684,8 @@ function getMenu() {
|
||||||
createFolder();
|
createFolder();
|
||||||
},
|
},
|
||||||
}];
|
}];
|
||||||
|
|
||||||
|
return menu;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function isSensitive(Files, isSensitive: boolean) {
|
async function isSensitive(Files, isSensitive: boolean) {
|
||||||
|
|
|
@ -4,64 +4,64 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<!-- このコンポーネントの要素のclassは親から利用されるのでむやみに弄らないこと -->
|
<!-- このコンポーネントの要素のclassは親から利用されるのでむやみに弄らないこと -->
|
||||||
<!-- フォルダの中にはカスタム絵文字だけ(Unicode絵文字もこっち) -->
|
<!-- フォルダの中にはカスタム絵文字だけ(Unicode絵文字もこっち) -->
|
||||||
<section v-if="!hasChildSection" style="border-radius: 6px;">
|
<section v-if="!hasChildSection" v-panel style="border-radius: 6px; border-bottom: 0.5px solid var(--divider);">
|
||||||
<header class="_acrylic" @click="shown = !shown" >
|
<header class="_acrylic" @click="shown = !shown">
|
||||||
<i class="toggle ti-fw" :class="shown ? 'ti ti-chevron-down' : 'ti ti-chevron-up'"></i> <slot></slot> ({{ emojis.length }})
|
<i class="toggle ti-fw" :class="shown ? 'ti ti-chevron-down' : 'ti ti-chevron-up'"></i> <slot></slot> (<i class="ti ti-icons"></i>:{{ emojis.length }})
|
||||||
</header>
|
</header>
|
||||||
<div v-if="shown" class="body">
|
<div v-if="shown" class="body">
|
||||||
<button
|
<button
|
||||||
v-for="emoji in emojis"
|
v-for="emoji in emojis"
|
||||||
:key="emoji"
|
:key="emoji"
|
||||||
:data-emoji="emoji"
|
:data-emoji="emoji"
|
||||||
class="_button item"
|
class="_button item"
|
||||||
@pointerenter="computeButtonTitle"
|
@pointerenter="computeButtonTitle"
|
||||||
@click="emit('chosen', emoji, $event)"
|
@click="emit('chosen', emoji, $event)"
|
||||||
>
|
>
|
||||||
<MkCustomEmoji v-if="emoji[0] === ':'" class="emoji" :name="emoji" :normal="true"/>
|
<MkCustomEmoji v-if="emoji[0] === ':'" class="emoji" :name="emoji" :normal="true"/>
|
||||||
<MkEmoji v-else class="emoji" :emoji="emoji" :normal="true"/>
|
<MkEmoji v-else class="emoji" :emoji="emoji" :normal="true"/>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<!-- フォルダの中にはカスタム絵文字やフォルダがある -->
|
<!-- フォルダの中にはカスタム絵文字やフォルダがある -->
|
||||||
<section v-else style="border-radius: 6px; padding-top: 9px;">
|
<section v-else v-panel style="border-radius: 6px; border-bottom: 0.5px solid var(--divider);">
|
||||||
<header class="_acrylic" @click="shown = !shown">
|
<header class="_acrylic" @click="shown = !shown">
|
||||||
<i class="toggle ti-fw" :class="shown ? 'ti ti-chevron-down' : 'ti ti-chevron-up'"></i> <slot></slot> (フォルダー)
|
<i class="toggle ti-fw" :class="shown ? 'ti ti-chevron-down' : 'ti ti-chevron-up'"></i> <slot></slot> (<i class="ti ti-folder ti-fw"></i>:{{ customEmojiTree?.length }} <i class="ti ti-icons ti-fw"></i>:{{ emojis.length }})
|
||||||
</header>
|
</header>
|
||||||
<div v-if="shown" style="padding-left: 9px; ">
|
<div v-if="shown" style="padding-left: 9px;">
|
||||||
<MkEmojiPickerSection
|
<MkEmojiPickerSection
|
||||||
v-for="child in customEmojiTree"
|
v-for="child in customEmojiTree"
|
||||||
:key="`custom:${child.value}`"
|
:key="`custom:${child.value}`"
|
||||||
:initialShown="initialShown"
|
:initialShown="initialShown"
|
||||||
:emojis="computed(() => customEmojis.filter(e => e.category === child.category || e.category === child.category+'/'+child.category).map(e => `:${e.name}:`))"
|
:emojis="computed(() => customEmojis.filter(e => e.category === child.category).map(e => `:${e.name}:`))"
|
||||||
:hasChildSection="child.children.length !== 0"
|
:hasChildSection="child.children.length !== 0"
|
||||||
:customEmojiTree="child.children"
|
:customEmojiTree="child.children"
|
||||||
@chosen="nestedChosen"
|
@chosen="nestedChosen"
|
||||||
>
|
>
|
||||||
{{ child.value || i18n.ts.other }}
|
{{ child.value || i18n.ts.other }}
|
||||||
</MkEmojiPickerSection>
|
</MkEmojiPickerSection>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="shown" class="body">
|
<div v-if="shown" class="body">
|
||||||
<button
|
<button
|
||||||
v-for="emoji in emojis"
|
v-for="emoji in emojis"
|
||||||
:key="emoji"
|
:key="emoji"
|
||||||
:data-emoji="emoji"
|
:data-emoji="emoji"
|
||||||
class="_button item"
|
class="_button item"
|
||||||
@pointerenter="computeButtonTitle"
|
@pointerenter="computeButtonTitle"
|
||||||
@click="emit('chosen', emoji, $event)"
|
@click="emit('chosen', emoji, $event)"
|
||||||
>
|
>
|
||||||
<MkCustomEmoji v-if="emoji[0] === ':'" class="emoji" :name="emoji" :normal="true"/>
|
<MkCustomEmoji v-if="emoji[0] === ':'" class="emoji" :name="emoji" :normal="true"/>
|
||||||
<MkEmoji v-else class="emoji" :emoji="emoji" :normal="true"/>
|
<MkEmoji v-else class="emoji" :emoji="emoji" :normal="true"/>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, computed, Ref } from 'vue';
|
import { ref, computed, Ref } from 'vue';
|
||||||
import { CustomEmojiFolderTree, getEmojiName } from '@/scripts/emojilist.js';
|
import { CustomEmojiFolderTree, getEmojiName } from '@/scripts/emojilist.js';
|
||||||
import { i18n } from '../i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { customEmojis } from '@/custom-emojis.js';
|
import { customEmojis } from '@/custom-emojis.js';
|
||||||
import MkEmojiPickerSection from '@/components/MkEmojiPicker.section.vue';
|
import MkEmojiPickerSection from '@/components/MkEmojiPicker.section.vue';
|
||||||
|
|
||||||
|
@ -87,7 +87,7 @@ function computeButtonTitle(ev: MouseEvent): void {
|
||||||
elm.title = getEmojiName(emoji) ?? emoji;
|
elm.title = getEmojiName(emoji) ?? emoji;
|
||||||
}
|
}
|
||||||
|
|
||||||
function nestedChosen(emoji: any, ev?: MouseEvent) {
|
function nestedChosen(emoji: any, ev: MouseEvent) {
|
||||||
emit('chosen', emoji, ev);
|
emit('chosen', emoji, ev);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -36,7 +36,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<div v-if="tab === 'index'" class="group index">
|
<div v-if="tab === 'index'" class="group index">
|
||||||
<section v-if="showPinned">
|
<section v-if="showPinned && (pinned && pinned.length > 0)">
|
||||||
<div style="display: flex; ">
|
<div style="display: flex; ">
|
||||||
<div v-for="a in profileMax" :key="a" :title="defaultStore.state[`pickerProfileName${a > 1 ? a - 1 : ''}`]" class="sllfktkhgl" :class="{ active: activeIndex === a || isDefaultProfile === a }" @click="pinnedProfileSelect(a)">
|
<div v-for="a in profileMax" :key="a" :title="defaultStore.state[`pickerProfileName${a > 1 ? a - 1 : ''}`]" class="sllfktkhgl" :class="{ active: activeIndex === a || isDefaultProfile === a }" @click="pinnedProfileSelect(a)">
|
||||||
{{ defaultStore.state[`pickerProfileName${a > 1 ? a - 1 : ''}`] }}
|
{{ defaultStore.state[`pickerProfileName${a > 1 ? a - 1 : ''}`] }}
|
||||||
|
@ -349,7 +349,7 @@ watch(q, () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
function filterAvailable(emoji: Misskey.entities.EmojiSimple): boolean {
|
function filterAvailable(emoji: Misskey.entities.EmojiSimple): boolean {
|
||||||
return (emoji.roleIdsThatCanBeUsedThisEmojiAsReaction == null || emoji.roleIdsThatCanBeUsedThisEmojiAsReaction.length === 0) || ($i && $i.roles.some(r => emoji.roleIdsThatCanBeUsedThisEmojiAsReaction.includes(r.id)));
|
return ((emoji.roleIdsThatCanBeUsedThisEmojiAsReaction == null || emoji.roleIdsThatCanBeUsedThisEmojiAsReaction.length === 0) || ($i && $i.roles.some(r => emoji.roleIdsThatCanBeUsedThisEmojiAsReaction?.includes(r.id)))) ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function focus() {
|
function focus() {
|
||||||
|
|
|
@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
:withOkButton="true"
|
:withOkButton="true"
|
||||||
:okButtonDisabled="false"
|
:okButtonDisabled="false"
|
||||||
@ok="ok()"
|
@ok="ok()"
|
||||||
@close="dialog.close()"
|
@close="dialog?.close()"
|
||||||
@closed="emit('closed')"
|
@closed="emit('closed')"
|
||||||
>
|
>
|
||||||
<template #header>{{ i18n.ts.describeFile }}</template>
|
<template #header>{{ i18n.ts.describeFile }}</template>
|
||||||
|
@ -48,6 +48,6 @@ const caption = ref(props.default);
|
||||||
|
|
||||||
async function ok() {
|
async function ok() {
|
||||||
emit('done', caption.value);
|
emit('done', caption.value);
|
||||||
dialog.value.close();
|
dialog.value?.close();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<div>
|
<div>
|
||||||
<MkPagination v-slot="{items}" :pagination="pagination" class="urempief" :class="{ grid: viewMode === 'grid' }">
|
<MkPagination v-slot="{items}" :pagination="pagination" class="urempief" :class="{ grid: viewMode === 'grid' }">
|
||||||
<MkA
|
<MkA
|
||||||
v-for="file in items"
|
v-for="file in (items as Misskey.entities.DriveFile[])"
|
||||||
:key="file.id"
|
:key="file.id"
|
||||||
v-tooltip.mfm="`${file.type}\n${bytes(file.size)}\n${dateString(file.createdAt)}\nby ${file.user ? '@' + Misskey.acct.toString(file.user) : 'system'}`"
|
v-tooltip.mfm="`${file.type}\n${bytes(file.size)}\n${dateString(file.createdAt)}\nby ${file.user ? '@' + Misskey.acct.toString(file.user) : 'system'}`"
|
||||||
:to="`/admin/file/${file.id}`"
|
:to="`/admin/file/${file.id}`"
|
||||||
|
|
|
@ -33,8 +33,9 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { nextTick, onMounted, shallowRef, ref } from 'vue';
|
import { nextTick, onMounted, shallowRef, ref, watch } from 'vue';
|
||||||
import { defaultStore } from '@/store.js';
|
import { defaultStore } from '@/store.js';
|
||||||
|
const miLocalStoragePrefix = 'ui:folder:' as const;
|
||||||
|
|
||||||
const props = withDefaults(defineProps<{
|
const props = withDefaults(defineProps<{
|
||||||
defaultOpen?: boolean;
|
defaultOpen?: boolean;
|
||||||
|
@ -43,41 +44,53 @@ const props = withDefaults(defineProps<{
|
||||||
defaultOpen: true,
|
defaultOpen: true,
|
||||||
maxHeight: null,
|
maxHeight: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
const getBgColor = (el: HTMLElement) => {
|
const getBgColor = (el: HTMLElement) => {
|
||||||
const style = window.getComputedStyle(el);
|
const style = window.getComputedStyle(el);
|
||||||
if (style.backgroundColor && !['rgba(0, 0, 0, 0)', 'rgba(0,0,0,0)', 'transparent'].includes(style.backgroundColor)) {
|
if (style.backgroundColor && !['rgba(0, 0, 0, 0)', 'rgba(0,0,0,0)', 'transparent'].includes(style.backgroundColor)) {
|
||||||
return style.backgroundColor;
|
return style.backgroundColor;
|
||||||
} else {
|
} else {
|
||||||
return el.parentElement ? getBgColor(el.parentElement) : 'transparent';
|
return el.parentElement ? getBgColor(el.parentElement) : 'transparent';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
const rootEl = shallowRef<HTMLDivElement>();
|
||||||
|
const bg = ref<string>();
|
||||||
|
const showBody = ref((props.persistKey && miLocalStorage.getItem(`${miLocalStoragePrefix}${props.persistKey}`)) ? (miLocalStorage.getItem(`${miLocalStoragePrefix}${props.persistKey}`) === 't') : props.expanded);
|
||||||
|
|
||||||
|
watch(showBody, () => {
|
||||||
|
if (props.persistKey) {
|
||||||
|
miLocalStorage.setItem(`${miLocalStoragePrefix}${props.persistKey}`, showBody.value ? 't' : 'f');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const rootEl = shallowRef<HTMLElement>();
|
const rootEl = shallowRef<HTMLElement>();
|
||||||
const bgSame = ref(false);
|
const bgSame = ref(false);
|
||||||
const opened = ref(props.defaultOpen);
|
const opened = ref(props.defaultOpen);
|
||||||
const openedAtLeastOnce = ref(props.defaultOpen);
|
const openedAtLeastOnce = ref(props.defaultOpen);
|
||||||
|
|
||||||
function enter(el) {
|
function enter(element: Element) {
|
||||||
|
const el = element as HTMLElement;
|
||||||
const elementHeight = el.getBoundingClientRect().height;
|
const elementHeight = el.getBoundingClientRect().height;
|
||||||
el.style.height = 0;
|
el.style.height = '0';
|
||||||
el.offsetHeight; // reflow
|
el.offsetHeight; // reflow
|
||||||
el.style.height = Math.min(elementHeight, props.maxHeight ?? Infinity) + 'px';
|
el.style.height = Math.min(elementHeight, props.maxHeight ?? Infinity) + 'px';
|
||||||
}
|
}
|
||||||
|
|
||||||
function afterEnter(el) {
|
function afterEnter(element: Element) {
|
||||||
el.style.height = null;
|
const el = element as HTMLElement;
|
||||||
|
el.style.height = 'unset';
|
||||||
}
|
}
|
||||||
|
|
||||||
function leave(el) {
|
function leave(element: Element) {
|
||||||
|
const el = element as HTMLElement;
|
||||||
const elementHeight = el.getBoundingClientRect().height;
|
const elementHeight = el.getBoundingClientRect().height;
|
||||||
el.style.height = elementHeight + 'px';
|
el.style.height = elementHeight + 'px';
|
||||||
el.offsetHeight; // reflow
|
el.offsetHeight; // reflow
|
||||||
el.style.height = 0;
|
el.style.height = '0';
|
||||||
}
|
}
|
||||||
|
|
||||||
function afterLeave(el) {
|
function afterLeave(element: Element) {
|
||||||
el.style.height = null;
|
const el = element as HTMLElement;
|
||||||
|
el.style.height = 'unset';
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggle() {
|
function toggle() {
|
||||||
|
@ -91,10 +104,23 @@ function toggle() {
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
const computedStyle = getComputedStyle(document.documentElement);
|
function getParentBg(el?: HTMLElement | null): string {
|
||||||
const parentBg = getBgColor(rootEl.value.parentElement);
|
if (el == null || el.tagName === 'BODY') return 'var(--bg)';
|
||||||
const myBg = computedStyle.getPropertyValue('--panel');
|
const background = el.style.background || el.style.backgroundColor;
|
||||||
bgSame.value = parentBg === myBg;
|
if (background) {
|
||||||
|
return background;
|
||||||
|
} else {
|
||||||
|
return getParentBg(el.parentElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const computedStyle = getComputedStyle(document.documentElement);
|
||||||
|
const parentBg = getBgColor(rootEl.value.parentElement);
|
||||||
|
const myBg = computedStyle.getPropertyValue('--panel');
|
||||||
|
bgSame.value = parentBg === myBg;
|
||||||
|
const rawBg = getParentBg(rootEl.value);
|
||||||
|
const _bg = tinycolor(rawBg.startsWith('var(') ? getComputedStyle(document.documentElement).getPropertyValue(rawBg.slice(4, -1)) : rawBg);
|
||||||
|
_bg.setAlpha(0.85);
|
||||||
|
bg.value = _bg.toRgbString();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue