mirror of
https://github.com/misskey-dev/misskey.git
synced 2024-12-27 08:10:24 +01:00
Merge branch 'develop' into enh-tweak-search-page
This commit is contained in:
commit
283e070702
57 changed files with 2621 additions and 3495 deletions
|
@ -5,7 +5,7 @@
|
|||
"workspaceFolder": "/workspace",
|
||||
"features": {
|
||||
"ghcr.io/devcontainers/features/node:1": {
|
||||
"version": "20.16.0"
|
||||
"version": "22.11.0"
|
||||
},
|
||||
"ghcr.io/devcontainers-contrib/features/corepack:1": {}
|
||||
},
|
||||
|
|
2
.github/workflows/get-api-diff.yml
vendored
2
.github/workflows/get-api-diff.yml
vendored
|
@ -17,7 +17,7 @@ jobs:
|
|||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [20.16.0]
|
||||
node-version: [22.11.0]
|
||||
api-json-name: [api-base.json, api-head.json]
|
||||
include:
|
||||
- api-json-name: api-base.json
|
||||
|
|
2
.github/workflows/on-release-created.yml
vendored
2
.github/workflows/on-release-created.yml
vendored
|
@ -17,7 +17,7 @@ jobs:
|
|||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [20.16.0]
|
||||
node-version: [22.11.0]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.1
|
||||
|
|
4
.github/workflows/test-backend.yml
vendored
4
.github/workflows/test-backend.yml
vendored
|
@ -22,7 +22,7 @@ jobs:
|
|||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [20.16.0]
|
||||
node-version: [22.11.0]
|
||||
|
||||
services:
|
||||
postgres:
|
||||
|
@ -71,7 +71,7 @@ jobs:
|
|||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [20.16.0]
|
||||
node-version: [22.11.0]
|
||||
|
||||
services:
|
||||
postgres:
|
||||
|
|
2
.github/workflows/test-federation.yml
vendored
2
.github/workflows/test-federation.yml
vendored
|
@ -20,7 +20,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [20.16.0]
|
||||
node-version: [22.11.0]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
|
|
4
.github/workflows/test-frontend.yml
vendored
4
.github/workflows/test-frontend.yml
vendored
|
@ -26,7 +26,7 @@ jobs:
|
|||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [20.16.0]
|
||||
node-version: [22.11.0]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.1
|
||||
|
@ -61,7 +61,7 @@ jobs:
|
|||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
node-version: [20.16.0]
|
||||
node-version: [22.11.0]
|
||||
browser: [chrome]
|
||||
|
||||
services:
|
||||
|
|
2
.github/workflows/test-misskey-js.yml
vendored
2
.github/workflows/test-misskey-js.yml
vendored
|
@ -21,7 +21,7 @@ jobs:
|
|||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [20.16.0]
|
||||
node-version: [22.11.0]
|
||||
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
|
||||
|
||||
steps:
|
||||
|
|
2
.github/workflows/test-production.yml
vendored
2
.github/workflows/test-production.yml
vendored
|
@ -16,7 +16,7 @@ jobs:
|
|||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [20.16.0]
|
||||
node-version: [22.11.0]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.1
|
||||
|
|
2
.github/workflows/validate-api-json.yml
vendored
2
.github/workflows/validate-api-json.yml
vendored
|
@ -18,7 +18,7 @@ jobs:
|
|||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [20.16.0]
|
||||
node-version: [22.11.0]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.1
|
||||
|
|
|
@ -1 +1 @@
|
|||
20.16.0
|
||||
22.11.0
|
||||
|
|
27
CHANGELOG.md
27
CHANGELOG.md
|
@ -1,8 +1,14 @@
|
|||
## 2024.10.2
|
||||
## 2024.11.0
|
||||
|
||||
### Note
|
||||
- Node.js 20.xは非推奨になりました。Node.js 22.x (LTS)の利用を推奨します。
|
||||
- DockerのNode.jsが22.11.0に更新されました
|
||||
|
||||
### General
|
||||
- Feat: コンテンツの表示にログインを必須にできるように
|
||||
- Feat: 過去のノートを非公開化/フォロワーのみ表示可能にできるように
|
||||
- Enhance: 依存関係の更新
|
||||
- Enhance: l10nの更新
|
||||
|
||||
### Client
|
||||
- Enhance: Bull DashboardでRelationship Queueの状態も確認できるように
|
||||
|
@ -15,7 +21,9 @@
|
|||
- どのアカウントで認証しようとしているのかがわかるように
|
||||
- 認証するアカウントを切り替えられるように
|
||||
- Enhance: Self-XSS防止用の警告を追加
|
||||
- Enhance: カタルーニャ語 (ca-ES) に対応
|
||||
- Enhance: カタルーニャ語 (ca-ES) に対応
|
||||
- Enhance: 個別お知らせページではMetaタグを出力するように
|
||||
- Enhance: ノート詳細画面にロールのバッジを表示
|
||||
- Enhance: ノート検索ページのデザイン調整
|
||||
(Cherry-picked from https://github.com/taiyme/misskey/pull/273)
|
||||
- Fix: 通知の範囲指定の設定項目が必要ない通知設定でも範囲指定の設定がでている問題を修正
|
||||
|
@ -24,15 +32,30 @@
|
|||
- Fix: デッキのタイムラインカラムで「センシティブなファイルを含むノートを表示」設定が使用できなかった問題を修正
|
||||
- Fix: Encode RSS urls with escape sequences before fetching allowing query parameters to be used
|
||||
- Fix: リンク切れを修正
|
||||
= Fix: ノート投稿ボタンにホバー時のスタイルが適用されていないのを修正
|
||||
(Cherry-picked from https://github.com/taiyme/misskey/pull/305)
|
||||
- Fix: メールアドレス登録有効化時の「完了」ダイアログボックスの表示条件を修正
|
||||
- Fix: 画面幅が狭い環境でデザインが崩れる問題を修正
|
||||
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/815)
|
||||
|
||||
### Server
|
||||
- Enhance: DockerのNode.jsを22.11.0に更新
|
||||
- Enhance: 起動前の疎通チェックで、DBとメイン以外のRedisの疎通確認も行うように
|
||||
(Based on https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/588)
|
||||
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/715)
|
||||
- Enhance: リモートユーザーの照会をオリジナルにリダイレクトするように
|
||||
- Fix: フォロワーへのメッセージの絵文字をemojisに含めるように
|
||||
- Fix: Nested proxy requestsを検出した際にブロックするように
|
||||
[ghsa-gq5q-c77c-v236](https://github.com/misskey-dev/misskey/security/advisories/ghsa-gq5q-c77c-v236)
|
||||
- Fix: 招待コードの発行可能な残り数算出に使用すべきロールポリシーの値が違う問題を修正
|
||||
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/706)
|
||||
- Fix: 連合への配信時に、acctの大小文字が区別されてしまい正しくメンションが処理されないことがある問題を修正
|
||||
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/711)
|
||||
- Fix: ローカルユーザーへのメンションを含むノートが連合される際に正しいURLに変換されないことがある問題を修正
|
||||
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/712)
|
||||
- Fix: FTT無効時にユーザーリストタイムラインが使用できない問題を修正
|
||||
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/709)
|
||||
- Fix: User Webhookテスト機能のMock Payloadを修正
|
||||
|
||||
### Misskey.js
|
||||
- Fix: Stream初期化時、別途WebSocketを指定する場合の型定義を修正
|
||||
|
|
|
@ -83,6 +83,10 @@ One should not add property that has defined before by other implementation, or
|
|||
## Reviewers guide
|
||||
Be willing to comment on the good points and not just the things you want fixed 💯
|
||||
|
||||
読んでおくといいやつ
|
||||
- https://blog.lacolaco.net/posts/1e2cf439b3c2/
|
||||
- https://konifar-zatsu.hatenadiary.jp/entry/2024/11/05/192421
|
||||
|
||||
### Review perspective
|
||||
- Scope
|
||||
- Are the goals of the PR clear?
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# syntax = docker/dockerfile:1.4
|
||||
|
||||
ARG NODE_VERSION=20.16.0-bullseye
|
||||
ARG NODE_VERSION=22.11.0-bullseye
|
||||
|
||||
# build assets & compile TypeScript
|
||||
|
||||
|
|
20
package.json
20
package.json
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "misskey",
|
||||
"version": "2024.10.2-alpha.2",
|
||||
"version": "2024.11.0-alpha.0",
|
||||
"codename": "nasubi",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -52,26 +52,26 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"cssnano": "6.1.2",
|
||||
"execa": "8.0.1",
|
||||
"execa": "9.5.1",
|
||||
"fast-glob": "3.3.2",
|
||||
"ignore-walk": "6.0.5",
|
||||
"js-yaml": "4.1.0",
|
||||
"postcss": "8.4.47",
|
||||
"postcss": "8.4.49",
|
||||
"tar": "6.2.1",
|
||||
"terser": "5.33.0",
|
||||
"typescript": "5.6.2",
|
||||
"esbuild": "0.23.1",
|
||||
"terser": "5.36.0",
|
||||
"typescript": "5.6.3",
|
||||
"esbuild": "0.24.0",
|
||||
"glob": "11.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@misskey-dev/eslint-plugin": "2.0.3",
|
||||
"@types/node": "20.14.12",
|
||||
"@types/node": "22.9.0",
|
||||
"@typescript-eslint/eslint-plugin": "7.17.0",
|
||||
"@typescript-eslint/parser": "7.17.0",
|
||||
"cross-env": "7.0.3",
|
||||
"cypress": "13.14.2",
|
||||
"eslint": "9.8.0",
|
||||
"globals": "15.9.0",
|
||||
"cypress": "13.15.2",
|
||||
"eslint": "9.14.0",
|
||||
"globals": "15.12.0",
|
||||
"ncp": "2.0.0",
|
||||
"start-server-and-test": "2.0.8"
|
||||
},
|
||||
|
|
|
@ -69,32 +69,32 @@
|
|||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "3.620.0",
|
||||
"@aws-sdk/lib-storage": "3.620.0",
|
||||
"@bull-board/api": "6.0.0",
|
||||
"@bull-board/fastify": "6.0.0",
|
||||
"@bull-board/ui": "6.0.0",
|
||||
"@bull-board/api": "6.5.0",
|
||||
"@bull-board/fastify": "6.5.0",
|
||||
"@bull-board/ui": "6.5.0",
|
||||
"@discordapp/twemoji": "15.1.0",
|
||||
"@fastify/accepts": "5.0.1",
|
||||
"@fastify/cookie": "10.0.1",
|
||||
"@fastify/cookie": "11.0.1",
|
||||
"@fastify/cors": "10.0.1",
|
||||
"@fastify/express": "4.0.1",
|
||||
"@fastify/http-proxy": "10.0.0",
|
||||
"@fastify/http-proxy": "10.0.1",
|
||||
"@fastify/multipart": "9.0.1",
|
||||
"@fastify/static": "8.0.1",
|
||||
"@fastify/static": "8.0.2",
|
||||
"@fastify/view": "10.0.1",
|
||||
"@misskey-dev/sharp-read-bmp": "1.2.0",
|
||||
"@misskey-dev/summaly": "5.1.0",
|
||||
"@napi-rs/canvas": "0.1.56",
|
||||
"@nestjs/common": "10.4.4",
|
||||
"@nestjs/core": "10.4.4",
|
||||
"@nestjs/testing": "10.4.4",
|
||||
"@nestjs/common": "10.4.7",
|
||||
"@nestjs/core": "10.4.7",
|
||||
"@nestjs/testing": "10.4.7",
|
||||
"@peertube/http-signature": "1.7.0",
|
||||
"@sentry/node": "8.20.0",
|
||||
"@sentry/profiling-node": "8.20.0",
|
||||
"@sentry/node": "8.38.0",
|
||||
"@sentry/profiling-node": "8.38.0",
|
||||
"@simplewebauthn/server": "10.0.1",
|
||||
"@sinonjs/fake-timers": "11.2.2",
|
||||
"@smithy/node-http-handler": "2.5.0",
|
||||
"@swc/cli": "0.3.12",
|
||||
"@swc/core": "1.6.6",
|
||||
"@swc/core": "1.9.2",
|
||||
"@twemoji/parser": "15.1.1",
|
||||
"accepts": "1.3.8",
|
||||
"ajv": "8.17.1",
|
||||
|
@ -103,7 +103,7 @@
|
|||
"bcryptjs": "2.4.3",
|
||||
"blurhash": "2.0.5",
|
||||
"body-parser": "1.20.3",
|
||||
"bullmq": "5.15.0",
|
||||
"bullmq": "5.26.1",
|
||||
"cacheable-lookup": "7.0.0",
|
||||
"cbor": "9.0.2",
|
||||
"chalk": "5.3.0",
|
||||
|
@ -117,11 +117,11 @@
|
|||
"fastify": "5.0.0",
|
||||
"fastify-raw-body": "5.0.0",
|
||||
"feed": "4.2.2",
|
||||
"file-type": "19.5.0",
|
||||
"file-type": "19.6.0",
|
||||
"fluent-ffmpeg": "2.1.3",
|
||||
"form-data": "4.0.0",
|
||||
"got": "14.4.2",
|
||||
"happy-dom": "15.7.4",
|
||||
"form-data": "4.0.1",
|
||||
"got": "14.4.4",
|
||||
"happy-dom": "15.11.4",
|
||||
"hpagent": "1.2.0",
|
||||
"htmlescape": "1.1.1",
|
||||
"http-link-header": "1.1.3",
|
||||
|
@ -134,7 +134,7 @@
|
|||
"json5": "2.2.3",
|
||||
"jsonld": "8.3.2",
|
||||
"jsrsasign": "11.1.0",
|
||||
"meilisearch": "0.42.0",
|
||||
"meilisearch": "0.45.0",
|
||||
"juice": "11.0.0",
|
||||
"mfm-js": "0.24.0",
|
||||
"microformats-parser": "2.0.2",
|
||||
|
@ -142,18 +142,18 @@
|
|||
"misskey-js": "workspace:*",
|
||||
"misskey-reversi": "workspace:*",
|
||||
"ms": "3.0.0-canary.1",
|
||||
"nanoid": "5.0.7",
|
||||
"nanoid": "5.0.8",
|
||||
"nested-property": "4.0.0",
|
||||
"node-fetch": "3.3.2",
|
||||
"nodemailer": "6.9.15",
|
||||
"nodemailer": "6.9.16",
|
||||
"nsfwjs": "2.4.2",
|
||||
"oauth": "0.10.0",
|
||||
"oauth2orize": "1.12.0",
|
||||
"oauth2orize-pkce": "0.1.2",
|
||||
"os-utils": "0.0.14",
|
||||
"otpauth": "9.3.4",
|
||||
"parse5": "7.1.2",
|
||||
"pg": "8.13.0",
|
||||
"parse5": "7.2.1",
|
||||
"pg": "8.13.1",
|
||||
"pkce-challenge": "4.1.0",
|
||||
"probe-image-size": "7.2.3",
|
||||
"promise-limit": "2.7.0",
|
||||
|
@ -180,7 +180,7 @@
|
|||
"tsc-alias": "1.8.10",
|
||||
"tsconfig-paths": "4.2.0",
|
||||
"typeorm": "0.3.20",
|
||||
"typescript": "5.6.2",
|
||||
"typescript": "5.6.3",
|
||||
"ulid": "2.3.0",
|
||||
"vary": "1.1.2",
|
||||
"web-push": "3.6.7",
|
||||
|
@ -189,28 +189,28 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@jest/globals": "29.7.0",
|
||||
"@nestjs/platform-express": "10.4.4",
|
||||
"@nestjs/platform-express": "10.4.7",
|
||||
"@simplewebauthn/types": "10.0.0",
|
||||
"@swc/jest": "0.2.36",
|
||||
"@swc/jest": "0.2.37",
|
||||
"@types/accepts": "1.3.7",
|
||||
"@types/archiver": "6.0.2",
|
||||
"@types/archiver": "6.0.3",
|
||||
"@types/bcryptjs": "2.4.6",
|
||||
"@types/body-parser": "1.19.5",
|
||||
"@types/color-convert": "2.0.4",
|
||||
"@types/content-disposition": "0.5.8",
|
||||
"@types/fluent-ffmpeg": "2.1.26",
|
||||
"@types/fluent-ffmpeg": "2.1.27",
|
||||
"@types/htmlescape": "1.1.3",
|
||||
"@types/http-link-header": "1.0.7",
|
||||
"@types/jest": "29.5.13",
|
||||
"@types/jest": "29.5.14",
|
||||
"@types/js-yaml": "4.0.9",
|
||||
"@types/jsdom": "21.1.7",
|
||||
"@types/jsonld": "1.5.15",
|
||||
"@types/jsrsasign": "10.5.14",
|
||||
"@types/mime-types": "2.1.4",
|
||||
"@types/ms": "0.7.34",
|
||||
"@types/node": "20.14.12",
|
||||
"@types/node": "22.9.0",
|
||||
"@types/nodemailer": "6.4.16",
|
||||
"@types/oauth": "0.9.5",
|
||||
"@types/oauth": "0.9.6",
|
||||
"@types/oauth2orize": "1.11.5",
|
||||
"@types/oauth2orize-pkce": "0.1.2",
|
||||
"@types/pg": "8.11.10",
|
||||
|
@ -227,14 +227,14 @@
|
|||
"@types/tinycolor2": "1.4.6",
|
||||
"@types/tmp": "0.2.6",
|
||||
"@types/vary": "1.1.3",
|
||||
"@types/web-push": "3.6.3",
|
||||
"@types/ws": "8.5.12",
|
||||
"@types/web-push": "3.6.4",
|
||||
"@types/ws": "8.5.13",
|
||||
"@typescript-eslint/eslint-plugin": "7.17.0",
|
||||
"@typescript-eslint/parser": "7.17.0",
|
||||
"aws-sdk-client-mock": "4.0.1",
|
||||
"cross-env": "7.0.3",
|
||||
"eslint-plugin-import": "2.30.0",
|
||||
"execa": "9.4.0",
|
||||
"execa": "9.5.1",
|
||||
"fkill": "9.0.0",
|
||||
"jest": "29.7.0",
|
||||
"jest-mock": "29.7.0",
|
||||
|
|
|
@ -406,8 +406,10 @@ export class MfmService {
|
|||
mention: (node) => {
|
||||
const a = doc.createElement('a');
|
||||
const { username, host, acct } = node.props;
|
||||
const remoteUserInfo = mentionedRemoteUsers.find(remoteUser => remoteUser.username === username && remoteUser.host === host);
|
||||
a.setAttribute('href', remoteUserInfo ? (remoteUserInfo.url ? remoteUserInfo.url : remoteUserInfo.uri) : `${this.config.url}/${acct}`);
|
||||
const remoteUserInfo = mentionedRemoteUsers.find(remoteUser => remoteUser.username.toLowerCase() === username.toLowerCase() && remoteUser.host?.toLowerCase() === host?.toLowerCase());
|
||||
a.setAttribute('href', remoteUserInfo
|
||||
? (remoteUserInfo.url ? remoteUserInfo.url : remoteUserInfo.uri)
|
||||
: `${this.config.url}/${acct.endsWith(`@${this.config.url}`) ? acct.substring(0, acct.length - this.config.url.length - 1) : acct}`);
|
||||
a.className = 'u-url mention';
|
||||
a.textContent = acct;
|
||||
return a;
|
||||
|
|
|
@ -7,7 +7,7 @@ import { randomUUID } from 'node:crypto';
|
|||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import type { IActivity } from '@/core/activitypub/type.js';
|
||||
import type { MiDriveFile } from '@/models/DriveFile.js';
|
||||
import type { MiWebhook, webhookEventTypes } from '@/models/Webhook.js';
|
||||
import type { MiWebhook, WebhookEventTypes, webhookEventTypes } from '@/models/Webhook.js';
|
||||
import type { MiSystemWebhook, SystemWebhookEventType } from '@/models/SystemWebhook.js';
|
||||
import type { Config } from '@/config.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
|
@ -35,6 +35,7 @@ import type {
|
|||
} from './QueueModule.js';
|
||||
import type httpSignature from '@peertube/http-signature';
|
||||
import type * as Bull from 'bullmq';
|
||||
import { type UserWebhookPayload } from './UserWebhookService.js';
|
||||
|
||||
@Injectable()
|
||||
export class QueueService {
|
||||
|
@ -468,10 +469,10 @@ export class QueueService {
|
|||
* @see UserWebhookDeliverProcessorService
|
||||
*/
|
||||
@bindThis
|
||||
public userWebhookDeliver(
|
||||
public userWebhookDeliver<T extends WebhookEventTypes>(
|
||||
webhook: MiWebhook,
|
||||
type: typeof webhookEventTypes[number],
|
||||
content: unknown,
|
||||
type: T,
|
||||
content: UserWebhookPayload<T>,
|
||||
opts?: { attempts?: number },
|
||||
) {
|
||||
const data: UserWebhookDeliverJobData = {
|
||||
|
|
|
@ -6,11 +6,23 @@
|
|||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import * as Redis from 'ioredis';
|
||||
import { type WebhooksRepository } from '@/models/_.js';
|
||||
import { MiWebhook } from '@/models/Webhook.js';
|
||||
import { MiWebhook, WebhookEventTypes } from '@/models/Webhook.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { GlobalEvents } from '@/core/GlobalEventService.js';
|
||||
import type { OnApplicationShutdown } from '@nestjs/common';
|
||||
import type { Packed } from '@/misc/json-schema.js';
|
||||
|
||||
export type UserWebhookPayload<T extends WebhookEventTypes> =
|
||||
T extends 'note' | 'reply' | 'renote' |'mention' ? {
|
||||
note: Packed<'Note'>,
|
||||
} :
|
||||
T extends 'follow' | 'unfollow' ? {
|
||||
user: Packed<'UserDetailedNotMe'>,
|
||||
} :
|
||||
T extends 'followed' ? {
|
||||
user: Packed<'UserLite'>,
|
||||
} : never;
|
||||
|
||||
@Injectable()
|
||||
export class UserWebhookService implements OnApplicationShutdown {
|
||||
|
|
|
@ -10,7 +10,7 @@ import { MiSystemWebhook, type SystemWebhookEventType } from '@/models/SystemWeb
|
|||
import { SystemWebhookService } from '@/core/SystemWebhookService.js';
|
||||
import { Packed } from '@/misc/json-schema.js';
|
||||
import { type WebhookEventTypes } from '@/models/Webhook.js';
|
||||
import { UserWebhookService } from '@/core/UserWebhookService.js';
|
||||
import { type UserWebhookPayload, UserWebhookService } from '@/core/UserWebhookService.js';
|
||||
import { QueueService } from '@/core/QueueService.js';
|
||||
import { ModeratorInactivityRemainingTime } from '@/queue/processors/CheckModeratorsActivityProcessorService.js';
|
||||
|
||||
|
@ -306,10 +306,10 @@ export class WebhookTestService {
|
|||
* - 送信対象イベント(on)に関する設定
|
||||
*/
|
||||
@bindThis
|
||||
public async testUserWebhook(
|
||||
public async testUserWebhook<T extends WebhookEventTypes>(
|
||||
params: {
|
||||
webhookId: MiWebhook['id'],
|
||||
type: WebhookEventTypes,
|
||||
type: T,
|
||||
override?: Partial<Omit<MiWebhook, 'id'>>,
|
||||
},
|
||||
sender: MiUser | null,
|
||||
|
@ -321,7 +321,7 @@ export class WebhookTestService {
|
|||
}
|
||||
|
||||
const webhook = webhooks[0];
|
||||
const send = (contents: unknown) => {
|
||||
const send = <U extends WebhookEventTypes>(type: U, contents: UserWebhookPayload<U>) => {
|
||||
const merged = {
|
||||
...webhook,
|
||||
...params.override,
|
||||
|
@ -329,7 +329,7 @@ export class WebhookTestService {
|
|||
|
||||
// テスト目的なのでUserWebhookServiceの機能を経由せず直接キューに追加する(チェック処理などをスキップする意図).
|
||||
// また、Jobの試行回数も1回だけ.
|
||||
this.queueService.userWebhookDeliver(merged, params.type, contents, { attempts: 1 });
|
||||
this.queueService.userWebhookDeliver(merged, type, contents, { attempts: 1 });
|
||||
};
|
||||
|
||||
const dummyNote1 = generateDummyNote({
|
||||
|
@ -361,33 +361,40 @@ export class WebhookTestService {
|
|||
|
||||
switch (params.type) {
|
||||
case 'note': {
|
||||
send(toPackedNote(dummyNote1));
|
||||
send('note', { note: toPackedNote(dummyNote1) });
|
||||
break;
|
||||
}
|
||||
case 'reply': {
|
||||
send(toPackedNote(dummyReply1));
|
||||
send('reply', { note: toPackedNote(dummyReply1) });
|
||||
break;
|
||||
}
|
||||
case 'renote': {
|
||||
send(toPackedNote(dummyRenote1));
|
||||
send('renote', { note: toPackedNote(dummyRenote1) });
|
||||
break;
|
||||
}
|
||||
case 'mention': {
|
||||
send(toPackedNote(dummyMention1));
|
||||
send('mention', { note: toPackedNote(dummyMention1) });
|
||||
break;
|
||||
}
|
||||
case 'follow': {
|
||||
send(toPackedUserDetailedNotMe(dummyUser1));
|
||||
send('follow', { user: toPackedUserDetailedNotMe(dummyUser1) });
|
||||
break;
|
||||
}
|
||||
case 'followed': {
|
||||
send(toPackedUserLite(dummyUser2));
|
||||
send('followed', { user: toPackedUserLite(dummyUser2) });
|
||||
break;
|
||||
}
|
||||
case 'unfollow': {
|
||||
send(toPackedUserDetailedNotMe(dummyUser3));
|
||||
send('unfollow', { user: toPackedUserDetailedNotMe(dummyUser3) });
|
||||
break;
|
||||
}
|
||||
// まだ実装されていない (#9485)
|
||||
case 'reaction': return;
|
||||
default: {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const _exhaustiveAssertion: never = params.type;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,5 +4,5 @@
|
|||
*/
|
||||
|
||||
export function sqlLikeEscape(s: string) {
|
||||
return s.replace(/([%_])/g, '\\$1');
|
||||
return s.replace(/([\\%_])/g, '\\$1');
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
|||
import { bindThis } from '@/decorators.js';
|
||||
import { IActivity } from '@/core/activitypub/type.js';
|
||||
import { isQuote, isRenote } from '@/misc/is-renote.js';
|
||||
import * as Acct from '@/misc/acct.js';
|
||||
import type { FastifyInstance, FastifyRequest, FastifyReply, FastifyPluginOptions, FastifyBodyParser } from 'fastify';
|
||||
import type { FindOptionsWhere } from 'typeorm';
|
||||
|
||||
|
@ -486,6 +487,16 @@ export class ActivityPubServerService {
|
|||
return;
|
||||
}
|
||||
|
||||
// リモートだったらリダイレクト
|
||||
if (user.host != null) {
|
||||
if (user.uri == null || this.utilityService.isSelfHost(user.host)) {
|
||||
reply.code(500);
|
||||
return;
|
||||
}
|
||||
reply.redirect(user.uri, 301);
|
||||
return;
|
||||
}
|
||||
|
||||
reply.header('Cache-Control', 'public, max-age=180');
|
||||
this.setResponseType(request, reply);
|
||||
return (this.apRendererService.addContext(await this.apRendererService.renderPerson(user as MiLocalUser)));
|
||||
|
@ -654,19 +665,20 @@ export class ActivityPubServerService {
|
|||
|
||||
const user = await this.usersRepository.findOneBy({
|
||||
id: userId,
|
||||
host: IsNull(),
|
||||
isSuspended: false,
|
||||
});
|
||||
|
||||
return await this.userInfo(request, reply, user);
|
||||
});
|
||||
|
||||
fastify.get<{ Params: { user: string; } }>('/@:user', { constraints: { apOrHtml: 'ap' } }, async (request, reply) => {
|
||||
fastify.get<{ Params: { acct: string; } }>('/@:acct', { constraints: { apOrHtml: 'ap' } }, async (request, reply) => {
|
||||
vary(reply.raw, 'Accept');
|
||||
|
||||
const acct = Acct.parse(request.params.acct);
|
||||
|
||||
const user = await this.usersRepository.findOneBy({
|
||||
usernameLower: request.params.user.toLowerCase(),
|
||||
host: IsNull(),
|
||||
usernameLower: acct.username,
|
||||
host: acct.host ?? IsNull(),
|
||||
isSuspended: false,
|
||||
});
|
||||
|
||||
|
|
|
@ -465,6 +465,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
const newName = updates.name === undefined ? user.name : updates.name;
|
||||
const newDescription = profileUpdates.description === undefined ? profile.description : profileUpdates.description;
|
||||
const newFields = profileUpdates.fields === undefined ? profile.fields : profileUpdates.fields;
|
||||
const newFollowedMessage = profileUpdates.followedMessage === undefined ? profile.followedMessage : profileUpdates.followedMessage;
|
||||
|
||||
if (newName != null) {
|
||||
let hasProhibitedWords = false;
|
||||
|
@ -494,6 +495,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
]);
|
||||
}
|
||||
|
||||
if (newFollowedMessage != null) {
|
||||
const tokens = mfm.parse(newFollowedMessage);
|
||||
emojis = emojis.concat(extractCustomEmojisFromMfm(tokens));
|
||||
}
|
||||
|
||||
updates.emojis = emojis;
|
||||
updates.tags = tags;
|
||||
|
||||
|
|
|
@ -112,7 +112,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
|
||||
this.activeUsersChart.read(me);
|
||||
|
||||
await this.noteEntityService.packMany(timeline, me);
|
||||
return await this.noteEntityService.packMany(timeline, me);
|
||||
}
|
||||
|
||||
const timeline = await this.fanoutTimelineEndpointService.timeline({
|
||||
|
|
|
@ -42,13 +42,26 @@ import { MetaEntityService } from '@/core/entities/MetaEntityService.js';
|
|||
import { GalleryPostEntityService } from '@/core/entities/GalleryPostEntityService.js';
|
||||
import { ClipEntityService } from '@/core/entities/ClipEntityService.js';
|
||||
import { ChannelEntityService } from '@/core/entities/ChannelEntityService.js';
|
||||
import type { ChannelsRepository, ClipsRepository, FlashsRepository, GalleryPostsRepository, MiMeta, NotesRepository, PagesRepository, ReversiGamesRepository, UserProfilesRepository, UsersRepository } from '@/models/_.js';
|
||||
import type {
|
||||
AnnouncementsRepository,
|
||||
ChannelsRepository,
|
||||
ClipsRepository,
|
||||
FlashsRepository,
|
||||
GalleryPostsRepository,
|
||||
MiMeta,
|
||||
NotesRepository,
|
||||
PagesRepository,
|
||||
ReversiGamesRepository,
|
||||
UserProfilesRepository,
|
||||
UsersRepository,
|
||||
} from '@/models/_.js';
|
||||
import type Logger from '@/logger.js';
|
||||
import { handleRequestRedirectToOmitSearch } from '@/misc/fastify-hook-handlers.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { FlashEntityService } from '@/core/entities/FlashEntityService.js';
|
||||
import { RoleService } from '@/core/RoleService.js';
|
||||
import { ReversiGameEntityService } from '@/core/entities/ReversiGameEntityService.js';
|
||||
import { AnnouncementEntityService } from '@/core/entities/AnnouncementEntityService.js';
|
||||
import { FeedService } from './FeedService.js';
|
||||
import { UrlPreviewService } from './UrlPreviewService.js';
|
||||
import { ClientLoggerService } from './ClientLoggerService.js';
|
||||
|
@ -103,6 +116,9 @@ export class ClientServerService {
|
|||
@Inject(DI.reversiGamesRepository)
|
||||
private reversiGamesRepository: ReversiGamesRepository,
|
||||
|
||||
@Inject(DI.announcementsRepository)
|
||||
private announcementsRepository: AnnouncementsRepository,
|
||||
|
||||
private flashEntityService: FlashEntityService,
|
||||
private userEntityService: UserEntityService,
|
||||
private noteEntityService: NoteEntityService,
|
||||
|
@ -112,6 +128,7 @@ export class ClientServerService {
|
|||
private clipEntityService: ClipEntityService,
|
||||
private channelEntityService: ChannelEntityService,
|
||||
private reversiGameEntityService: ReversiGameEntityService,
|
||||
private announcementEntityService: AnnouncementEntityService,
|
||||
private urlPreviewService: UrlPreviewService,
|
||||
private feedService: FeedService,
|
||||
private roleService: RoleService,
|
||||
|
@ -776,6 +793,24 @@ export class ClientServerService {
|
|||
return await renderBase(reply);
|
||||
}
|
||||
});
|
||||
|
||||
// 個別お知らせページ
|
||||
fastify.get<{ Params: { announcementId: string; } }>('/announcements/:announcementId', async (request, reply) => {
|
||||
const announcement = await this.announcementsRepository.findOneBy({
|
||||
id: request.params.announcementId,
|
||||
});
|
||||
|
||||
if (announcement) {
|
||||
const _announcement = await this.announcementEntityService.pack(announcement);
|
||||
reply.header('Cache-Control', 'public, max-age=3600');
|
||||
return await reply.view('announcement', {
|
||||
announcement: _announcement,
|
||||
...await this.generateCommonPugData(this.meta),
|
||||
});
|
||||
} else {
|
||||
return await renderBase(reply);
|
||||
}
|
||||
});
|
||||
//#endregion
|
||||
|
||||
//#region noindex pages
|
||||
|
|
21
packages/backend/src/server/web/views/announcement.pug
Normal file
21
packages/backend/src/server/web/views/announcement.pug
Normal file
|
@ -0,0 +1,21 @@
|
|||
extends ./base
|
||||
|
||||
block vars
|
||||
- const title = announcement.title;
|
||||
- const description = announcement.text.length > 100 ? announcement.text.slice(0, 100) + '…' : announcement.text;
|
||||
- const url = `${config.url}/announcements/${announcement.id}`;
|
||||
|
||||
block title
|
||||
= `${title} | ${instanceName}`
|
||||
|
||||
block desc
|
||||
meta(name='description' content=description)
|
||||
|
||||
block og
|
||||
meta(property='og:type' content='article')
|
||||
meta(property='og:title' content= title)
|
||||
meta(property='og:description' content= description)
|
||||
meta(property='og:url' content= url)
|
||||
if announcement.imageUrl
|
||||
meta(property='og:image' content=announcement.imageUrl)
|
||||
meta(property='twitter:card' content='summary_large_image')
|
|
@ -2,6 +2,7 @@ block vars
|
|||
|
||||
block loadClientEntry
|
||||
- const entry = config.frontendEntry;
|
||||
- const baseUrl = config.url;
|
||||
|
||||
doctype html
|
||||
|
||||
|
@ -32,7 +33,7 @@ html
|
|||
link(rel='icon' href= icon || '/favicon.ico')
|
||||
link(rel='apple-touch-icon' href= appleTouchIcon || '/apple-touch-icon.png')
|
||||
link(rel='manifest' href='/manifest.json')
|
||||
link(rel='search' type='application/opensearchdescription+xml' title=(title || "Misskey") href=`${url}/opensearch.xml`)
|
||||
link(rel='search' type='application/opensearchdescription+xml' title=(title || "Misskey") href=`${baseUrl}/opensearch.xml`)
|
||||
link(rel='prefetch' href=serverErrorImageUrl)
|
||||
link(rel='prefetch' href=infoImageUrl)
|
||||
link(rel='prefetch' href=notFoundImageUrl)
|
||||
|
|
|
@ -230,6 +230,7 @@ describe('Webリソース', () => {
|
|||
path: path('xxxxxxxxxx'),
|
||||
type: HTML,
|
||||
}));
|
||||
test.todo('HTMLとしてGETできる。(リモートユーザーでもリダイレクトせず)');
|
||||
});
|
||||
|
||||
describe.each([
|
||||
|
@ -249,6 +250,7 @@ describe('Webリソース', () => {
|
|||
path: path('xxxxxxxxxx'),
|
||||
accept,
|
||||
}));
|
||||
test.todo('はオリジナルにリダイレクトされる。(リモートユーザー)');
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { beforeAll, describe, jest } from '@jest/globals';
|
||||
import { WebhookTestService } from '@/core/WebhookTestService.js';
|
||||
import { UserWebhookService } from '@/core/UserWebhookService.js';
|
||||
import { UserWebhookPayload, UserWebhookService } from '@/core/UserWebhookService.js';
|
||||
import { SystemWebhookService } from '@/core/SystemWebhookService.js';
|
||||
import { GlobalModule } from '@/GlobalModule.js';
|
||||
import { MiSystemWebhook, MiUser, MiWebhook, UserProfilesRepository, UsersRepository } from '@/models/_.js';
|
||||
|
@ -122,7 +122,7 @@ describe('WebhookTestService', () => {
|
|||
const calls = queueService.userWebhookDeliver.mock.calls[0];
|
||||
expect((calls[0] as any).id).toBe('dummy-webhook');
|
||||
expect(calls[1]).toBe('note');
|
||||
expect((calls[2] as any).id).toBe('dummy-note-1');
|
||||
expect((calls[2] as UserWebhookPayload<'note'>).note.id).toBe('dummy-note-1');
|
||||
});
|
||||
|
||||
test('reply', async () => {
|
||||
|
@ -131,7 +131,7 @@ describe('WebhookTestService', () => {
|
|||
const calls = queueService.userWebhookDeliver.mock.calls[0];
|
||||
expect((calls[0] as any).id).toBe('dummy-webhook');
|
||||
expect(calls[1]).toBe('reply');
|
||||
expect((calls[2] as any).id).toBe('dummy-reply-1');
|
||||
expect((calls[2] as UserWebhookPayload<'reply'>).note.id).toBe('dummy-reply-1');
|
||||
});
|
||||
|
||||
test('renote', async () => {
|
||||
|
@ -140,7 +140,7 @@ describe('WebhookTestService', () => {
|
|||
const calls = queueService.userWebhookDeliver.mock.calls[0];
|
||||
expect((calls[0] as any).id).toBe('dummy-webhook');
|
||||
expect(calls[1]).toBe('renote');
|
||||
expect((calls[2] as any).id).toBe('dummy-renote-1');
|
||||
expect((calls[2] as UserWebhookPayload<'renote'>).note.id).toBe('dummy-renote-1');
|
||||
});
|
||||
|
||||
test('mention', async () => {
|
||||
|
@ -149,7 +149,7 @@ describe('WebhookTestService', () => {
|
|||
const calls = queueService.userWebhookDeliver.mock.calls[0];
|
||||
expect((calls[0] as any).id).toBe('dummy-webhook');
|
||||
expect(calls[1]).toBe('mention');
|
||||
expect((calls[2] as any).id).toBe('dummy-mention-1');
|
||||
expect((calls[2] as UserWebhookPayload<'mention'>).note.id).toBe('dummy-mention-1');
|
||||
});
|
||||
|
||||
test('follow', async () => {
|
||||
|
@ -158,7 +158,7 @@ describe('WebhookTestService', () => {
|
|||
const calls = queueService.userWebhookDeliver.mock.calls[0];
|
||||
expect((calls[0] as any).id).toBe('dummy-webhook');
|
||||
expect(calls[1]).toBe('follow');
|
||||
expect((calls[2] as any).id).toBe('dummy-user-1');
|
||||
expect((calls[2] as UserWebhookPayload<'follow'>).user.id).toBe('dummy-user-1');
|
||||
});
|
||||
|
||||
test('followed', async () => {
|
||||
|
@ -167,7 +167,7 @@ describe('WebhookTestService', () => {
|
|||
const calls = queueService.userWebhookDeliver.mock.calls[0];
|
||||
expect((calls[0] as any).id).toBe('dummy-webhook');
|
||||
expect(calls[1]).toBe('followed');
|
||||
expect((calls[2] as any).id).toBe('dummy-user-2');
|
||||
expect((calls[2] as UserWebhookPayload<'followed'>).user.id).toBe('dummy-user-2');
|
||||
});
|
||||
|
||||
test('unfollow', async () => {
|
||||
|
@ -176,7 +176,7 @@ describe('WebhookTestService', () => {
|
|||
const calls = queueService.userWebhookDeliver.mock.calls[0];
|
||||
expect((calls[0] as any).id).toBe('dummy-webhook');
|
||||
expect(calls[1]).toBe('unfollow');
|
||||
expect((calls[2] as any).id).toBe('dummy-user-3');
|
||||
expect((calls[2] as UserWebhookPayload<'unfollow'>).user.id).toBe('dummy-user-3');
|
||||
});
|
||||
|
||||
describe('NoSuchWebhookError', () => {
|
||||
|
|
|
@ -14,11 +14,11 @@
|
|||
"@discordapp/twemoji": "15.1.0",
|
||||
"@rollup/plugin-json": "6.1.0",
|
||||
"@rollup/plugin-replace": "5.0.7",
|
||||
"@rollup/pluginutils": "5.1.2",
|
||||
"@rollup/pluginutils": "5.1.3",
|
||||
"@tabler/icons-webfont": "3.3.0",
|
||||
"@twemoji/parser": "15.1.1",
|
||||
"@vitejs/plugin-vue": "5.1.4",
|
||||
"@vue/compiler-sfc": "3.5.11",
|
||||
"@vitejs/plugin-vue": "5.2.0",
|
||||
"@vue/compiler-sfc": "3.5.12",
|
||||
"astring": "1.9.0",
|
||||
"buraha": "0.0.1",
|
||||
"estree-walker": "3.0.3",
|
||||
|
@ -26,47 +26,47 @@
|
|||
"misskey-js": "workspace:*",
|
||||
"frontend-shared": "workspace:*",
|
||||
"punycode": "2.3.1",
|
||||
"rollup": "4.22.5",
|
||||
"rollup": "4.26.0",
|
||||
"sass": "1.79.4",
|
||||
"shiki": "1.21.0",
|
||||
"shiki": "1.22.2",
|
||||
"tinycolor2": "1.6.0",
|
||||
"tsc-alias": "1.8.10",
|
||||
"tsconfig-paths": "4.2.0",
|
||||
"typescript": "5.6.2",
|
||||
"typescript": "5.6.3",
|
||||
"uuid": "10.0.0",
|
||||
"json5": "2.2.3",
|
||||
"vite": "5.4.8",
|
||||
"vue": "3.5.11"
|
||||
"vite": "5.4.11",
|
||||
"vue": "3.5.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@misskey-dev/summaly": "5.1.0",
|
||||
"@testing-library/vue": "8.1.0",
|
||||
"@types/estree": "1.0.6",
|
||||
"@types/micromatch": "4.0.9",
|
||||
"@types/node": "20.14.12",
|
||||
"@types/node": "22.9.0",
|
||||
"@types/punycode": "2.1.4",
|
||||
"@types/tinycolor2": "1.4.6",
|
||||
"@types/uuid": "10.0.0",
|
||||
"@types/ws": "8.5.12",
|
||||
"@types/ws": "8.5.13",
|
||||
"@typescript-eslint/eslint-plugin": "7.17.0",
|
||||
"@typescript-eslint/parser": "7.17.0",
|
||||
"@vitest/coverage-v8": "1.6.0",
|
||||
"@vue/runtime-core": "3.5.11",
|
||||
"acorn": "8.12.1",
|
||||
"@vue/runtime-core": "3.5.12",
|
||||
"acorn": "8.14.0",
|
||||
"cross-env": "7.0.3",
|
||||
"eslint-plugin-import": "2.31.0",
|
||||
"eslint-plugin-vue": "9.28.0",
|
||||
"eslint-plugin-vue": "9.31.0",
|
||||
"fast-glob": "3.3.2",
|
||||
"happy-dom": "10.0.3",
|
||||
"intersection-observer": "0.12.2",
|
||||
"micromatch": "4.0.8",
|
||||
"msw": "2.3.4",
|
||||
"msw": "2.6.4",
|
||||
"nodemon": "3.1.7",
|
||||
"prettier": "3.3.3",
|
||||
"start-server-and-test": "2.0.8",
|
||||
"vite-plugin-turbosnap": "1.0.3",
|
||||
"vue-component-type-helpers": "2.1.6",
|
||||
"vue-component-type-helpers": "2.1.10",
|
||||
"vue-eslint-parser": "9.4.3",
|
||||
"vue-tsc": "2.1.6"
|
||||
"vue-tsc": "2.1.10"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,12 +21,12 @@
|
|||
"lint": "pnpm typecheck && pnpm eslint"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "20.14.12",
|
||||
"@types/node": "22.9.0",
|
||||
"@typescript-eslint/eslint-plugin": "7.17.0",
|
||||
"@typescript-eslint/parser": "7.17.0",
|
||||
"esbuild": "0.23.0",
|
||||
"eslint-plugin-vue": "9.27.0",
|
||||
"typescript": "5.5.4",
|
||||
"esbuild": "0.24.0",
|
||||
"eslint-plugin-vue": "9.31.0",
|
||||
"typescript": "5.6.3",
|
||||
"vue-eslint-parser": "9.4.3"
|
||||
},
|
||||
"files": [
|
||||
|
@ -34,6 +34,6 @@
|
|||
],
|
||||
"dependencies": {
|
||||
"misskey-js": "workspace:*",
|
||||
"vue": "3.4.37"
|
||||
"vue": "3.5.12"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,23 +23,23 @@
|
|||
"@misskey-dev/browser-image-resizer": "2024.1.0",
|
||||
"@rollup/plugin-json": "6.1.0",
|
||||
"@rollup/plugin-replace": "5.0.7",
|
||||
"@rollup/pluginutils": "5.1.2",
|
||||
"@rollup/pluginutils": "5.1.3",
|
||||
"@syuilo/aiscript": "0.19.0",
|
||||
"@tabler/icons-webfont": "3.3.0",
|
||||
"@twemoji/parser": "15.1.1",
|
||||
"@vitejs/plugin-vue": "5.1.4",
|
||||
"@vue/compiler-sfc": "3.5.11",
|
||||
"@vitejs/plugin-vue": "5.2.0",
|
||||
"@vue/compiler-sfc": "3.5.12",
|
||||
"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.11",
|
||||
"astring": "1.9.0",
|
||||
"broadcast-channel": "7.0.0",
|
||||
"buraha": "0.0.1",
|
||||
"canvas-confetti": "1.9.3",
|
||||
"chart.js": "4.4.4",
|
||||
"chart.js": "4.4.6",
|
||||
"chartjs-adapter-date-fns": "3.0.0",
|
||||
"chartjs-chart-matrix": "2.0.1",
|
||||
"chartjs-plugin-gradient": "0.6.1",
|
||||
"chartjs-plugin-zoom": "2.0.1",
|
||||
"chromatic": "11.11.0",
|
||||
"chromatic": "11.18.1",
|
||||
"compare-versions": "6.1.1",
|
||||
"cropperjs": "2.0.0-rc.2",
|
||||
"date-fns": "2.30.0",
|
||||
|
@ -57,10 +57,10 @@
|
|||
"misskey-reversi": "workspace:*",
|
||||
"photoswipe": "5.4.4",
|
||||
"punycode": "2.3.1",
|
||||
"rollup": "4.22.5",
|
||||
"rollup": "4.26.0",
|
||||
"sanitize-html": "2.13.1",
|
||||
"sass": "1.79.3",
|
||||
"shiki": "1.21.0",
|
||||
"shiki": "1.22.2",
|
||||
"strict-event-emitter-types": "2.0.0",
|
||||
"textarea-caret": "3.1.0",
|
||||
"three": "0.169.0",
|
||||
|
@ -68,74 +68,74 @@
|
|||
"tinycolor2": "1.6.0",
|
||||
"tsc-alias": "1.8.10",
|
||||
"tsconfig-paths": "4.2.0",
|
||||
"typescript": "5.6.2",
|
||||
"typescript": "5.6.3",
|
||||
"uuid": "10.0.0",
|
||||
"v-code-diff": "1.13.1",
|
||||
"vite": "5.4.8",
|
||||
"vue": "3.5.11",
|
||||
"vite": "5.4.11",
|
||||
"vue": "3.5.12",
|
||||
"vuedraggable": "next"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@misskey-dev/summaly": "5.1.0",
|
||||
"@storybook/addon-actions": "8.3.4",
|
||||
"@storybook/addon-essentials": "8.3.4",
|
||||
"@storybook/addon-interactions": "8.3.4",
|
||||
"@storybook/addon-links": "8.3.4",
|
||||
"@storybook/addon-mdx-gfm": "8.3.4",
|
||||
"@storybook/addon-storysource": "8.3.4",
|
||||
"@storybook/blocks": "8.3.4",
|
||||
"@storybook/components": "8.3.4",
|
||||
"@storybook/core-events": "8.3.4",
|
||||
"@storybook/manager-api": "8.3.4",
|
||||
"@storybook/preview-api": "8.3.4",
|
||||
"@storybook/react": "8.3.4",
|
||||
"@storybook/react-vite": "8.3.4",
|
||||
"@storybook/test": "8.3.4",
|
||||
"@storybook/theming": "8.3.4",
|
||||
"@storybook/types": "8.3.4",
|
||||
"@storybook/vue3": "8.3.4",
|
||||
"@storybook/vue3-vite": "8.3.4",
|
||||
"@storybook/addon-actions": "8.4.4",
|
||||
"@storybook/addon-essentials": "8.4.4",
|
||||
"@storybook/addon-interactions": "8.4.4",
|
||||
"@storybook/addon-links": "8.4.4",
|
||||
"@storybook/addon-mdx-gfm": "8.4.4",
|
||||
"@storybook/addon-storysource": "8.4.4",
|
||||
"@storybook/blocks": "8.4.4",
|
||||
"@storybook/components": "8.4.4",
|
||||
"@storybook/core-events": "8.4.4",
|
||||
"@storybook/manager-api": "8.4.4",
|
||||
"@storybook/preview-api": "8.4.4",
|
||||
"@storybook/react": "8.4.4",
|
||||
"@storybook/react-vite": "8.4.4",
|
||||
"@storybook/test": "8.4.4",
|
||||
"@storybook/theming": "8.4.4",
|
||||
"@storybook/types": "8.4.4",
|
||||
"@storybook/vue3": "8.4.4",
|
||||
"@storybook/vue3-vite": "8.4.4",
|
||||
"@testing-library/vue": "8.1.0",
|
||||
"@types/canvas-confetti": "^1.6.4",
|
||||
"@types/estree": "1.0.6",
|
||||
"@types/matter-js": "0.19.7",
|
||||
"@types/micromatch": "4.0.9",
|
||||
"@types/node": "20.14.12",
|
||||
"@types/node": "22.9.0",
|
||||
"@types/punycode": "2.1.4",
|
||||
"@types/sanitize-html": "2.13.0",
|
||||
"@types/seedrandom": "3.0.8",
|
||||
"@types/throttle-debounce": "5.0.2",
|
||||
"@types/tinycolor2": "1.4.6",
|
||||
"@types/uuid": "10.0.0",
|
||||
"@types/ws": "8.5.12",
|
||||
"@types/ws": "8.5.13",
|
||||
"@typescript-eslint/eslint-plugin": "7.17.0",
|
||||
"@typescript-eslint/parser": "7.17.0",
|
||||
"@vitest/coverage-v8": "1.6.0",
|
||||
"@vue/runtime-core": "3.5.11",
|
||||
"acorn": "8.12.1",
|
||||
"@vue/runtime-core": "3.5.12",
|
||||
"acorn": "8.14.0",
|
||||
"cross-env": "7.0.3",
|
||||
"cypress": "13.15.0",
|
||||
"cypress": "13.15.2",
|
||||
"eslint-plugin-import": "2.31.0",
|
||||
"eslint-plugin-vue": "9.28.0",
|
||||
"eslint-plugin-vue": "9.31.0",
|
||||
"fast-glob": "3.3.2",
|
||||
"happy-dom": "10.0.3",
|
||||
"intersection-observer": "0.12.2",
|
||||
"micromatch": "4.0.8",
|
||||
"msw": "2.4.9",
|
||||
"msw-storybook-addon": "2.0.3",
|
||||
"msw": "2.6.4",
|
||||
"msw-storybook-addon": "2.0.4",
|
||||
"nodemon": "3.1.7",
|
||||
"prettier": "3.3.3",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
"seedrandom": "3.0.5",
|
||||
"start-server-and-test": "2.0.8",
|
||||
"storybook": "8.3.4",
|
||||
"storybook": "8.4.4",
|
||||
"storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme",
|
||||
"vite-plugin-turbosnap": "1.0.3",
|
||||
"vitest": "1.6.0",
|
||||
"vitest-fetch-mock": "0.2.2",
|
||||
"vue-component-type-helpers": "2.1.6",
|
||||
"vue-component-type-helpers": "2.1.10",
|
||||
"vue-eslint-parser": "9.4.3",
|
||||
"vue-tsc": "2.1.6"
|
||||
"vue-tsc": "2.1.10"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ import { updateI18n, i18n } from '@/i18n.js';
|
|||
import { $i, refreshAccount, login } from '@/account.js';
|
||||
import { defaultStore, ColdDeviceStorage } from '@/store.js';
|
||||
import { fetchInstance, instance } from '@/instance.js';
|
||||
import { deviceKind } from '@/scripts/device-kind.js';
|
||||
import { deviceKind, updateDeviceKind } from '@/scripts/device-kind.js';
|
||||
import { reloadChannel } from '@/scripts/unison-reload.js';
|
||||
import { getUrlWithoutLoginId } from '@/scripts/login-id.js';
|
||||
import { getAccountFromId } from '@/scripts/get-account-from-id.js';
|
||||
|
@ -185,6 +185,10 @@ export async function common(createVue: () => App<Element>) {
|
|||
}
|
||||
});
|
||||
|
||||
watch(defaultStore.reactiveState.overridedDeviceKind, (kind) => {
|
||||
updateDeviceKind(kind);
|
||||
}, { immediate: true });
|
||||
|
||||
watch(defaultStore.reactiveState.useBlurEffectForModal, v => {
|
||||
document.documentElement.style.setProperty('--MI-modalBgFilter', v ? 'blur(4px)' : 'none');
|
||||
}, { immediate: true });
|
||||
|
|
|
@ -118,7 +118,7 @@ import { hms } from '@/filters/hms.js';
|
|||
import { defaultStore } from '@/store.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import * as os from '@/os.js';
|
||||
import { isFullscreenNotSupported } from '@/scripts/device-kind.js';
|
||||
import { exitFullscreen, requestFullscreen } from '@/scripts/fullscreen.js';
|
||||
import hasAudio from '@/scripts/media-has-audio.js';
|
||||
import MkMediaRange from '@/components/MkMediaRange.vue';
|
||||
import { $i, iAmModerator } from '@/account.js';
|
||||
|
@ -334,26 +334,21 @@ function togglePlayPause() {
|
|||
}
|
||||
|
||||
function toggleFullscreen() {
|
||||
if (isFullscreenNotSupported && videoEl.value) {
|
||||
if (isFullscreen.value) {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
//@ts-ignore
|
||||
videoEl.value.webkitExitFullscreen();
|
||||
isFullscreen.value = false;
|
||||
} else {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
//@ts-ignore
|
||||
videoEl.value.webkitEnterFullscreen();
|
||||
isFullscreen.value = true;
|
||||
}
|
||||
} else if (playerEl.value) {
|
||||
if (isFullscreen.value) {
|
||||
document.exitFullscreen();
|
||||
isFullscreen.value = false;
|
||||
} else {
|
||||
playerEl.value.requestFullscreen({ navigationUI: 'hide' });
|
||||
isFullscreen.value = true;
|
||||
}
|
||||
if (playerEl.value == null || videoEl.value == null) return;
|
||||
if (isFullscreen.value) {
|
||||
exitFullscreen({
|
||||
videoEl: videoEl.value,
|
||||
});
|
||||
isFullscreen.value = false;
|
||||
} else {
|
||||
requestFullscreen({
|
||||
videoEl: videoEl.value,
|
||||
playerEl: playerEl.value,
|
||||
options: {
|
||||
navigationUI: 'hide',
|
||||
},
|
||||
});
|
||||
isFullscreen.value = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -454,8 +449,10 @@ watch(loop, (to) => {
|
|||
});
|
||||
|
||||
watch(hide, (to) => {
|
||||
if (to && isFullscreen.value) {
|
||||
document.exitFullscreen();
|
||||
if (videoEl.value && to && isFullscreen.value) {
|
||||
exitFullscreen({
|
||||
videoEl: videoEl.value,
|
||||
});
|
||||
isFullscreen.value = false;
|
||||
}
|
||||
});
|
||||
|
|
|
@ -292,15 +292,18 @@ function checkMute(noteToCheck: Misskey.entities.Note, mutedWords: Array<string
|
|||
function checkMute(noteToCheck: Misskey.entities.Note, mutedWords: Array<string | string[]> | undefined | null, checkOnly: false): boolean | 'sensitiveMute';
|
||||
*/
|
||||
function checkMute(noteToCheck: Misskey.entities.Note, mutedWords: Array<string | string[]> | undefined | null, checkOnly = false): boolean | 'sensitiveMute' {
|
||||
if (mutedWords == null) return false;
|
||||
|
||||
if (checkWordMute(noteToCheck, $i, mutedWords)) return true;
|
||||
if (noteToCheck.reply && checkWordMute(noteToCheck.reply, $i, mutedWords)) return true;
|
||||
if (noteToCheck.renote && checkWordMute(noteToCheck.renote, $i, mutedWords)) return true;
|
||||
if (mutedWords != null) {
|
||||
if (checkWordMute(noteToCheck, $i, mutedWords)) return true;
|
||||
if (noteToCheck.reply && checkWordMute(noteToCheck.reply, $i, mutedWords)) return true;
|
||||
if (noteToCheck.renote && checkWordMute(noteToCheck.renote, $i, mutedWords)) return true;
|
||||
}
|
||||
|
||||
if (checkOnly) return false;
|
||||
|
||||
if (inTimeline && !tl_withSensitive.value && noteToCheck.files?.some((v) => v.isSensitive)) return 'sensitiveMute';
|
||||
if (inTimeline && tl_withSensitive.value === false && noteToCheck.files?.some((v) => v.isSensitive)) {
|
||||
return 'sensitiveMute';
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -62,7 +62,14 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<span v-if="appearNote.localOnly" style="margin-left: 0.5em;" :title="i18n.ts._visibility['disableFederation']"><i class="ti ti-rocket-off"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
<div :class="$style.noteHeaderUsername"><MkAcct :user="appearNote.user"/></div>
|
||||
<div :class="$style.noteHeaderUsernameAndBadgeRoles">
|
||||
<div :class="$style.noteHeaderUsername">
|
||||
<MkAcct :user="appearNote.user"/>
|
||||
</div>
|
||||
<div v-if="appearNote.user.badgeRoles" :class="$style.noteHeaderBadgeRoles">
|
||||
<img v-for="(role, i) in appearNote.user.badgeRoles" :key="i" v-tooltip="role.name" :class="$style.noteHeaderBadgeRole" :src="role.iconUrl!"/>
|
||||
</div>
|
||||
</div>
|
||||
<MkInstanceTicker v-if="showTicker" :instance="appearNote.user.instance"/>
|
||||
</div>
|
||||
</header>
|
||||
|
@ -679,12 +686,30 @@ function loadConversation() {
|
|||
float: right;
|
||||
}
|
||||
|
||||
.noteHeaderUsernameAndBadgeRoles {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.noteHeaderUsername {
|
||||
margin-bottom: 2px;
|
||||
margin-right: 0.5em;
|
||||
line-height: 1.3;
|
||||
word-wrap: anywhere;
|
||||
}
|
||||
|
||||
.noteHeaderBadgeRoles {
|
||||
margin: 0 .5em 0 0;
|
||||
}
|
||||
|
||||
.noteHeaderBadgeRole {
|
||||
height: 1.3em;
|
||||
vertical-align: -20%;
|
||||
|
||||
& + .noteHeaderBadgeRole {
|
||||
margin-left: 0.2em;
|
||||
}
|
||||
}
|
||||
|
||||
.noteContent {
|
||||
container-type: inline-size;
|
||||
overflow-wrap: break-word;
|
||||
|
|
|
@ -1108,7 +1108,7 @@ defineExpose({
|
|||
&:focus-visible {
|
||||
outline: none;
|
||||
|
||||
.submitInner {
|
||||
> .submitInner {
|
||||
outline: 2px solid var(--MI_THEME-fgOnAccent);
|
||||
outline-offset: -4px;
|
||||
}
|
||||
|
@ -1123,13 +1123,13 @@ defineExpose({
|
|||
}
|
||||
|
||||
&:not(:disabled):hover {
|
||||
> .inner {
|
||||
> .submitInner {
|
||||
background: linear-gradient(90deg, hsl(from var(--MI_THEME-accent) h s calc(l + 5)), hsl(from var(--MI_THEME-accent) h s calc(l + 5)));
|
||||
}
|
||||
}
|
||||
|
||||
&:not(:disabled):active {
|
||||
> .inner {
|
||||
> .submitInner {
|
||||
background: linear-gradient(90deg, hsl(from var(--MI_THEME-accent) h s calc(l + 5)), hsl(from var(--MI_THEME-accent) h s calc(l + 5)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -277,7 +277,7 @@ async function onSubmit(): Promise<void> {
|
|||
return null;
|
||||
});
|
||||
|
||||
if (res) {
|
||||
if (res && res.ok) {
|
||||
if (res.status === 204 || instance.emailRequiredForSignup) {
|
||||
os.alert({
|
||||
type: 'success',
|
||||
|
@ -295,6 +295,8 @@ async function onSubmit(): Promise<void> {
|
|||
await login(resJson.token);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
onSignupApiError();
|
||||
}
|
||||
|
||||
submitting.value = false;
|
||||
|
|
|
@ -43,6 +43,7 @@ const props = withDefaults(defineProps<{
|
|||
}>(), {
|
||||
withRenotes: true,
|
||||
withReplies: false,
|
||||
withSensitive: true,
|
||||
onlyFiles: false,
|
||||
});
|
||||
|
||||
|
|
|
@ -272,6 +272,9 @@ const patronsWithIcon = [{
|
|||
}, {
|
||||
name: 'Yatoigawa',
|
||||
icon: 'https://assets.misskey-hub.net/patrons/505e3568885a4a488431a8f22b4553d0.jpg',
|
||||
}, {
|
||||
name: '秋瀬カヲル',
|
||||
icon: 'https://assets.misskey-hub.net/patrons/0f22aeb866484f4fa51db6721e3f9847.jpg',
|
||||
}];
|
||||
|
||||
const patrons = [
|
||||
|
@ -380,6 +383,7 @@ const patrons = [
|
|||
'ケモナーのケシン',
|
||||
'こまつぶり',
|
||||
'まゆつな空高',
|
||||
'asata',
|
||||
];
|
||||
|
||||
const thereIsTreasure = ref($i && !claimedAchievements.includes('foundTreasure'));
|
||||
|
|
|
@ -627,6 +627,7 @@ definePageMetadata(() => ({
|
|||
<style lang="scss" module>
|
||||
.ip {
|
||||
display: flex;
|
||||
word-break: break-all;
|
||||
|
||||
> :global(.date) {
|
||||
opacity: 0.7;
|
||||
|
|
|
@ -103,7 +103,7 @@ const headerActions = computed(() => []);
|
|||
const headerTabs = computed(() => []);
|
||||
|
||||
definePageMetadata(() => ({
|
||||
title: announcement.value ? `${i18n.ts.announcements}: ${announcement.value.title}` : i18n.ts.announcements,
|
||||
title: announcement.value ? announcement.value.title : i18n.ts.announcements,
|
||||
icon: 'ti ti-speakerphone',
|
||||
}));
|
||||
</script>
|
||||
|
|
|
@ -62,7 +62,7 @@ function accepted() {
|
|||
state.value = 'accepted';
|
||||
if (session.value && session.value.app.callbackUrl) {
|
||||
const url = new URL(session.value.app.callbackUrl);
|
||||
if (['javascript:', 'file:', 'data:', 'mailto:', 'tel:'].includes(url.protocol)) throw new Error('invalid url');
|
||||
if (['javascript:', 'file:', 'data:', 'mailto:', 'tel:', 'vbscript:'].includes(url.protocol)) throw new Error('invalid url');
|
||||
location.href = `${session.value.app.callbackUrl}?token=${session.value.token}`;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,7 +65,7 @@ async function onAccept(token: string) {
|
|||
|
||||
if (props.callback && props.callback !== '') {
|
||||
const cbUrl = new URL(props.callback);
|
||||
if (['javascript:', 'file:', 'data:', 'mailto:', 'tel:'].includes(cbUrl.protocol)) throw new Error('invalid url');
|
||||
if (['javascript:', 'file:', 'data:', 'mailto:', 'tel:', 'vbscript:'].includes(cbUrl.protocol)) throw new Error('invalid url');
|
||||
cbUrl.searchParams.set('session', props.session);
|
||||
location.href = cbUrl.toString();
|
||||
} else {
|
||||
|
|
|
@ -44,7 +44,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<MkButton transparent :class="$style.testButton" :disabled="!(active && event_renote)" @click="test('renote')"><i class="ti ti-send"></i></MkButton>
|
||||
</div>
|
||||
<div :class="$style.switchBox">
|
||||
<MkSwitch v-model="event_reaction">{{ i18n.ts._webhookSettings._events.reaction }}</MkSwitch>
|
||||
<MkSwitch v-model="event_reaction" :disabled="true">{{ i18n.ts._webhookSettings._events.reaction }}</MkSwitch>
|
||||
<MkButton transparent :class="$style.testButton" :disabled="!(active && event_reaction)" @click="test('reaction')"><i class="ti ti-send"></i></MkButton>
|
||||
</div>
|
||||
<div :class="$style.switchBox">
|
||||
|
|
|
@ -27,7 +27,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<MkSwitch v-model="event_note">{{ i18n.ts._webhookSettings._events.note }}</MkSwitch>
|
||||
<MkSwitch v-model="event_reply">{{ i18n.ts._webhookSettings._events.reply }}</MkSwitch>
|
||||
<MkSwitch v-model="event_renote">{{ i18n.ts._webhookSettings._events.renote }}</MkSwitch>
|
||||
<MkSwitch v-model="event_reaction">{{ i18n.ts._webhookSettings._events.reaction }}</MkSwitch>
|
||||
<MkSwitch v-model="event_reaction" :disabled="true">{{ i18n.ts._webhookSettings._events.reaction }}</MkSwitch>
|
||||
<MkSwitch v-model="event_mention">{{ i18n.ts._webhookSettings._events.mention }}</MkSwitch>
|
||||
</div>
|
||||
</FormSection>
|
||||
|
|
|
@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<div :class="$style.tl">
|
||||
<MkTimeline
|
||||
ref="tlComponent"
|
||||
:key="src + withRenotes + withReplies + onlyFiles"
|
||||
:key="src + withRenotes + withReplies + onlyFiles + withSensitive"
|
||||
:src="src.split(':')[0]"
|
||||
:list="src.split(':')[1]"
|
||||
:withRenotes="withRenotes"
|
||||
|
|
|
@ -241,9 +241,13 @@ export class Storage<T extends StateDef> {
|
|||
* 特定のキーの、簡易的なgetter/setterを作ります
|
||||
* 主にvue上で設定コントロールのmodelとして使う用
|
||||
*/
|
||||
public makeGetterSetter<K extends keyof T>(key: K, getter?: (v: T[K]) => unknown, setter?: (v: unknown) => T[K]): {
|
||||
get: () => T[K]['default'];
|
||||
set: (value: T[K]['default']) => void;
|
||||
public makeGetterSetter<K extends keyof T, R = T[K]['default']>(
|
||||
key: K,
|
||||
getter?: (v: T[K]['default']) => R,
|
||||
setter?: (v: R) => T[K]['default'],
|
||||
): {
|
||||
get: () => R;
|
||||
set: (value: R) => void;
|
||||
} {
|
||||
const valueRef = ref(this.state[key]);
|
||||
|
||||
|
@ -265,7 +269,7 @@ export class Storage<T extends StateDef> {
|
|||
return valueRef.value;
|
||||
}
|
||||
},
|
||||
set: (value: unknown) => {
|
||||
set: (value) => {
|
||||
const val = setter ? setter(value) : value;
|
||||
this.set(key, val);
|
||||
valueRef.value = val;
|
||||
|
|
|
@ -3,22 +3,22 @@
|
|||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { defaultStore } from '@/store.js';
|
||||
|
||||
await defaultStore.ready;
|
||||
export type DeviceKind = 'smartphone' | 'tablet' | 'desktop';
|
||||
|
||||
const ua = navigator.userAgent.toLowerCase();
|
||||
const isTablet = /ipad/.test(ua) || (/mobile|iphone|android/.test(ua) && window.innerWidth > 700);
|
||||
const isSmartphone = !isTablet && /mobile|iphone|android/.test(ua);
|
||||
|
||||
const isIPhone = /iphone|ipod/gi.test(ua) && navigator.maxTouchPoints > 1;
|
||||
// navigator.platform may be deprecated but this check is still required
|
||||
const isIPadOS = navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1;
|
||||
const isIos = /ipad|iphone|ipod/gi.test(ua) && navigator.maxTouchPoints > 1;
|
||||
export const DEFAULT_DEVICE_KIND: DeviceKind = (
|
||||
isSmartphone
|
||||
? 'smartphone'
|
||||
: isTablet
|
||||
? 'tablet'
|
||||
: 'desktop'
|
||||
);
|
||||
|
||||
export const isFullscreenNotSupported = isIPhone || isIos;
|
||||
export let deviceKind: DeviceKind = DEFAULT_DEVICE_KIND;
|
||||
|
||||
export const deviceKind: 'smartphone' | 'tablet' | 'desktop' = defaultStore.state.overridedDeviceKind ? defaultStore.state.overridedDeviceKind
|
||||
: isSmartphone ? 'smartphone'
|
||||
: isTablet ? 'tablet'
|
||||
: 'desktop';
|
||||
export function updateDeviceKind(kind: DeviceKind | null) {
|
||||
deviceKind = kind ?? DEFAULT_DEVICE_KIND;
|
||||
}
|
||||
|
|
46
packages/frontend/src/scripts/fullscreen.ts
Normal file
46
packages/frontend/src/scripts/fullscreen.ts
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
type PartiallyPartial<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
|
||||
|
||||
type VideoEl = PartiallyPartial<HTMLVideoElement, 'requestFullscreen'> & {
|
||||
webkitEnterFullscreen?(): void;
|
||||
webkitExitFullscreen?(): void;
|
||||
};
|
||||
|
||||
type PlayerEl = PartiallyPartial<HTMLElement, 'requestFullscreen'>;
|
||||
|
||||
type RequestFullscreenProps = {
|
||||
readonly videoEl: VideoEl;
|
||||
readonly playerEl: PlayerEl;
|
||||
readonly options?: FullscreenOptions | null;
|
||||
};
|
||||
|
||||
type ExitFullscreenProps = {
|
||||
readonly videoEl: VideoEl;
|
||||
};
|
||||
|
||||
export const requestFullscreen = ({ videoEl, playerEl, options }: RequestFullscreenProps) => {
|
||||
if (playerEl.requestFullscreen != null) {
|
||||
playerEl.requestFullscreen(options ?? undefined);
|
||||
return;
|
||||
}
|
||||
if (videoEl.webkitEnterFullscreen != null) {
|
||||
videoEl.webkitEnterFullscreen();
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
export const exitFullscreen = ({ videoEl }: ExitFullscreenProps) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (document.exitFullscreen != null) {
|
||||
document.exitFullscreen();
|
||||
return;
|
||||
}
|
||||
if (videoEl.webkitExitFullscreen != null) {
|
||||
videoEl.webkitExitFullscreen();
|
||||
return;
|
||||
}
|
||||
};
|
|
@ -8,8 +8,9 @@ import * as Misskey from 'misskey-js';
|
|||
import { hemisphere } from '@@/js/intl-const.js';
|
||||
import lightTheme from '@@/themes/l-light.json5';
|
||||
import darkTheme from '@@/themes/d-green-lime.json5';
|
||||
import { miLocalStorage } from './local-storage.js';
|
||||
import type { SoundType } from '@/scripts/sound.js';
|
||||
import { DEFAULT_DEVICE_KIND, type DeviceKind } from '@/scripts/device-kind.js';
|
||||
import { miLocalStorage } from '@/local-storage.js';
|
||||
import { Storage } from '@/pizzax.js';
|
||||
import type { Ast } from '@syuilo/aiscript';
|
||||
|
||||
|
@ -207,7 +208,7 @@ export const defaultStore = markRaw(new Storage('base', {
|
|||
|
||||
overridedDeviceKind: {
|
||||
where: 'device',
|
||||
default: null as null | 'smartphone' | 'tablet' | 'desktop',
|
||||
default: null as DeviceKind | null,
|
||||
},
|
||||
serverDisconnectedBehavior: {
|
||||
where: 'device',
|
||||
|
@ -263,11 +264,11 @@ export const defaultStore = markRaw(new Storage('base', {
|
|||
},
|
||||
useBlurEffectForModal: {
|
||||
where: 'device',
|
||||
default: !/mobile|iphone|android/.test(navigator.userAgent.toLowerCase()), // 循環参照するのでdevice-kind.tsは参照できない
|
||||
default: DEFAULT_DEVICE_KIND === 'desktop',
|
||||
},
|
||||
useBlurEffect: {
|
||||
where: 'device',
|
||||
default: !/mobile|iphone|android/.test(navigator.userAgent.toLowerCase()), // 循環参照するのでdevice-kind.tsは参照できない
|
||||
default: DEFAULT_DEVICE_KIND === 'desktop',
|
||||
},
|
||||
showFixedPostForm: {
|
||||
where: 'device',
|
||||
|
|
|
@ -22,16 +22,16 @@
|
|||
"lint": "pnpm typecheck && pnpm eslint"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/matter-js": "0.19.6",
|
||||
"@types/matter-js": "0.19.7",
|
||||
"@types/seedrandom": "3.0.8",
|
||||
"@types/node": "20.11.5",
|
||||
"@types/node": "22.9.0",
|
||||
"@typescript-eslint/eslint-plugin": "7.1.0",
|
||||
"@typescript-eslint/parser": "7.1.0",
|
||||
"nodemon": "3.0.2",
|
||||
"execa": "8.0.1",
|
||||
"typescript": "5.3.3",
|
||||
"esbuild": "0.19.11",
|
||||
"glob": "10.3.10"
|
||||
"nodemon": "3.1.7",
|
||||
"execa": "9.5.1",
|
||||
"typescript": "5.6.3",
|
||||
"esbuild": "0.24.0",
|
||||
"glob": "11.0.0"
|
||||
},
|
||||
"files": [
|
||||
"built"
|
||||
|
|
|
@ -8,14 +8,14 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@readme/openapi-parser": "2.6.0",
|
||||
"@types/node": "20.9.1",
|
||||
"@types/node": "22.9.0",
|
||||
"@typescript-eslint/eslint-plugin": "7.17.0",
|
||||
"@typescript-eslint/parser": "7.17.0",
|
||||
"openapi-types": "12.1.3",
|
||||
"openapi-typescript": "6.7.3",
|
||||
"ts-case-convert": "2.0.7",
|
||||
"ts-case-convert": "2.1.0",
|
||||
"tsx": "4.4.0",
|
||||
"typescript": "5.6.2"
|
||||
"typescript": "5.6.3"
|
||||
},
|
||||
"files": [
|
||||
"built"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"type": "module",
|
||||
"name": "misskey-js",
|
||||
"version": "2024.10.2-alpha.2",
|
||||
"version": "2024.11.0-alpha.0",
|
||||
"description": "Misskey SDK for JavaScript",
|
||||
"license": "MIT",
|
||||
"main": "./built/index.js",
|
||||
|
@ -35,10 +35,10 @@
|
|||
"directory": "packages/misskey-js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@microsoft/api-extractor": "7.47.9",
|
||||
"@swc/jest": "0.2.36",
|
||||
"@types/jest": "29.5.13",
|
||||
"@types/node": "20.14.12",
|
||||
"@microsoft/api-extractor": "7.47.11",
|
||||
"@swc/jest": "0.2.37",
|
||||
"@types/jest": "29.5.14",
|
||||
"@types/node": "22.9.0",
|
||||
"@typescript-eslint/eslint-plugin": "7.17.0",
|
||||
"@typescript-eslint/parser": "7.17.0",
|
||||
"jest": "29.7.0",
|
||||
|
@ -47,17 +47,17 @@
|
|||
"mock-socket": "9.3.1",
|
||||
"ncp": "2.0.0",
|
||||
"nodemon": "3.1.7",
|
||||
"execa": "9.4.0",
|
||||
"execa": "9.5.1",
|
||||
"tsd": "0.31.2",
|
||||
"typescript": "5.6.2",
|
||||
"esbuild": "0.23.1",
|
||||
"typescript": "5.6.3",
|
||||
"esbuild": "0.24.0",
|
||||
"glob": "11.0.0"
|
||||
},
|
||||
"files": [
|
||||
"built"
|
||||
],
|
||||
"dependencies": {
|
||||
"@simplewebauthn/types": "10.0.0",
|
||||
"@simplewebauthn/types": "11.0.0",
|
||||
"eventemitter3": "5.0.1",
|
||||
"reconnecting-websocket": "4.4.0"
|
||||
}
|
||||
|
|
|
@ -22,14 +22,14 @@
|
|||
"lint": "pnpm typecheck && pnpm eslint"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "20.11.5",
|
||||
"@types/node": "22.9.0",
|
||||
"@typescript-eslint/eslint-plugin": "7.1.0",
|
||||
"@typescript-eslint/parser": "7.1.0",
|
||||
"execa": "8.0.1",
|
||||
"nodemon": "3.0.2",
|
||||
"typescript": "5.3.3",
|
||||
"esbuild": "0.19.11",
|
||||
"glob": "10.3.10"
|
||||
"execa": "9.5.1",
|
||||
"nodemon": "3.1.7",
|
||||
"typescript": "5.6.3",
|
||||
"esbuild": "0.24.0",
|
||||
"glob": "11.0.0"
|
||||
},
|
||||
"files": [
|
||||
"built"
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
"lint": "pnpm typecheck && pnpm eslint"
|
||||
},
|
||||
"dependencies": {
|
||||
"esbuild": "0.23.1",
|
||||
"esbuild": "0.24.0",
|
||||
"idb-keyval": "6.2.1",
|
||||
"misskey-js": "workspace:*"
|
||||
},
|
||||
|
@ -18,7 +18,7 @@
|
|||
"@typescript/lib-webworker": "npm:@types/serviceworker@0.0.67",
|
||||
"eslint-plugin-import": "2.30.0",
|
||||
"nodemon": "3.1.7",
|
||||
"typescript": "5.6.2"
|
||||
"typescript": "5.6.3"
|
||||
},
|
||||
"type": "module"
|
||||
}
|
||||
|
|
5410
pnpm-lock.yaml
5410
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
|
@ -9,15 +9,15 @@
|
|||
"test:coverage": "vitest run --coverage"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/mdast": "4.0.3",
|
||||
"@types/node": "20.10.7",
|
||||
"@types/mdast": "4.0.4",
|
||||
"@types/node": "22.9.0",
|
||||
"@vitest/coverage-v8": "1.1.3",
|
||||
"mdast-util-to-string": "4.0.0",
|
||||
"remark": "15.0.1",
|
||||
"remark-parse": "11.0.0",
|
||||
"typescript": "5.3.3",
|
||||
"unified": "11.0.4",
|
||||
"vite": "5.4.6",
|
||||
"typescript": "5.6.3",
|
||||
"unified": "11.0.5",
|
||||
"vite": "5.4.11",
|
||||
"vite-node": "1.1.3",
|
||||
"vitest": "1.1.3"
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue