Compare commits
18 Commits
3379b3e0f4
...
53818622be
Author | SHA1 | Date |
---|---|---|
|
53818622be | |
|
4d562e7439 | |
|
30df768d26 | |
|
ebd06becbf | |
|
cf35208777 | |
|
d1eddf0d88 | |
|
d5ad953c9e | |
|
e339293673 | |
|
a1ca68aadd | |
|
420365c17f | |
|
dc608aada0 | |
|
231c2c2e54 | |
|
2f8d02024a | |
|
4b98b446be | |
|
026ec40b3b | |
|
54fc232a23 | |
|
a3cc865e11 | |
|
d7b443d1f0 |
|
@ -7,7 +7,9 @@
|
||||||
"ghcr.io/devcontainers/features/node:1": {
|
"ghcr.io/devcontainers/features/node:1": {
|
||||||
"version": "22.11.0"
|
"version": "22.11.0"
|
||||||
},
|
},
|
||||||
"ghcr.io/devcontainers-contrib/features/corepack:1": {}
|
"ghcr.io/devcontainers-extra/features/corepack:1": {
|
||||||
|
"version": "0.31.0"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"forwardPorts": [3000],
|
"forwardPorts": [3000],
|
||||||
"postCreateCommand": "/bin/bash .devcontainer/init.sh",
|
"postCreateCommand": "/bin/bash .devcontainer/init.sh",
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
# If someone with write access comments "/ok-to-test" on a pull request, emit a repository_dispatch event
|
|
||||||
name: Ok To Test
|
|
||||||
|
|
||||||
on:
|
|
||||||
issue_comment:
|
|
||||||
types: [created]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
ok-to-test:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
# Only run for PRs, not issue comments
|
|
||||||
if: ${{ github.event.issue.pull_request }}
|
|
||||||
steps:
|
|
||||||
# Generate a GitHub App installation access token from an App ID and private key
|
|
||||||
# To create a new GitHub App:
|
|
||||||
# https://developer.github.com/apps/building-github-apps/creating-a-github-app/
|
|
||||||
# See app.yml for an example app manifest
|
|
||||||
- name: Generate token
|
|
||||||
id: generate_token
|
|
||||||
uses: tibdex/github-app-token@v2
|
|
||||||
with:
|
|
||||||
app_id: ${{ secrets.DEPLOYBOT_APP_ID }}
|
|
||||||
private_key: ${{ secrets.DEPLOYBOT_PRIVATE_KEY }}
|
|
||||||
|
|
||||||
- name: Slash Command Dispatch
|
|
||||||
uses: peter-evans/slash-command-dispatch@v4
|
|
||||||
env:
|
|
||||||
TOKEN: ${{ steps.generate_token.outputs.token }}
|
|
||||||
with:
|
|
||||||
token: ${{ env.TOKEN }} # GitHub App installation access token
|
|
||||||
# token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} # PAT or OAuth token will also work
|
|
||||||
reaction-token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
issue-type: pull-request
|
|
||||||
commands: deploy
|
|
||||||
named-args: true
|
|
||||||
permission: write
|
|
|
@ -1,92 +0,0 @@
|
||||||
# Run secret-dependent integration tests only after /deploy approval
|
|
||||||
on:
|
|
||||||
repository_dispatch:
|
|
||||||
types: [deploy-command]
|
|
||||||
|
|
||||||
name: Deploy preview environment
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
# Repo owner has commented /deploy on a (fork-based) pull request
|
|
||||||
deploy-preview-environment:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if:
|
|
||||||
github.event.client_payload.slash_command.sha != '' &&
|
|
||||||
contains(github.event.client_payload.pull_request.head.sha, github.event.client_payload.slash_command.sha)
|
|
||||||
steps:
|
|
||||||
- uses: actions/github-script@v7.0.1
|
|
||||||
id: check-id
|
|
||||||
env:
|
|
||||||
number: ${{ github.event.client_payload.pull_request.number }}
|
|
||||||
job: ${{ github.job }}
|
|
||||||
with:
|
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
result-encoding: string
|
|
||||||
script: |
|
|
||||||
const { data: pull } = await github.rest.pulls.get({
|
|
||||||
...context.repo,
|
|
||||||
pull_number: process.env.number
|
|
||||||
});
|
|
||||||
const ref = pull.head.sha;
|
|
||||||
|
|
||||||
const { data: checks } = await github.rest.checks.listForRef({
|
|
||||||
...context.repo,
|
|
||||||
ref
|
|
||||||
});
|
|
||||||
|
|
||||||
const check = checks.check_runs.filter(c => c.name === process.env.job);
|
|
||||||
|
|
||||||
return check[0].id;
|
|
||||||
|
|
||||||
- uses: actions/github-script@v7.0.1
|
|
||||||
env:
|
|
||||||
check_id: ${{ steps.check-id.outputs.result }}
|
|
||||||
details_url: ${{ github.server_url }}/${{ github.repository }}/runs/${{ github.run_id }}
|
|
||||||
with:
|
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
script: |
|
|
||||||
await github.rest.checks.update({
|
|
||||||
...context.repo,
|
|
||||||
check_run_id: process.env.check_id,
|
|
||||||
status: 'in_progress',
|
|
||||||
details_url: process.env.details_url
|
|
||||||
});
|
|
||||||
|
|
||||||
# Check out merge commit
|
|
||||||
- name: Fork based /deploy checkout
|
|
||||||
uses: actions/checkout@v4.1.1
|
|
||||||
with:
|
|
||||||
ref: 'refs/pull/${{ github.event.client_payload.pull_request.number }}/merge'
|
|
||||||
|
|
||||||
# <insert integration tests needing secrets>
|
|
||||||
- name: Context
|
|
||||||
uses: okteto/context@latest
|
|
||||||
with:
|
|
||||||
token: ${{ secrets.OKTETO_TOKEN }}
|
|
||||||
|
|
||||||
- name: Deploy preview environment
|
|
||||||
uses: ikuradon/deploy-preview@latest
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
with:
|
|
||||||
name: pr-${{ github.event.client_payload.pull_request.number }}-syuilo
|
|
||||||
timeout: 15m
|
|
||||||
|
|
||||||
# Update check run called "integration-fork"
|
|
||||||
- uses: actions/github-script@v7.0.1
|
|
||||||
id: update-check-run
|
|
||||||
if: ${{ always() }}
|
|
||||||
env:
|
|
||||||
# Conveniently, job.status maps to https://developer.github.com/v3/checks/runs/#update-a-check-run
|
|
||||||
conclusion: ${{ job.status }}
|
|
||||||
check_id: ${{ steps.check-id.outputs.result }}
|
|
||||||
with:
|
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
script: |
|
|
||||||
const { data: result } = await github.rest.checks.update({
|
|
||||||
...context.repo,
|
|
||||||
check_run_id: process.env.check_id,
|
|
||||||
status: 'completed',
|
|
||||||
conclusion: process.env.conclusion
|
|
||||||
});
|
|
||||||
|
|
||||||
return result;
|
|
|
@ -1,54 +0,0 @@
|
||||||
# file: .github/workflows/preview-closed.yaml
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
types:
|
|
||||||
- closed
|
|
||||||
|
|
||||||
name: Destroy preview environment
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
destroy-preview-environment:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/github-script@v7.0.1
|
|
||||||
id: check-conclusion
|
|
||||||
env:
|
|
||||||
number: ${{ github.event.number }}
|
|
||||||
with:
|
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
result-encoding: string
|
|
||||||
script: |
|
|
||||||
const { data: pull } = await github.rest.pulls.get({
|
|
||||||
...context.repo,
|
|
||||||
pull_number: process.env.number
|
|
||||||
});
|
|
||||||
const ref = pull.head.sha;
|
|
||||||
|
|
||||||
const { data: checks } = await github.rest.checks.listForRef({
|
|
||||||
...context.repo,
|
|
||||||
ref
|
|
||||||
});
|
|
||||||
|
|
||||||
const check = checks.check_runs.filter(c => c.name === 'deploy-preview-environment');
|
|
||||||
|
|
||||||
if (check.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { data: result } = await github.rest.checks.get({
|
|
||||||
...context.repo,
|
|
||||||
check_run_id: check[0].id,
|
|
||||||
});
|
|
||||||
|
|
||||||
return result.conclusion;
|
|
||||||
- name: Context
|
|
||||||
if: steps.check-conclusion.outputs.result == 'success'
|
|
||||||
uses: okteto/context@latest
|
|
||||||
with:
|
|
||||||
token: ${{ secrets.OKTETO_TOKEN }}
|
|
||||||
|
|
||||||
- name: Destroy preview environment
|
|
||||||
if: steps.check-conclusion.outputs.result == 'success'
|
|
||||||
uses: okteto/destroy-preview@latest
|
|
||||||
with:
|
|
||||||
name: pr-${{ github.event.number }}-syuilo
|
|
11
CHANGELOG.md
11
CHANGELOG.md
|
@ -1,14 +1,21 @@
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
### General
|
### General
|
||||||
-
|
- Feat: アクセストークン発行時に通知するように
|
||||||
|
|
||||||
### Client
|
### Client
|
||||||
|
- Feat: 投稿フォームで画像をプレビュー可能に
|
||||||
- Enhance: 投稿フォームの「迷惑になる可能性があります」のダイアログを表示する条件においてCWを考慮するように
|
- Enhance: 投稿フォームの「迷惑になる可能性があります」のダイアログを表示する条件においてCWを考慮するように
|
||||||
- Enhance: アンテナ、リスト等の名前をカラム名のデフォルト値にするように `#13992`
|
- Enhance: アンテナ、リスト等の名前をカラム名のデフォルト値にするように `#13992`
|
||||||
|
- Enhance: クライアントエラー画面の多言語対応
|
||||||
|
- Enhance: 開発者モードでメニューからファイルIDをコピー出来るように `#15441'
|
||||||
|
- Enhance: ノートに埋め込まれたメディアのコンテキストメニューから管理者用のファイル管理画面を開けるように ( #15440 )
|
||||||
|
- Fix: コンディショナルロールを手動で割り当てできる導線を削除 `#13529`
|
||||||
|
- Fix: 埋め込みプレイヤーから外部ページに移動できない問題を修正
|
||||||
|
|
||||||
### Server
|
### Server
|
||||||
-
|
- Fix: `following/invalidate`でフォロワーを解除しようとしているユーザーの情報を返すように
|
||||||
|
- Fix: オブジェクトストレージの設定でPrefixを設定していなかった場合nullまたは空文字になる問題を修正
|
||||||
|
|
||||||
|
|
||||||
## 2025.2.0
|
## 2025.2.0
|
||||||
|
|
|
@ -9472,6 +9472,14 @@ export interface Locale extends ILocale {
|
||||||
* ログインがありました
|
* ログインがありました
|
||||||
*/
|
*/
|
||||||
"login": string;
|
"login": string;
|
||||||
|
/**
|
||||||
|
* アクセストークンが作成されました
|
||||||
|
*/
|
||||||
|
"createToken": string;
|
||||||
|
/**
|
||||||
|
* 心当たりがない場合は「{text}」を通じてアクセストークンを削除してください。
|
||||||
|
*/
|
||||||
|
"createTokenDescription": ParameterizedString<"text">;
|
||||||
"_types": {
|
"_types": {
|
||||||
/**
|
/**
|
||||||
* すべて
|
* すべて
|
||||||
|
@ -10944,6 +10952,52 @@ export interface Locale extends ILocale {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
"_bootErrors": {
|
||||||
|
/**
|
||||||
|
* 読み込みに失敗しました
|
||||||
|
*/
|
||||||
|
"title": string;
|
||||||
|
/**
|
||||||
|
* 少し待ってからリロードしてもまだ問題が解決されない場合、以下のError IDを添えてサーバー管理者に連絡してください。
|
||||||
|
*/
|
||||||
|
"serverError": string;
|
||||||
|
/**
|
||||||
|
* 以下を行うと解決する可能性があります。
|
||||||
|
*/
|
||||||
|
"solution": string;
|
||||||
|
/**
|
||||||
|
* ブラウザおよびOSを最新バージョンに更新する
|
||||||
|
*/
|
||||||
|
"solution1": string;
|
||||||
|
/**
|
||||||
|
* アドブロッカーを無効にする
|
||||||
|
*/
|
||||||
|
"solution2": string;
|
||||||
|
/**
|
||||||
|
* ブラウザのキャッシュをクリアする
|
||||||
|
*/
|
||||||
|
"solution3": string;
|
||||||
|
/**
|
||||||
|
* (Tor Browser) dom.webaudio.enabledをtrueに設定する
|
||||||
|
*/
|
||||||
|
"solution4": string;
|
||||||
|
/**
|
||||||
|
* その他のオプション
|
||||||
|
*/
|
||||||
|
"otherOption": string;
|
||||||
|
/**
|
||||||
|
* クライアント設定とキャッシュを削除
|
||||||
|
*/
|
||||||
|
"otherOption1": string;
|
||||||
|
/**
|
||||||
|
* 簡易クライアントを起動
|
||||||
|
*/
|
||||||
|
"otherOption2": string;
|
||||||
|
/**
|
||||||
|
* 修復ツールを起動
|
||||||
|
*/
|
||||||
|
"otherOption3": string;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
declare const locales: {
|
declare const locales: {
|
||||||
[lang: string]: Locale;
|
[lang: string]: Locale;
|
||||||
|
|
|
@ -2500,6 +2500,8 @@ _notification:
|
||||||
flushNotification: "通知の履歴をリセットする"
|
flushNotification: "通知の履歴をリセットする"
|
||||||
exportOfXCompleted: "{x}のエクスポートが完了しました"
|
exportOfXCompleted: "{x}のエクスポートが完了しました"
|
||||||
login: "ログインがありました"
|
login: "ログインがありました"
|
||||||
|
createToken: "アクセストークンが作成されました"
|
||||||
|
createTokenDescription: "心当たりがない場合は「{text}」を通じてアクセストークンを削除してください。"
|
||||||
|
|
||||||
_types:
|
_types:
|
||||||
all: "すべて"
|
all: "すべて"
|
||||||
|
@ -2927,3 +2929,16 @@ _captcha:
|
||||||
_unknown:
|
_unknown:
|
||||||
title: "CAPTCHAエラー"
|
title: "CAPTCHAエラー"
|
||||||
text: "想定外のエラーが発生しました。"
|
text: "想定外のエラーが発生しました。"
|
||||||
|
|
||||||
|
_bootErrors:
|
||||||
|
title: "読み込みに失敗しました"
|
||||||
|
serverError: "少し待ってからリロードしてもまだ問題が解決されない場合、以下のError IDを添えてサーバー管理者に連絡してください。"
|
||||||
|
solution: "以下を行うと解決する可能性があります。"
|
||||||
|
solution1: "ブラウザおよびOSを最新バージョンに更新する"
|
||||||
|
solution2: "アドブロッカーを無効にする"
|
||||||
|
solution3: "ブラウザのキャッシュをクリアする"
|
||||||
|
solution4: "(Tor Browser) dom.webaudio.enabledをtrueに設定する"
|
||||||
|
otherOption: "その他のオプション"
|
||||||
|
otherOption1: "クライアント設定とキャッシュを削除"
|
||||||
|
otherOption2: "簡易クライアントを起動"
|
||||||
|
otherOption3: "修復ツールを起動"
|
||||||
|
|
|
@ -173,7 +173,8 @@ export class DriveService {
|
||||||
?? `${ this.meta.objectStorageUseSSL ? 'https' : 'http' }://${ this.meta.objectStorageEndpoint }${ this.meta.objectStoragePort ? `:${this.meta.objectStoragePort}` : '' }/${ this.meta.objectStorageBucket }`;
|
?? `${ this.meta.objectStorageUseSSL ? 'https' : 'http' }://${ this.meta.objectStorageEndpoint }${ this.meta.objectStoragePort ? `:${this.meta.objectStoragePort}` : '' }/${ this.meta.objectStorageBucket }`;
|
||||||
|
|
||||||
// for original
|
// for original
|
||||||
const key = `${this.meta.objectStoragePrefix}/${randomUUID()}${ext}`;
|
const prefix = this.meta.objectStoragePrefix ? `${this.meta.objectStoragePrefix}/` : '';
|
||||||
|
const key = `${prefix}${randomUUID()}${ext}`;
|
||||||
const url = `${ baseUrl }/${ key }`;
|
const url = `${ baseUrl }/${ key }`;
|
||||||
|
|
||||||
// for alts
|
// for alts
|
||||||
|
@ -190,7 +191,7 @@ export class DriveService {
|
||||||
];
|
];
|
||||||
|
|
||||||
if (alts.webpublic) {
|
if (alts.webpublic) {
|
||||||
webpublicKey = `${this.meta.objectStoragePrefix}/webpublic-${randomUUID()}.${alts.webpublic.ext}`;
|
webpublicKey = `${prefix}webpublic-${randomUUID()}.${alts.webpublic.ext}`;
|
||||||
webpublicUrl = `${ baseUrl }/${ webpublicKey }`;
|
webpublicUrl = `${ baseUrl }/${ webpublicKey }`;
|
||||||
|
|
||||||
this.registerLogger.info(`uploading webpublic: ${webpublicKey}`);
|
this.registerLogger.info(`uploading webpublic: ${webpublicKey}`);
|
||||||
|
@ -198,7 +199,7 @@ export class DriveService {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (alts.thumbnail) {
|
if (alts.thumbnail) {
|
||||||
thumbnailKey = `${this.meta.objectStoragePrefix}/thumbnail-${randomUUID()}.${alts.thumbnail.ext}`;
|
thumbnailKey = `${prefix}thumbnail-${randomUUID()}.${alts.thumbnail.ext}`;
|
||||||
thumbnailUrl = `${ baseUrl }/${ thumbnailKey }`;
|
thumbnailUrl = `${ baseUrl }/${ thumbnailKey }`;
|
||||||
|
|
||||||
this.registerLogger.info(`uploading thumbnail: ${thumbnailKey}`);
|
this.registerLogger.info(`uploading thumbnail: ${thumbnailKey}`);
|
||||||
|
|
|
@ -90,6 +90,10 @@ export type MiNotification = {
|
||||||
type: 'login';
|
type: 'login';
|
||||||
id: string;
|
id: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
|
} | {
|
||||||
|
type: 'createToken';
|
||||||
|
id: string;
|
||||||
|
createdAt: string;
|
||||||
} | {
|
} | {
|
||||||
type: 'app';
|
type: 'app';
|
||||||
id: string;
|
id: string;
|
||||||
|
|
|
@ -332,6 +332,16 @@ export const packedNotificationSchema = {
|
||||||
enum: ['login'],
|
enum: ['login'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
}, {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
...baseSchema.properties,
|
||||||
|
type: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
enum: ['createToken'],
|
||||||
|
},
|
||||||
|
},
|
||||||
}, {
|
}, {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
|
|
|
@ -512,6 +512,7 @@ export const meta = {
|
||||||
},
|
},
|
||||||
federation: {
|
federation: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
enum: ['all', 'specified', 'none'],
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
},
|
},
|
||||||
federationHosts: {
|
federationHosts: {
|
||||||
|
|
|
@ -96,7 +96,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
|
|
||||||
await this.userFollowingService.unfollow(follower, followee);
|
await this.userFollowingService.unfollow(follower, followee);
|
||||||
|
|
||||||
return await this.userEntityService.pack(followee.id, me);
|
return await this.userEntityService.pack(follower.id, me);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
import type { AccessTokensRepository } from '@/models/_.js';
|
import type { AccessTokensRepository } from '@/models/_.js';
|
||||||
import { IdService } from '@/core/IdService.js';
|
import { IdService } from '@/core/IdService.js';
|
||||||
|
import { NotificationService } from '@/core/NotificationService.js';
|
||||||
import { secureRndstr } from '@/misc/secure-rndstr.js';
|
import { secureRndstr } from '@/misc/secure-rndstr.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
|
|
||||||
|
@ -50,6 +51,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
private accessTokensRepository: AccessTokensRepository,
|
private accessTokensRepository: AccessTokensRepository,
|
||||||
|
|
||||||
private idService: IdService,
|
private idService: IdService,
|
||||||
|
private notificationService: NotificationService,
|
||||||
) {
|
) {
|
||||||
super(meta, paramDef, async (ps, me) => {
|
super(meta, paramDef, async (ps, me) => {
|
||||||
// Generate access token
|
// Generate access token
|
||||||
|
@ -71,6 +73,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
permission: ps.permission,
|
permission: ps.permission,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// アクセストークンが生成されたことを通知
|
||||||
|
this.notificationService.createNotification(me.id, 'createToken', {});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
token: accessToken,
|
token: accessToken,
|
||||||
};
|
};
|
||||||
|
|
|
@ -210,9 +210,15 @@ export function genOpenapiSpec(config: Config, includeSelfRef = false) {
|
||||||
|
|
||||||
spec.paths['/' + endpoint.name] = {
|
spec.paths['/' + endpoint.name] = {
|
||||||
...(endpoint.meta.allowGet ? {
|
...(endpoint.meta.allowGet ? {
|
||||||
get: info,
|
get: {
|
||||||
|
...info,
|
||||||
|
operationId: 'get___' + info.operationId,
|
||||||
|
},
|
||||||
} : {}),
|
} : {}),
|
||||||
post: info,
|
post: {
|
||||||
|
...info,
|
||||||
|
operationId: 'post___' + info.operationId,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -114,13 +114,17 @@
|
||||||
if (document.readyState === 'loading') {
|
if (document.readyState === 'loading') {
|
||||||
await new Promise(resolve => window.addEventListener('DOMContentLoaded', resolve));
|
await new Promise(resolve => window.addEventListener('DOMContentLoaded', resolve));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const locale = JSON.parse(localStorage.getItem('locale') || '{}');
|
||||||
|
|
||||||
|
const title = locale?._bootErrors?.title || 'Failed to initialize Misskey';
|
||||||
|
const reload = locale?.reload || 'Reload';
|
||||||
|
|
||||||
document.body.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0" /><path d="M12 9v4" /><path d="M12 16v.01" /></svg>
|
document.body.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0" /><path d="M12 9v4" /><path d="M12 16v.01" /></svg>
|
||||||
<div class="message">読み込みに失敗しました</div>
|
<div class="message">${title}</div>
|
||||||
<div class="submessage">Failed to initialize Misskey</div>
|
|
||||||
<div class="submessage">Error Code: ${code}</div>
|
<div class="submessage">Error Code: ${code}</div>
|
||||||
<button onclick="location.reload(!0)">
|
<button onclick="location.reload(!0)">
|
||||||
<div>リロード</div>
|
<div>${reload}</div>
|
||||||
<div><small>Reload</small></div>
|
|
||||||
</button>`;
|
</button>`;
|
||||||
addStyle(`
|
addStyle(`
|
||||||
#misskey_app,
|
#misskey_app,
|
||||||
|
|
|
@ -151,6 +151,22 @@
|
||||||
await new Promise(resolve => window.addEventListener('DOMContentLoaded', resolve));
|
await new Promise(resolve => window.addEventListener('DOMContentLoaded', resolve));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const locale = JSON.parse(localStorage.getItem('locale') || '{}');
|
||||||
|
|
||||||
|
const messages = Object.assign({
|
||||||
|
title: 'Failed to initialize Misskey',
|
||||||
|
solution: 'The following actions may solve the problem.',
|
||||||
|
solution1: 'Update your os and browser',
|
||||||
|
solution2: 'Disable an adblocker',
|
||||||
|
solution3: 'Clear the browser cache',
|
||||||
|
solution4: '(Tor Browser) Set dom.webaudio.enabled to true',
|
||||||
|
otherOption: 'Other options',
|
||||||
|
otherOption1: 'Clear preferences and cache',
|
||||||
|
otherOption2: 'Start the simple client',
|
||||||
|
otherOption3: 'Start the repair tool',
|
||||||
|
}, locale?._bootErrors || {});
|
||||||
|
const reload = locale?.reload || 'Reload';
|
||||||
|
|
||||||
let errorsElement = document.getElementById('errors');
|
let errorsElement = document.getElementById('errors');
|
||||||
|
|
||||||
if (!errorsElement) {
|
if (!errorsElement) {
|
||||||
|
@ -160,32 +176,32 @@
|
||||||
<path d="M12 9v2m0 4v.01"></path>
|
<path d="M12 9v2m0 4v.01"></path>
|
||||||
<path d="M5 19h14a2 2 0 0 0 1.84 -2.75l-7.1 -12.25a2 2 0 0 0 -3.5 0l-7.1 12.25a2 2 0 0 0 1.75 2.75"></path>
|
<path d="M5 19h14a2 2 0 0 0 1.84 -2.75l-7.1 -12.25a2 2 0 0 0 -3.5 0l-7.1 12.25a2 2 0 0 0 1.75 2.75"></path>
|
||||||
</svg>
|
</svg>
|
||||||
<h1>Failed to load<br>読み込みに失敗しました</h1>
|
<h1>${messages.title}</h1>
|
||||||
<button class="button-big" onclick="location.reload(true);">
|
<button class="button-big" onclick="location.reload(true);">
|
||||||
<span class="button-label-big">Reload / リロード</span>
|
<span class="button-label-big">${reload}</span>
|
||||||
</button>
|
</button>
|
||||||
<p><b>The following actions may solve the problem. / 以下を行うと解決する可能性があります。</b></p>
|
<p><b>${messages.solution}</b></p>
|
||||||
<p>Update your os and browser / ブラウザおよびOSを最新バージョンに更新する</p>
|
<p>${messages.solution1}</p>
|
||||||
<p>Disable an adblocker / アドブロッカーを無効にする</p>
|
<p>${messages.solution2}</p>
|
||||||
<p>Clear the browser cache / ブラウザのキャッシュをクリアする</p>
|
<p>${messages.solution3}</p>
|
||||||
<p>(Tor Browser) Set dom.webaudio.enabled to true / dom.webaudio.enabledをtrueに設定する</p>
|
<p>${messages.solution4}</p>
|
||||||
<details style="color: #86b300;">
|
<details style="color: #86b300;">
|
||||||
<summary>Other options / その他のオプション</summary>
|
<summary>${messages.otherOption}</summary>
|
||||||
<a href="/flush">
|
<a href="/flush">
|
||||||
<button class="button-small">
|
<button class="button-small">
|
||||||
<span class="button-label-small">Clear preferences and cache</span>
|
<span class="button-label-small">${messages.otherOption1}</span>
|
||||||
</button>
|
</button>
|
||||||
</a>
|
</a>
|
||||||
<br>
|
<br>
|
||||||
<a href="/cli">
|
<a href="/cli">
|
||||||
<button class="button-small">
|
<button class="button-small">
|
||||||
<span class="button-label-small">Start the simple client</span>
|
<span class="button-label-small">${messages.otherOption2}</span>
|
||||||
</button>
|
</button>
|
||||||
</a>
|
</a>
|
||||||
<br>
|
<br>
|
||||||
<a href="/bios">
|
<a href="/bios">
|
||||||
<button class="button-small">
|
<button class="button-small">
|
||||||
<span class="button-label-small">Start the repair tool</span>
|
<span class="button-label-small">${messages.otherOption3}</span>
|
||||||
</button>
|
</button>
|
||||||
</a>
|
</a>
|
||||||
</details>
|
</details>
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
* achievementEarned - 実績を獲得
|
* achievementEarned - 実績を獲得
|
||||||
* exportCompleted - エクスポートが完了
|
* exportCompleted - エクスポートが完了
|
||||||
* login - ログイン
|
* login - ログイン
|
||||||
|
* createToken - トークン作成
|
||||||
* app - アプリ通知
|
* app - アプリ通知
|
||||||
* test - テスト通知(サーバー側)
|
* test - テスト通知(サーバー側)
|
||||||
*/
|
*/
|
||||||
|
@ -36,6 +37,7 @@ export const notificationTypes = [
|
||||||
'achievementEarned',
|
'achievementEarned',
|
||||||
'exportCompleted',
|
'exportCompleted',
|
||||||
'login',
|
'login',
|
||||||
|
'createToken',
|
||||||
'app',
|
'app',
|
||||||
'test',
|
'test',
|
||||||
] as const;
|
] as const;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"$schema": "https://json.schemastore.org/swcrc",
|
"$schema": "https://swc.rs/schema.json",
|
||||||
"jsc": {
|
"jsc": {
|
||||||
"parser": {
|
"parser": {
|
||||||
"syntax": "typescript",
|
"syntax": "typescript",
|
||||||
|
|
|
@ -69,6 +69,7 @@ export const notificationTypes = [
|
||||||
'achievementEarned',
|
'achievementEarned',
|
||||||
'exportCompleted',
|
'exportCompleted',
|
||||||
'login',
|
'login',
|
||||||
|
'createToken',
|
||||||
'test',
|
'test',
|
||||||
'app',
|
'app',
|
||||||
] as const;
|
] as const;
|
||||||
|
|
|
@ -414,6 +414,7 @@ function toStories(component: string): Promise<string> {
|
||||||
glob('src/components/MkSignupServerRules.vue'),
|
glob('src/components/MkSignupServerRules.vue'),
|
||||||
glob('src/components/MkUserSetupDialog.vue'),
|
glob('src/components/MkUserSetupDialog.vue'),
|
||||||
glob('src/components/MkUserSetupDialog.*.vue'),
|
glob('src/components/MkUserSetupDialog.*.vue'),
|
||||||
|
glob('src/components/MkImgPreviewDialog.vue'),
|
||||||
glob('src/components/MkInstanceCardMini.vue'),
|
glob('src/components/MkInstanceCardMini.vue'),
|
||||||
glob('src/components/MkInviteCode.vue'),
|
glob('src/components/MkInviteCode.vue'),
|
||||||
glob('src/components/MkTagItem.vue'),
|
glob('src/components/MkTagItem.vue'),
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { StoryObj } from '@storybook/vue3';
|
||||||
|
import { file } from '../../.storybook/fakes.js';
|
||||||
|
import MkImgPreviewDialog from './MkImgPreviewDialog.vue';
|
||||||
|
export const Default = {
|
||||||
|
render(args) {
|
||||||
|
return {
|
||||||
|
components: {
|
||||||
|
MkImgPreviewDialog,
|
||||||
|
},
|
||||||
|
setup() {
|
||||||
|
return {
|
||||||
|
args,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
props() {
|
||||||
|
return {
|
||||||
|
...this.args,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
template: '<MkImgPreviewDialog v-bind="props" />',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
args: {
|
||||||
|
file: file(),
|
||||||
|
},
|
||||||
|
parameters: {
|
||||||
|
chromatic: {
|
||||||
|
// NOTE: ロードが終わるまで待つ
|
||||||
|
delay: 3000,
|
||||||
|
},
|
||||||
|
layout: 'centered',
|
||||||
|
},
|
||||||
|
} satisfies StoryObj<typeof MkImgPreviewDialog>;
|
|
@ -0,0 +1,58 @@
|
||||||
|
<!--
|
||||||
|
SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
-->
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<MkModalWindow
|
||||||
|
ref="modal"
|
||||||
|
:width="1800"
|
||||||
|
:height="900"
|
||||||
|
@close="close"
|
||||||
|
@esc="close"
|
||||||
|
@click="close"
|
||||||
|
>
|
||||||
|
<template #header>{{ file.name }}</template>
|
||||||
|
<div :class="$style.container">
|
||||||
|
<img :src="file.url" :alt="file.comment ?? file.name" :class="$style.img"/>
|
||||||
|
</div>
|
||||||
|
</MkModalWindow>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { defineProps, ref } from 'vue';
|
||||||
|
import MkModalWindow from './MkModalWindow.vue';
|
||||||
|
import type * as Misskey from 'misskey-js';
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
file: Misskey.entities.DriveFile;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const modal = ref<typeof MkModalWindow | null>(null);
|
||||||
|
|
||||||
|
function close() {
|
||||||
|
modal.value?.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<style lang="scss" module>
|
||||||
|
.container {
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
min-height: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
background-color: var(--MI_THEME-bg);
|
||||||
|
background-size: auto auto;
|
||||||
|
background-image: repeating-linear-gradient(135deg, transparent, transparent 6px, var(--MI_THEME-panel) 6px, var(--MI_THEME-panel) 12px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.img {
|
||||||
|
width: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -91,10 +91,11 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
import { shallowRef, watch, computed, ref, onDeactivated, onActivated, onMounted } from 'vue';
|
import { shallowRef, watch, computed, ref, onDeactivated, onActivated, onMounted } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import type { MenuItem } from '@/types/menu.js';
|
import type { MenuItem } from '@/types/menu.js';
|
||||||
|
import type { Keymap } from '@/scripts/hotkey.js';
|
||||||
|
import { copyToClipboard } from '@/scripts/copy-to-clipboard';
|
||||||
import { defaultStore } from '@/store.js';
|
import { defaultStore } from '@/store.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import type { Keymap } from '@/scripts/hotkey.js';
|
|
||||||
import bytes from '@/filters/bytes.js';
|
import bytes from '@/filters/bytes.js';
|
||||||
import { hms } from '@/filters/hms.js';
|
import { hms } from '@/filters/hms.js';
|
||||||
import MkMediaRange from '@/components/MkMediaRange.vue';
|
import MkMediaRange from '@/components/MkMediaRange.vue';
|
||||||
|
@ -216,10 +217,9 @@ function showMenu(ev: MouseEvent) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const details: MenuItem[] = [];
|
||||||
if ($i?.id === props.audio.userId) {
|
if ($i?.id === props.audio.userId) {
|
||||||
menu.push({
|
details.push({
|
||||||
type: 'divider',
|
|
||||||
}, {
|
|
||||||
type: 'link',
|
type: 'link',
|
||||||
text: i18n.ts._fileViewer.title,
|
text: i18n.ts._fileViewer.title,
|
||||||
icon: 'ti ti-info-circle',
|
icon: 'ti ti-info-circle',
|
||||||
|
@ -227,6 +227,29 @@ function showMenu(ev: MouseEvent) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (iAmModerator) {
|
||||||
|
details.push({
|
||||||
|
type: 'link',
|
||||||
|
text: i18n.ts.moderation,
|
||||||
|
icon: 'ti ti-photo-exclamation',
|
||||||
|
to: `/admin/file/${props.audio.id}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (details.length > 0) {
|
||||||
|
menu.push({ type: 'divider' }, ...details);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (defaultStore.state.devMode) {
|
||||||
|
menu.push({ type: 'divider' }, {
|
||||||
|
icon: 'ti ti-id',
|
||||||
|
text: i18n.ts.copyFileId,
|
||||||
|
action: () => {
|
||||||
|
copyToClipboard(props.audio.id);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
menuShowing.value = true;
|
menuShowing.value = true;
|
||||||
os.popupMenu(menu, ev.currentTarget ?? ev.target, {
|
os.popupMenu(menu, ev.currentTarget ?? ev.target, {
|
||||||
align: 'right',
|
align: 'right',
|
||||||
|
|
|
@ -54,6 +54,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
import { watch, ref, computed } from 'vue';
|
import { watch, ref, computed } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import type { MenuItem } from '@/types/menu.js';
|
import type { MenuItem } from '@/types/menu.js';
|
||||||
|
import { copyToClipboard } from '@/scripts/copy-to-clipboard';
|
||||||
import { getStaticImageUrl } from '@/scripts/media-proxy.js';
|
import { getStaticImageUrl } from '@/scripts/media-proxy.js';
|
||||||
import bytes from '@/filters/bytes.js';
|
import bytes from '@/filters/bytes.js';
|
||||||
import ImgWithBlurhash from '@/components/MkImgWithBlurhash.vue';
|
import ImgWithBlurhash from '@/components/MkImgWithBlurhash.vue';
|
||||||
|
@ -132,10 +133,9 @@ function showMenu(ev: MouseEvent) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const details: MenuItem[] = [];
|
||||||
if ($i?.id === props.image.userId) {
|
if ($i?.id === props.image.userId) {
|
||||||
menuItems.push({
|
details.push({
|
||||||
type: 'divider',
|
|
||||||
}, {
|
|
||||||
type: 'link',
|
type: 'link',
|
||||||
text: i18n.ts._fileViewer.title,
|
text: i18n.ts._fileViewer.title,
|
||||||
icon: 'ti ti-info-circle',
|
icon: 'ti ti-info-circle',
|
||||||
|
@ -143,6 +143,29 @@ function showMenu(ev: MouseEvent) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (iAmModerator) {
|
||||||
|
details.push({
|
||||||
|
type: 'link',
|
||||||
|
text: i18n.ts.moderation,
|
||||||
|
icon: 'ti ti-photo-exclamation',
|
||||||
|
to: `/admin/file/${props.image.id}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (details.length > 0) {
|
||||||
|
menuItems.push({ type: 'divider' }, ...details);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (defaultStore.state.devMode) {
|
||||||
|
menuItems.push({ type: 'divider' }, {
|
||||||
|
icon: 'ti ti-id',
|
||||||
|
text: i18n.ts.copyFileId,
|
||||||
|
action: () => {
|
||||||
|
copyToClipboard(props.image.id);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
os.popupMenu(menuItems, ev.currentTarget ?? ev.target);
|
os.popupMenu(menuItems, ev.currentTarget ?? ev.target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -113,6 +113,7 @@ import { ref, shallowRef, computed, watch, onDeactivated, onActivated, onMounted
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import type { MenuItem } from '@/types/menu.js';
|
import type { MenuItem } from '@/types/menu.js';
|
||||||
import type { Keymap } from '@/scripts/hotkey.js';
|
import type { Keymap } from '@/scripts/hotkey.js';
|
||||||
|
import { copyToClipboard } from '@/scripts/copy-to-clipboard';
|
||||||
import bytes from '@/filters/bytes.js';
|
import bytes from '@/filters/bytes.js';
|
||||||
import { hms } from '@/filters/hms.js';
|
import { hms } from '@/filters/hms.js';
|
||||||
import { defaultStore } from '@/store.js';
|
import { defaultStore } from '@/store.js';
|
||||||
|
@ -241,10 +242,9 @@ function showMenu(ev: MouseEvent) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const details: MenuItem[] = [];
|
||||||
if ($i?.id === props.video.userId) {
|
if ($i?.id === props.video.userId) {
|
||||||
menu.push({
|
details.push({
|
||||||
type: 'divider',
|
|
||||||
}, {
|
|
||||||
type: 'link',
|
type: 'link',
|
||||||
text: i18n.ts._fileViewer.title,
|
text: i18n.ts._fileViewer.title,
|
||||||
icon: 'ti ti-info-circle',
|
icon: 'ti ti-info-circle',
|
||||||
|
@ -252,6 +252,29 @@ function showMenu(ev: MouseEvent) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (iAmModerator) {
|
||||||
|
details.push({
|
||||||
|
type: 'link',
|
||||||
|
text: i18n.ts.moderation,
|
||||||
|
icon: 'ti ti-photo-exclamation',
|
||||||
|
to: `/admin/file/${props.video.id}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (details.length > 0) {
|
||||||
|
menu.push({ type: 'divider' }, ...details);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (defaultStore.state.devMode) {
|
||||||
|
menu.push({ type: 'divider' }, {
|
||||||
|
icon: 'ti ti-id',
|
||||||
|
text: i18n.ts.copyFileId,
|
||||||
|
action: () => {
|
||||||
|
copyToClipboard(props.video.id);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
menuShowing.value = true;
|
menuShowing.value = true;
|
||||||
os.popupMenu(menu, ev.currentTarget ?? ev.target, {
|
os.popupMenu(menu, ev.currentTarget ?? ev.target, {
|
||||||
align: 'right',
|
align: 'right',
|
||||||
|
|
|
@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<div :class="$style.root">
|
<div :class="$style.root">
|
||||||
<div :class="$style.head">
|
<div :class="$style.head">
|
||||||
<MkAvatar v-if="['pollEnded', 'note'].includes(notification.type) && 'note' in notification" :class="$style.icon" :user="notification.note.user" link preview/>
|
<MkAvatar v-if="['pollEnded', 'note'].includes(notification.type) && 'note' in notification" :class="$style.icon" :user="notification.note.user" link preview/>
|
||||||
<MkAvatar v-else-if="['roleAssigned', 'achievementEarned', 'exportCompleted', 'login'].includes(notification.type)" :class="$style.icon" :user="$i" link preview/>
|
<MkAvatar v-else-if="['roleAssigned', 'achievementEarned', 'exportCompleted', 'login', 'createToken'].includes(notification.type)" :class="$style.icon" :user="$i" link preview/>
|
||||||
<div v-else-if="notification.type === 'reaction:grouped' && notification.note.reactionAcceptance === 'likeOnly'" :class="[$style.icon, $style.icon_reactionGroupHeart]"><i class="ti ti-heart" style="line-height: 1;"></i></div>
|
<div v-else-if="notification.type === 'reaction:grouped' && notification.note.reactionAcceptance === 'likeOnly'" :class="[$style.icon, $style.icon_reactionGroupHeart]"><i class="ti ti-heart" style="line-height: 1;"></i></div>
|
||||||
<div v-else-if="notification.type === 'reaction:grouped'" :class="[$style.icon, $style.icon_reactionGroup]"><i class="ti ti-plus" style="line-height: 1;"></i></div>
|
<div v-else-if="notification.type === 'reaction:grouped'" :class="[$style.icon, $style.icon_reactionGroup]"><i class="ti ti-plus" style="line-height: 1;"></i></div>
|
||||||
<div v-else-if="notification.type === 'renote:grouped'" :class="[$style.icon, $style.icon_renoteGroup]"><i class="ti ti-repeat" style="line-height: 1;"></i></div>
|
<div v-else-if="notification.type === 'renote:grouped'" :class="[$style.icon, $style.icon_renoteGroup]"><i class="ti ti-repeat" style="line-height: 1;"></i></div>
|
||||||
|
@ -27,6 +27,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
[$style.t_achievementEarned]: notification.type === 'achievementEarned',
|
[$style.t_achievementEarned]: notification.type === 'achievementEarned',
|
||||||
[$style.t_exportCompleted]: notification.type === 'exportCompleted',
|
[$style.t_exportCompleted]: notification.type === 'exportCompleted',
|
||||||
[$style.t_login]: notification.type === 'login',
|
[$style.t_login]: notification.type === 'login',
|
||||||
|
[$style.t_createToken]: notification.type === 'createToken',
|
||||||
[$style.t_roleAssigned]: notification.type === 'roleAssigned' && notification.role.iconUrl == null,
|
[$style.t_roleAssigned]: notification.type === 'roleAssigned' && notification.role.iconUrl == null,
|
||||||
}]"
|
}]"
|
||||||
>
|
>
|
||||||
|
@ -41,6 +42,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<i v-else-if="notification.type === 'achievementEarned'" class="ti ti-medal"></i>
|
<i v-else-if="notification.type === 'achievementEarned'" class="ti ti-medal"></i>
|
||||||
<i v-else-if="notification.type === 'exportCompleted'" class="ti ti-archive"></i>
|
<i v-else-if="notification.type === 'exportCompleted'" class="ti ti-archive"></i>
|
||||||
<i v-else-if="notification.type === 'login'" class="ti ti-login-2"></i>
|
<i v-else-if="notification.type === 'login'" class="ti ti-login-2"></i>
|
||||||
|
<i v-else-if="notification.type === 'createToken'" class="ti ti-key"></i>
|
||||||
<template v-else-if="notification.type === 'roleAssigned'">
|
<template v-else-if="notification.type === 'roleAssigned'">
|
||||||
<img v-if="notification.role.iconUrl" style="height: 1.3em; vertical-align: -22%;" :src="notification.role.iconUrl" alt=""/>
|
<img v-if="notification.role.iconUrl" style="height: 1.3em; vertical-align: -22%;" :src="notification.role.iconUrl" alt=""/>
|
||||||
<i v-else class="ti ti-badges"></i>
|
<i v-else class="ti ti-badges"></i>
|
||||||
|
@ -61,6 +63,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<span v-else-if="notification.type === 'roleAssigned'">{{ i18n.ts._notification.roleAssigned }}</span>
|
<span v-else-if="notification.type === 'roleAssigned'">{{ i18n.ts._notification.roleAssigned }}</span>
|
||||||
<span v-else-if="notification.type === 'achievementEarned'">{{ i18n.ts._notification.achievementEarned }}</span>
|
<span v-else-if="notification.type === 'achievementEarned'">{{ i18n.ts._notification.achievementEarned }}</span>
|
||||||
<span v-else-if="notification.type === 'login'">{{ i18n.ts._notification.login }}</span>
|
<span v-else-if="notification.type === 'login'">{{ i18n.ts._notification.login }}</span>
|
||||||
|
<span v-else-if="notification.type === 'createToken'">{{ i18n.ts._notification.createToken }}</span>
|
||||||
<span v-else-if="notification.type === 'test'">{{ i18n.ts._notification.testNotification }}</span>
|
<span v-else-if="notification.type === 'test'">{{ i18n.ts._notification.testNotification }}</span>
|
||||||
<span v-else-if="notification.type === 'exportCompleted'">{{ i18n.tsx._notification.exportOfXCompleted({ x: exportEntityName[notification.exportedEntity] }) }}</span>
|
<span v-else-if="notification.type === 'exportCompleted'">{{ i18n.tsx._notification.exportOfXCompleted({ x: exportEntityName[notification.exportedEntity] }) }}</span>
|
||||||
<MkA v-else-if="notification.type === 'follow' || notification.type === 'mention' || notification.type === 'reply' || notification.type === 'renote' || notification.type === 'quote' || notification.type === 'reaction' || notification.type === 'receiveFollowRequest' || notification.type === 'followRequestAccepted'" v-user-preview="notification.user.id" :class="$style.headerName" :to="userPage(notification.user)"><MkUserName :user="notification.user"/></MkA>
|
<MkA v-else-if="notification.type === 'follow' || notification.type === 'mention' || notification.type === 'reply' || notification.type === 'renote' || notification.type === 'quote' || notification.type === 'reaction' || notification.type === 'receiveFollowRequest' || notification.type === 'followRequestAccepted'" v-user-preview="notification.user.id" :class="$style.headerName" :to="userPage(notification.user)"><MkUserName :user="notification.user"/></MkA>
|
||||||
|
@ -107,6 +110,9 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<MkA v-else-if="notification.type === 'exportCompleted'" :class="$style.text" :to="`/my/drive/file/${notification.fileId}`">
|
<MkA v-else-if="notification.type === 'exportCompleted'" :class="$style.text" :to="`/my/drive/file/${notification.fileId}`">
|
||||||
{{ i18n.ts.showFile }}
|
{{ i18n.ts.showFile }}
|
||||||
</MkA>
|
</MkA>
|
||||||
|
<MkA v-else-if="notification.type === 'createToken'" :class="$style.text" to="/settings/apps">
|
||||||
|
<Mfm :text="i18n.tsx._notification.createTokenDescription({ text: i18n.ts.manageAccessTokens })"/>
|
||||||
|
</MkA>
|
||||||
<template v-else-if="notification.type === 'follow'">
|
<template v-else-if="notification.type === 'follow'">
|
||||||
<span :class="$style.text" style="opacity: 0.6;">{{ i18n.ts.youGotNewFollower }}</span>
|
<span :class="$style.text" style="opacity: 0.6;">{{ i18n.ts.youGotNewFollower }}</span>
|
||||||
</template>
|
</template>
|
||||||
|
@ -357,6 +363,12 @@ function getActualReactedUsersCount(notification: Misskey.entities.Notification)
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.t_createToken {
|
||||||
|
padding: 3px;
|
||||||
|
background: var(--eventOther);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
.tail {
|
.tail {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
|
|
@ -22,20 +22,26 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</Sortable>
|
</Sortable>
|
||||||
<p :class="[$style.remain, {
|
<p
|
||||||
[$style.exceeded]: props.modelValue.length > 16,
|
:class="[$style.remain, {
|
||||||
}]">{{ 16 - props.modelValue.length }}/16</p>
|
[$style.exceeded]: props.modelValue.length > 16,
|
||||||
|
}]"
|
||||||
|
>
|
||||||
|
{{ 16 - props.modelValue.length }}/16
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { defineAsyncComponent, inject } from 'vue';
|
import { defineAsyncComponent, inject } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
|
import type { MenuItem } from '@/types/menu';
|
||||||
|
import { defaultStore } from '@/store';
|
||||||
|
import { copyToClipboard } from '@/scripts/copy-to-clipboard';
|
||||||
import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue';
|
import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { misskeyApi } from '@/scripts/misskey-api.js';
|
import { misskeyApi } from '@/scripts/misskey-api.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import type { MenuItem } from '@/types/menu.js';
|
|
||||||
|
|
||||||
const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
|
const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
|
||||||
|
|
||||||
|
@ -168,6 +174,14 @@ function showFileMenu(file: Misskey.entities.DriveFile, ev: MouseEvent | Keyboar
|
||||||
text: i18n.ts.cropImage,
|
text: i18n.ts.cropImage,
|
||||||
icon: 'ti ti-crop',
|
icon: 'ti ti-crop',
|
||||||
action: () : void => { crop(file); },
|
action: () : void => { crop(file); },
|
||||||
|
}, {
|
||||||
|
text: i18n.ts.preview,
|
||||||
|
icon: 'ti ti-photo-search',
|
||||||
|
action: () => {
|
||||||
|
os.popup(defineAsyncComponent(() => import('@/components/MkImgPreviewDialog.vue')), {
|
||||||
|
file: file,
|
||||||
|
});
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,6 +198,16 @@ function showFileMenu(file: Misskey.entities.DriveFile, ev: MouseEvent | Keyboar
|
||||||
action: () => { detachAndDeleteMedia(file); },
|
action: () => { detachAndDeleteMedia(file); },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (defaultStore.state.devMode) {
|
||||||
|
menuItems.push({ type: 'divider' }, {
|
||||||
|
icon: 'ti ti-id',
|
||||||
|
text: i18n.ts.copyFileId,
|
||||||
|
action: () => {
|
||||||
|
copyToClipboard(file.id);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
os.popupMenu(menuItems, ev.currentTarget ?? ev.target).then(() => menuShowing = false);
|
os.popupMenu(menuItems, ev.currentTarget ?? ev.target).then(() => menuShowing = false);
|
||||||
menuShowing = true;
|
menuShowing = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
>
|
>
|
||||||
<iframe
|
<iframe
|
||||||
v-if="player.url.startsWith('http://') || player.url.startsWith('https://')"
|
v-if="player.url.startsWith('http://') || player.url.startsWith('https://')"
|
||||||
sandbox="allow-popups allow-scripts allow-storage-access-by-user-activation allow-same-origin"
|
sandbox="allow-popups allow-popups-to-escape-sandbox allow-scripts allow-storage-access-by-user-activation allow-same-origin"
|
||||||
scrolling="no"
|
scrolling="no"
|
||||||
:allow="player.allow == null ? 'autoplay;encrypted-media;fullscreen' : player.allow.filter(x => ['autoplay', 'clipboard-write', 'fullscreen', 'encrypted-media', 'picture-in-picture', 'web-share'].includes(x)).join(';')"
|
:allow="player.allow == null ? 'autoplay;encrypted-media;fullscreen' : player.allow.filter(x => ['autoplay', 'clipboard-write', 'fullscreen', 'encrypted-media', 'picture-in-picture', 'web-share'].includes(x)).join(';')"
|
||||||
:class="$style.playerIframe"
|
:class="$style.playerIframe"
|
||||||
|
|
|
@ -109,6 +109,9 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<div>
|
<div>
|
||||||
<a style="display: inline-block;" class="pepabo" title="GMO Pepabo" href="https://pepabo.com/" target="_blank"><img style="width: 100%;" src="https://assets.misskey-hub.net/sponsors/gmo_pepabo.svg" alt="GMO Pepabo"></a>
|
<a style="display: inline-block;" class="pepabo" title="GMO Pepabo" href="https://pepabo.com/" target="_blank"><img style="width: 100%;" src="https://assets.misskey-hub.net/sponsors/gmo_pepabo.svg" alt="GMO Pepabo"></a>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<a style="display: inline-block;" class="purpledotdigital" title="Purple Dot Digital" href="https://purpledotdigital.com/" target="_blank"><img style="width: 100%;" src="https://assets.misskey-hub.net/sponsors/purple-dot-digital.jpg" alt="Purple Dot Digital"></a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</FormSection>
|
</FormSection>
|
||||||
<FormSection>
|
<FormSection>
|
||||||
|
|
|
@ -418,7 +418,7 @@ async function deleteAccount() {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function assignRole() {
|
async function assignRole() {
|
||||||
const roles = await misskeyApi('admin/roles/list');
|
const roles = await misskeyApi('admin/roles/list').then(it => it.filter(r => r.target === 'manual'));
|
||||||
|
|
||||||
const { canceled, result: roleId } = await os.select({
|
const { canceled, result: roleId } = await os.select({
|
||||||
title: i18n.ts._role.chooseRoleToAssign,
|
title: i18n.ts._role.chooseRoleToAssign,
|
||||||
|
|
|
@ -81,7 +81,7 @@ const $i = signinRequired();
|
||||||
|
|
||||||
const nonConfigurableNotificationTypes = ['note', 'roleAssigned', 'followRequestAccepted', 'test', 'exportCompleted'] satisfies (typeof notificationTypes[number])[] as string[];
|
const nonConfigurableNotificationTypes = ['note', 'roleAssigned', 'followRequestAccepted', 'test', 'exportCompleted'] satisfies (typeof notificationTypes[number])[] as string[];
|
||||||
|
|
||||||
const onlyOnOrOffNotificationTypes = ['app', 'achievementEarned', 'login'] satisfies (typeof notificationTypes[number])[] as string[];
|
const onlyOnOrOffNotificationTypes = ['app', 'achievementEarned', 'login', 'createToken'] satisfies (typeof notificationTypes[number])[] as string[];
|
||||||
|
|
||||||
const allowButton = shallowRef<InstanceType<typeof MkPushNotificationAllowButton>>();
|
const allowButton = shallowRef<InstanceType<typeof MkPushNotificationAllowButton>>();
|
||||||
const pushRegistrationInServer = computed(() => allowButton.value?.pushRegistrationInServer);
|
const pushRegistrationInServer = computed(() => allowButton.value?.pushRegistrationInServer);
|
||||||
|
|
|
@ -98,7 +98,7 @@ describe('MkUrlPreview', () => {
|
||||||
assert.strictEqual(iframe?.src, 'https://example.local/player?autoplay=1&auto_play=1');
|
assert.strictEqual(iframe?.src, 'https://example.local/player?autoplay=1&auto_play=1');
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
iframe?.sandbox.toString(),
|
iframe?.sandbox.toString(),
|
||||||
'allow-popups allow-scripts allow-storage-access-by-user-activation allow-same-origin',
|
'allow-popups allow-popups-to-escape-sandbox allow-scripts allow-storage-access-by-user-activation allow-same-origin',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"$schema": "https://json.schemastore.org/swcrc",
|
"$schema": "https://swc.rs/schema.json",
|
||||||
"jsc": {
|
"jsc": {
|
||||||
"parser": {
|
"parser": {
|
||||||
"syntax": "typescript",
|
"syntax": "typescript",
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import { mkdir, writeFile } from 'fs/promises';
|
import assert from 'assert';
|
||||||
|
import { mkdir, readFile, writeFile } from 'fs/promises';
|
||||||
import { OpenAPIV3_1 } from 'openapi-types';
|
import { OpenAPIV3_1 } from 'openapi-types';
|
||||||
import { toPascal } from 'ts-case-convert';
|
import { toPascal } from 'ts-case-convert';
|
||||||
import OpenAPIParser from '@readme/openapi-parser';
|
import OpenAPIParser from '@readme/openapi-parser';
|
||||||
import openapiTS from 'openapi-typescript';
|
import openapiTS, { OpenAPI3, OperationObject, PathItemObject } from 'openapi-typescript';
|
||||||
|
|
||||||
async function generateBaseTypes(
|
async function generateBaseTypes(
|
||||||
openApiDocs: OpenAPIV3_1.Document,
|
openApiDocs: OpenAPIV3_1.Document,
|
||||||
|
@ -20,7 +21,29 @@ async function generateBaseTypes(
|
||||||
}
|
}
|
||||||
lines.push('');
|
lines.push('');
|
||||||
|
|
||||||
const generatedTypes = await openapiTS(openApiJsonPath, {
|
// NOTE: Align `operationId` of GET and POST to avoid duplication of type definitions
|
||||||
|
const openApi = JSON.parse(await readFile(openApiJsonPath, 'utf8')) as OpenAPI3;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
|
for (const [key, item] of Object.entries(openApi.paths!)) {
|
||||||
|
assert('post' in item);
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
|
openApi.paths![key] = {
|
||||||
|
...('get' in item ? {
|
||||||
|
get: {
|
||||||
|
...item.get,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
|
operationId: ((item as PathItemObject).get as OperationObject).operationId!.replaceAll('get___', ''),
|
||||||
|
},
|
||||||
|
} : {}),
|
||||||
|
post: {
|
||||||
|
...item.post,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
|
operationId: ((item as PathItemObject).post as OperationObject).operationId!.replaceAll('post___', ''),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const generatedTypes = await openapiTS(openApi, {
|
||||||
exportType: true,
|
exportType: true,
|
||||||
transform(schemaObject) {
|
transform(schemaObject) {
|
||||||
if ('format' in schemaObject && schemaObject.format === 'binary') {
|
if ('format' in schemaObject && schemaObject.format === 'binary') {
|
||||||
|
@ -78,7 +101,7 @@ async function generateEndpoints(
|
||||||
for (const operation of postPathItems) {
|
for (const operation of postPathItems) {
|
||||||
const path = operation._path_;
|
const path = operation._path_;
|
||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
const operationId = operation.operationId!;
|
const operationId = operation.operationId!.replaceAll('get___', '').replaceAll('post___', '');
|
||||||
const endpoint = new Endpoint(path);
|
const endpoint = new Endpoint(path);
|
||||||
endpoints.push(endpoint);
|
endpoints.push(endpoint);
|
||||||
|
|
||||||
|
@ -195,7 +218,7 @@ async function generateApiClientJSDoc(
|
||||||
|
|
||||||
for (const operation of postPathItems) {
|
for (const operation of postPathItems) {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
const operationId = operation.operationId!;
|
const operationId = operation.operationId!.replaceAll('get___', '').replaceAll('post___', '');
|
||||||
|
|
||||||
if (operation.description) {
|
if (operation.description) {
|
||||||
endpoints.push({
|
endpoints.push({
|
||||||
|
|
|
@ -4355,6 +4355,13 @@ export type components = {
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
/** @enum {string} */
|
/** @enum {string} */
|
||||||
type: 'login';
|
type: 'login';
|
||||||
|
} | {
|
||||||
|
/** Format: id */
|
||||||
|
id: string;
|
||||||
|
/** Format: date-time */
|
||||||
|
createdAt: string;
|
||||||
|
/** @enum {string} */
|
||||||
|
type: 'createToken';
|
||||||
} | ({
|
} | ({
|
||||||
/** Format: id */
|
/** Format: id */
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -8349,7 +8356,8 @@ export type operations = {
|
||||||
urlPreviewRequireContentLength: boolean;
|
urlPreviewRequireContentLength: boolean;
|
||||||
urlPreviewUserAgent: string | null;
|
urlPreviewUserAgent: string | null;
|
||||||
urlPreviewSummaryProxyUrl: string | null;
|
urlPreviewSummaryProxyUrl: string | null;
|
||||||
federation: string;
|
/** @enum {string} */
|
||||||
|
federation: 'all' | 'specified' | 'none';
|
||||||
federationHosts: string[];
|
federationHosts: string[];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -19916,8 +19924,8 @@ export type operations = {
|
||||||
untilId?: string;
|
untilId?: string;
|
||||||
/** @default true */
|
/** @default true */
|
||||||
markAsRead?: boolean;
|
markAsRead?: boolean;
|
||||||
includeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'receiveFollowRequest' | 'followRequestAccepted' | 'roleAssigned' | 'achievementEarned' | 'exportCompleted' | 'login' | 'app' | 'test' | 'pollVote' | 'groupInvited')[];
|
includeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'receiveFollowRequest' | 'followRequestAccepted' | 'roleAssigned' | 'achievementEarned' | 'exportCompleted' | 'login' | 'createToken' | 'app' | 'test' | 'pollVote' | 'groupInvited')[];
|
||||||
excludeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'receiveFollowRequest' | 'followRequestAccepted' | 'roleAssigned' | 'achievementEarned' | 'exportCompleted' | 'login' | 'app' | 'test' | 'pollVote' | 'groupInvited')[];
|
excludeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'receiveFollowRequest' | 'followRequestAccepted' | 'roleAssigned' | 'achievementEarned' | 'exportCompleted' | 'login' | 'createToken' | 'app' | 'test' | 'pollVote' | 'groupInvited')[];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -19984,8 +19992,8 @@ export type operations = {
|
||||||
untilId?: string;
|
untilId?: string;
|
||||||
/** @default true */
|
/** @default true */
|
||||||
markAsRead?: boolean;
|
markAsRead?: boolean;
|
||||||
includeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'receiveFollowRequest' | 'followRequestAccepted' | 'roleAssigned' | 'achievementEarned' | 'exportCompleted' | 'login' | 'app' | 'test' | 'reaction:grouped' | 'renote:grouped' | 'pollVote' | 'groupInvited')[];
|
includeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'receiveFollowRequest' | 'followRequestAccepted' | 'roleAssigned' | 'achievementEarned' | 'exportCompleted' | 'login' | 'createToken' | 'app' | 'test' | 'reaction:grouped' | 'renote:grouped' | 'pollVote' | 'groupInvited')[];
|
||||||
excludeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'receiveFollowRequest' | 'followRequestAccepted' | 'roleAssigned' | 'achievementEarned' | 'exportCompleted' | 'login' | 'app' | 'test' | 'reaction:grouped' | 'renote:grouped' | 'pollVote' | 'groupInvited')[];
|
excludeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'receiveFollowRequest' | 'followRequestAccepted' | 'roleAssigned' | 'achievementEarned' | 'exportCompleted' | 'login' | 'createToken' | 'app' | 'test' | 'reaction:grouped' | 'renote:grouped' | 'pollVote' | 'groupInvited')[];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue