Merge upstream
This commit is contained in:
commit
f761a454d3
63 changed files with 3131 additions and 3096 deletions
4
.github/unused/test-backend.yml
vendored
4
.github/unused/test-backend.yml
vendored
|
@ -57,7 +57,7 @@ jobs:
|
||||||
- name: Install FFmpeg
|
- name: Install FFmpeg
|
||||||
uses: FedericoCarboni/setup-ffmpeg@v3
|
uses: FedericoCarboni/setup-ffmpeg@v3
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v4.2.0
|
uses: actions/setup-node@v4.3.0
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
|
@ -116,7 +116,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
run_install: false
|
run_install: false
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v4.2.0
|
uses: actions/setup-node@v4.3.0
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
|
|
2
.github/workflows/api-misskey-js.yml
vendored
2
.github/workflows/api-misskey-js.yml
vendored
|
@ -26,7 +26,7 @@ jobs:
|
||||||
run_install: false
|
run_install: false
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v4.2.0
|
uses: actions/setup-node@v4.3.0
|
||||||
with:
|
with:
|
||||||
node-version-file: '.node-version'
|
node-version-file: '.node-version'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
|
|
6
.github/workflows/lint.yml
vendored
6
.github/workflows/lint.yml
vendored
|
@ -29,7 +29,7 @@ jobs:
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v4
|
||||||
with:
|
with:
|
||||||
run_install: false
|
run_install: false
|
||||||
- uses: actions/setup-node@v4.2.0
|
- uses: actions/setup-node@v4.3.0
|
||||||
with:
|
with:
|
||||||
node-version-file: '.node-version'
|
node-version-file: '.node-version'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
|
@ -54,7 +54,7 @@ jobs:
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v4
|
||||||
with:
|
with:
|
||||||
run_install: false
|
run_install: false
|
||||||
- uses: actions/setup-node@v4.2.0
|
- uses: actions/setup-node@v4.3.0
|
||||||
with:
|
with:
|
||||||
node-version-file: '.node-version'
|
node-version-file: '.node-version'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
|
@ -78,7 +78,7 @@ jobs:
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v4
|
||||||
with:
|
with:
|
||||||
run_install: false
|
run_install: false
|
||||||
- uses: actions/setup-node@v4.2.0
|
- uses: actions/setup-node@v4.3.0
|
||||||
with:
|
with:
|
||||||
node-version-file: '.node-version'
|
node-version-file: '.node-version'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
|
|
2
.github/workflows/test-frontend.yml
vendored
2
.github/workflows/test-frontend.yml
vendored
|
@ -37,7 +37,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
run_install: false
|
run_install: false
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v4.2.0
|
uses: actions/setup-node@v4.3.0
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
|
|
2
.github/workflows/test-misskey-js.yml
vendored
2
.github/workflows/test-misskey-js.yml
vendored
|
@ -36,7 +36,7 @@ jobs:
|
||||||
run_install: false
|
run_install: false
|
||||||
|
|
||||||
- name: Setup Node.js ${{ matrix.node-version }}
|
- name: Setup Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v4.2.0
|
uses: actions/setup-node@v4.3.0
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
|
|
2
.github/workflows/test-production.yml
vendored
2
.github/workflows/test-production.yml
vendored
|
@ -27,7 +27,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
run_install: false
|
run_install: false
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v4.2.0
|
uses: actions/setup-node@v4.3.0
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
|
|
2
.github/workflows/validate-api-json.yml
vendored
2
.github/workflows/validate-api-json.yml
vendored
|
@ -28,7 +28,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
run_install: false
|
run_install: false
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v4.2.0
|
uses: actions/setup-node@v4.3.0
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
|
|
18
Dockerfile
18
Dockerfile
|
@ -1,10 +1,8 @@
|
||||||
# syntax = docker/dockerfile:1.4
|
# syntax = docker/dockerfile:1.4
|
||||||
|
|
||||||
ARG NODE_VERSION=22
|
|
||||||
|
|
||||||
# build assets & compile TypeScript
|
# build assets & compile TypeScript
|
||||||
|
|
||||||
FROM --platform=$BUILDPLATFORM node:${NODE_VERSION} AS native-builder
|
FROM --platform=$BUILDPLATFORM node:22 AS native-builder
|
||||||
|
|
||||||
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
|
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
|
||||||
--mount=type=cache,target=/var/lib/apt,sharing=locked \
|
--mount=type=cache,target=/var/lib/apt,sharing=locked \
|
||||||
|
@ -17,7 +15,7 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
|
||||||
WORKDIR /misskey
|
WORKDIR /misskey
|
||||||
|
|
||||||
COPY --link pnpm-lock.yaml ./
|
COPY --link pnpm-lock.yaml ./
|
||||||
RUN npm install -g pnpm
|
RUN npm install -g pnpm@10
|
||||||
RUN --mount=type=cache,target=/root/.local/share/pnpm/store,sharing=locked \
|
RUN --mount=type=cache,target=/root/.local/share/pnpm/store,sharing=locked \
|
||||||
pnpm fetch --ignore-scripts
|
pnpm fetch --ignore-scripts
|
||||||
|
|
||||||
|
@ -30,7 +28,7 @@ COPY --link ["packages/misskey-js/package.json", "./packages/misskey-js/"]
|
||||||
COPY --link ["packages/misskey-reversi/package.json", "./packages/misskey-reversi/"]
|
COPY --link ["packages/misskey-reversi/package.json", "./packages/misskey-reversi/"]
|
||||||
COPY --link ["packages/misskey-bubble-game/package.json", "./packages/misskey-bubble-game/"]
|
COPY --link ["packages/misskey-bubble-game/package.json", "./packages/misskey-bubble-game/"]
|
||||||
|
|
||||||
RUN pnpm i --frozen-lockfile --aggregate-output --offline \
|
RUN pnpm i --frozen-lockfile --aggregate-output --prefer-offline \
|
||||||
&& pnpm rebuild -r
|
&& pnpm rebuild -r
|
||||||
|
|
||||||
COPY --link . ./
|
COPY --link . ./
|
||||||
|
@ -39,7 +37,7 @@ RUN NODE_ENV=production pnpm build
|
||||||
|
|
||||||
# build native dependencies for target platform
|
# build native dependencies for target platform
|
||||||
|
|
||||||
FROM --platform=$TARGETPLATFORM node:${NODE_VERSION} AS target-builder
|
FROM --platform=$TARGETPLATFORM node:22 AS target-builder
|
||||||
|
|
||||||
RUN apt-get update \
|
RUN apt-get update \
|
||||||
&& apt-get install -yqq --no-install-recommends \
|
&& apt-get install -yqq --no-install-recommends \
|
||||||
|
@ -48,7 +46,7 @@ RUN apt-get update \
|
||||||
WORKDIR /misskey
|
WORKDIR /misskey
|
||||||
|
|
||||||
COPY --link pnpm-lock.yaml ./
|
COPY --link pnpm-lock.yaml ./
|
||||||
RUN npm install -g pnpm
|
RUN npm install -g pnpm@10
|
||||||
RUN --mount=type=cache,target=/root/.local/share/pnpm/store,sharing=locked \
|
RUN --mount=type=cache,target=/root/.local/share/pnpm/store,sharing=locked \
|
||||||
pnpm fetch --ignore-scripts
|
pnpm fetch --ignore-scripts
|
||||||
|
|
||||||
|
@ -59,10 +57,10 @@ COPY --link ["packages/misskey-js/package.json", "./packages/misskey-js/"]
|
||||||
COPY --link ["packages/misskey-reversi/package.json", "./packages/misskey-reversi/"]
|
COPY --link ["packages/misskey-reversi/package.json", "./packages/misskey-reversi/"]
|
||||||
COPY --link ["packages/misskey-bubble-game/package.json", "./packages/misskey-bubble-game/"]
|
COPY --link ["packages/misskey-bubble-game/package.json", "./packages/misskey-bubble-game/"]
|
||||||
|
|
||||||
RUN pnpm i --frozen-lockfile --aggregate-output --offline \
|
RUN pnpm i --frozen-lockfile --aggregate-output --prefer-offline \
|
||||||
&& pnpm rebuild -r
|
&& pnpm rebuild -r
|
||||||
|
|
||||||
FROM --platform=$TARGETPLATFORM node:${NODE_VERSION}-slim AS runner
|
FROM --platform=$TARGETPLATFORM node:22-slim AS runner
|
||||||
|
|
||||||
ARG UID="991"
|
ARG UID="991"
|
||||||
ARG GID="991"
|
ARG GID="991"
|
||||||
|
@ -81,7 +79,7 @@ RUN apt-get update \
|
||||||
WORKDIR /misskey
|
WORKDIR /misskey
|
||||||
|
|
||||||
COPY --chown=misskey:misskey pnpm-lock.yaml ./
|
COPY --chown=misskey:misskey pnpm-lock.yaml ./
|
||||||
RUN npm install -g pnpm
|
RUN npm install -g pnpm@10
|
||||||
|
|
||||||
COPY --chown=misskey:misskey --from=target-builder /misskey/node_modules ./node_modules
|
COPY --chown=misskey:misskey --from=target-builder /misskey/node_modules ./node_modules
|
||||||
COPY --chown=misskey:misskey --from=target-builder /misskey/packages/backend/node_modules ./packages/backend/node_modules
|
COPY --chown=misskey:misskey --from=target-builder /misskey/packages/backend/node_modules ./packages/backend/node_modules
|
||||||
|
|
|
@ -1354,6 +1354,10 @@ scheduled: "Scheduled"
|
||||||
unschedule: "Unschedule"
|
unschedule: "Unschedule"
|
||||||
setScheduledTime: "Set scheduled time"
|
setScheduledTime: "Set scheduled time"
|
||||||
willBePostedAt: "Note will be posted at {x}"
|
willBePostedAt: "Note will be posted at {x}"
|
||||||
|
sensitiveByModerator: "This file has been marked as sensitive by the administrator.\nFor more information, please check the [NSFW Guidelines](https://go.misskey.io/media-guideline)."
|
||||||
|
thisInfoIsNotVisibleOtherUser: "This information is not visible to other users."
|
||||||
|
flushItAway: "Let it go"
|
||||||
|
deleteNotWash: "Deleting this won't make everything let bygones be bygones, but..."
|
||||||
|
|
||||||
_bubbleGame:
|
_bubbleGame:
|
||||||
howToPlay: "How to play"
|
howToPlay: "How to play"
|
||||||
|
@ -2544,6 +2548,7 @@ _notification:
|
||||||
renotedBySomeUsers: "Renote from {n} users"
|
renotedBySomeUsers: "Renote from {n} users"
|
||||||
followedBySomeUsers: "Followed by {n} users"
|
followedBySomeUsers: "Followed by {n} users"
|
||||||
flushNotification: "Clear notifications"
|
flushNotification: "Clear notifications"
|
||||||
|
sensitiveFlagAssigned: "Your file has been marked as sensitive"
|
||||||
_types:
|
_types:
|
||||||
all: "All"
|
all: "All"
|
||||||
note: "New notes"
|
note: "New notes"
|
||||||
|
|
21
locales/index.d.ts
vendored
21
locales/index.d.ts
vendored
|
@ -5564,6 +5564,23 @@ export interface Locale extends ILocale {
|
||||||
* {x}に投稿されます
|
* {x}に投稿されます
|
||||||
*/
|
*/
|
||||||
"willBePostedAt": ParameterizedString<"x">;
|
"willBePostedAt": ParameterizedString<"x">;
|
||||||
|
/**
|
||||||
|
* 管理者によって、ドライブのファイルがセンシティブとして設定されました。
|
||||||
|
* 詳細については、[NSFWガイドライン](https://go.misskey.io/media-guideline)を確認してください。
|
||||||
|
*/
|
||||||
|
"sensitiveByModerator": string;
|
||||||
|
/**
|
||||||
|
* この情報は他のユーザーには公開されません。
|
||||||
|
*/
|
||||||
|
"thisInfoIsNotVisibleOtherUser": string;
|
||||||
|
/**
|
||||||
|
* 水に流す
|
||||||
|
*/
|
||||||
|
"flushItAway": string;
|
||||||
|
/**
|
||||||
|
* 削除をしても全てが水に流れるわけではありませんが…
|
||||||
|
*/
|
||||||
|
"deleteNotWash": string;
|
||||||
"_bubbleGame": {
|
"_bubbleGame": {
|
||||||
/**
|
/**
|
||||||
* 遊び方
|
* 遊び方
|
||||||
|
@ -10089,6 +10106,10 @@ export interface Locale extends ILocale {
|
||||||
* 通知の履歴をリセットする
|
* 通知の履歴をリセットする
|
||||||
*/
|
*/
|
||||||
"flushNotification": string;
|
"flushNotification": string;
|
||||||
|
/**
|
||||||
|
* ファイルがセンシティブとして設定されました
|
||||||
|
*/
|
||||||
|
"sensitiveFlagAssigned": string;
|
||||||
"_types": {
|
"_types": {
|
||||||
/**
|
/**
|
||||||
* すべて
|
* すべて
|
||||||
|
|
|
@ -1384,6 +1384,10 @@ scheduled: "予約済み"
|
||||||
unschedule: "予約を解除"
|
unschedule: "予約を解除"
|
||||||
setScheduledTime: "予約日時を設定"
|
setScheduledTime: "予約日時を設定"
|
||||||
willBePostedAt: "{x}に投稿されます"
|
willBePostedAt: "{x}に投稿されます"
|
||||||
|
sensitiveByModerator: "管理者によって、ドライブのファイルがセンシティブとして設定されました。\n詳細については、[NSFWガイドライン](https://go.misskey.io/media-guideline)を確認してください。"
|
||||||
|
thisInfoIsNotVisibleOtherUser: "この情報は他のユーザーには公開されません。"
|
||||||
|
flushItAway: "水に流す"
|
||||||
|
deleteNotWash: "削除をしても全てが水に流れるわけではありませんが…"
|
||||||
|
|
||||||
_bubbleGame:
|
_bubbleGame:
|
||||||
howToPlay: "遊び方"
|
howToPlay: "遊び方"
|
||||||
|
@ -2651,6 +2655,7 @@ _notification:
|
||||||
renotedBySomeUsers: "{n}人がリノートしました"
|
renotedBySomeUsers: "{n}人がリノートしました"
|
||||||
followedBySomeUsers: "{n}人にフォローされました"
|
followedBySomeUsers: "{n}人にフォローされました"
|
||||||
flushNotification: "通知の履歴をリセットする"
|
flushNotification: "通知の履歴をリセットする"
|
||||||
|
sensitiveFlagAssigned: "ファイルがセンシティブとして設定されました"
|
||||||
|
|
||||||
_types:
|
_types:
|
||||||
all: "すべて"
|
all: "すべて"
|
||||||
|
|
|
@ -1374,6 +1374,10 @@ scheduled: "예약됨"
|
||||||
unschedule: "예약 취소"
|
unschedule: "예약 취소"
|
||||||
setScheduledTime: "예약 시간 설정"
|
setScheduledTime: "예약 시간 설정"
|
||||||
willBePostedAt: "{x}에 게시됩니다"
|
willBePostedAt: "{x}에 게시됩니다"
|
||||||
|
sensitiveByModerator: "관리자에 의해 드라이브의 파일이 열람주의로 설정되었습니다.\n자세한 내용은 [NSFW 가이드라인](https://go.misskey.io/media-guideline)을 확인해 주세요."
|
||||||
|
thisInfoIsNotVisibleOtherUser: "이 정보는 다른 사용자에게 공개되지 않습니다."
|
||||||
|
flushItAway: "묻어두기"
|
||||||
|
deleteNotWash: "삭제해도 모든 것이 없었던 일이 되는 것은 아닙니다만..."
|
||||||
|
|
||||||
_bubbleGame:
|
_bubbleGame:
|
||||||
howToPlay: "설명"
|
howToPlay: "설명"
|
||||||
|
@ -2574,6 +2578,7 @@ _notification:
|
||||||
renotedBySomeUsers: "{n}명이 리노트함"
|
renotedBySomeUsers: "{n}명이 리노트함"
|
||||||
followedBySomeUsers: "{n}명이 팔로우함"
|
followedBySomeUsers: "{n}명이 팔로우함"
|
||||||
flushNotification: "알림 이력을 초기화"
|
flushNotification: "알림 이력을 초기화"
|
||||||
|
sensitiveFlagAssigned: "파일이 열람주의로 설정되었습니다"
|
||||||
_types:
|
_types:
|
||||||
all: "전부"
|
all: "전부"
|
||||||
note: "유저의 새 글"
|
note: "유저의 새 글"
|
||||||
|
|
24
package.json
24
package.json
|
@ -6,7 +6,7 @@
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://git.psec.dev/oscar-surf/misskey.git"
|
"url": "https://git.psec.dev/oscar-surf/misskey.git"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@9.15.4",
|
"packageManager": "pnpm@10.7.0",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"packages/frontend",
|
"packages/frontend",
|
||||||
"packages/backend",
|
"packages/backend",
|
||||||
|
@ -46,38 +46,42 @@
|
||||||
"cleanall": "pnpm clean-all"
|
"cleanall": "pnpm clean-all"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
|
"@aiscript-dev/aiscript-languageserver": "github:aiscript-dev/aiscript-languageserver#0.1.6",
|
||||||
"@tensorflow/tfjs-core": "4.22.0",
|
"@tensorflow/tfjs-core": "4.22.0",
|
||||||
"axios": "1.7.9",
|
"axios": "1.8.4",
|
||||||
"chokidar": "4.0.3",
|
"chokidar": "4.0.3",
|
||||||
"cookie": "1.0.2",
|
"cookie": "1.0.2",
|
||||||
"cookie-signature": "1.2.2",
|
"cookie-signature": "1.2.2",
|
||||||
"debug": "4.4.0",
|
"debug": "4.4.0",
|
||||||
"esbuild": "0.24.2",
|
"esbuild": "0.25.2",
|
||||||
"jpeg-js": "0.4.4",
|
"jpeg-js": "0.4.4",
|
||||||
"lodash": "4.17.21",
|
"lodash": "4.17.21",
|
||||||
"sharp": "0.33.5",
|
"sharp": "0.33.5",
|
||||||
"tough-cookie": "5.1.0",
|
"tough-cookie": "5.1.2",
|
||||||
"web-streams-polyfill": "4.1.0"
|
"web-streams-polyfill": "4.1.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cssnano": "7.0.6",
|
"cssnano": "7.0.6",
|
||||||
"execa": "9.5.2",
|
"execa": "9.5.2",
|
||||||
"js-yaml": "4.1.0",
|
"js-yaml": "4.1.0",
|
||||||
"postcss": "8.5.1",
|
"postcss": "8.5.3",
|
||||||
"terser": "5.37.0",
|
"terser": "5.39.0",
|
||||||
"typescript": "5.7.3"
|
"typescript": "5.8.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "22.10.7",
|
"@types/node": "22.13.14",
|
||||||
"@typescript-eslint/eslint-plugin": "7.10.0",
|
"@typescript-eslint/eslint-plugin": "7.10.0",
|
||||||
"@typescript-eslint/parser": "7.10.0",
|
"@typescript-eslint/parser": "7.10.0",
|
||||||
"cross-env": "7.0.3",
|
"cross-env": "7.0.3",
|
||||||
"cypress": "13.17.0",
|
"cypress": "14.2.1",
|
||||||
"eslint": "8.57.1",
|
"eslint": "8.57.1",
|
||||||
"ncp": "2.0.0",
|
"ncp": "2.0.0",
|
||||||
"start-server-and-test": "2.0.10"
|
"start-server-and-test": "2.0.11"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@tensorflow/tfjs-core": "4.22.0"
|
"@tensorflow/tfjs-core": "4.22.0"
|
||||||
|
},
|
||||||
|
"pnpm": {
|
||||||
|
"neverBuiltDependencies": []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
13
packages/backend/migration/1739335129758-sensitiveFlag.js
Normal file
13
packages/backend/migration/1739335129758-sensitiveFlag.js
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
export class SensitiveFlag1739335129758 {
|
||||||
|
name = 'SensitiveFlag1739335129758'
|
||||||
|
|
||||||
|
async up(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "drive_file" ADD "isSensitiveByModerator" boolean NOT NULL DEFAULT false`);
|
||||||
|
await queryRunner.query(`CREATE INDEX "IDX_e779d1afdfa44dc3d64213cd2e" ON "drive_file" ("isSensitiveByModerator") `);
|
||||||
|
}
|
||||||
|
|
||||||
|
async down(queryRunner) {
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_e779d1afdfa44dc3d64213cd2e"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "drive_file" DROP COLUMN "isSensitiveByModerator"`);
|
||||||
|
}
|
||||||
|
}
|
|
@ -33,16 +33,16 @@
|
||||||
"generate-api-json": "pnpm build && node ./scripts/generate_api_json.js"
|
"generate-api-json": "pnpm build && node ./scripts/generate_api_json.js"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@swc/core-darwin-arm64": "1.10.12",
|
"@swc/core-darwin-arm64": "1.11.13",
|
||||||
"@swc/core-darwin-x64": "1.10.12",
|
"@swc/core-darwin-x64": "1.11.13",
|
||||||
"@swc/core-linux-arm-gnueabihf": "1.10.12",
|
"@swc/core-linux-arm-gnueabihf": "1.11.13",
|
||||||
"@swc/core-linux-arm64-gnu": "1.10.12",
|
"@swc/core-linux-arm64-gnu": "1.11.13",
|
||||||
"@swc/core-linux-arm64-musl": "1.10.12",
|
"@swc/core-linux-arm64-musl": "1.11.13",
|
||||||
"@swc/core-linux-x64-gnu": "1.10.12",
|
"@swc/core-linux-x64-gnu": "1.11.13",
|
||||||
"@swc/core-linux-x64-musl": "1.10.12",
|
"@swc/core-linux-x64-musl": "1.11.13",
|
||||||
"@swc/core-win32-arm64-msvc": "1.10.12",
|
"@swc/core-win32-arm64-msvc": "1.11.13",
|
||||||
"@swc/core-win32-ia32-msvc": "1.10.12",
|
"@swc/core-win32-ia32-msvc": "1.11.13",
|
||||||
"@swc/core-win32-x64-msvc": "1.10.12",
|
"@swc/core-win32-x64-msvc": "1.11.13",
|
||||||
"@tensorflow/tfjs": "4.22.0",
|
"@tensorflow/tfjs": "4.22.0",
|
||||||
"@tensorflow/tfjs-node": "4.22.0",
|
"@tensorflow/tfjs-node": "4.22.0",
|
||||||
"bufferutil": "4.0.9",
|
"bufferutil": "4.0.9",
|
||||||
|
@ -63,94 +63,94 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@authenio/samlify-node-xmllint": "2.0.0",
|
"@authenio/samlify-node-xmllint": "2.0.0",
|
||||||
"@aws-sdk/client-s3": "3.740.0",
|
"@aws-sdk/client-s3": "3.777.0",
|
||||||
"@aws-sdk/lib-storage": "3.740.0",
|
"@aws-sdk/lib-storage": "3.777.0",
|
||||||
"@bull-board/api": "6.7.4",
|
"@bull-board/api": "6.7.10",
|
||||||
"@bull-board/fastify": "6.7.4",
|
"@bull-board/fastify": "6.7.10",
|
||||||
"@bull-board/ui": "6.7.4",
|
"@bull-board/ui": "6.7.10",
|
||||||
"@discordapp/twemoji": "15.1.0",
|
"@discordapp/twemoji": "15.1.0",
|
||||||
"@elastic/elasticsearch": "8.17.0",
|
"@elastic/elasticsearch": "8.17.1",
|
||||||
"@fastify/accepts": "5.0.2",
|
"@fastify/accepts": "5.0.2",
|
||||||
"@fastify/cookie": "11.0.2",
|
"@fastify/cookie": "11.0.2",
|
||||||
"@fastify/cors": "10.0.2",
|
"@fastify/cors": "11.0.1",
|
||||||
"@fastify/express": "4.0.2",
|
"@fastify/express": "4.0.2",
|
||||||
"@fastify/formbody": "8.0.2",
|
"@fastify/formbody": "8.0.2",
|
||||||
"@fastify/http-proxy": "11.0.1",
|
"@fastify/http-proxy": "11.1.2",
|
||||||
"@fastify/multipart": "9.0.3",
|
"@fastify/multipart": "9.0.3",
|
||||||
"@fastify/static": "8.0.4",
|
"@fastify/static": "8.1.1",
|
||||||
"@fastify/view": "10.0.2",
|
"@fastify/view": "11.0.0",
|
||||||
"@misskey-dev/sharp-read-bmp": "1.2.0",
|
"@misskey-dev/sharp-read-bmp": "1.2.0",
|
||||||
"@misskey-dev/summaly": "github:MisskeyIO/summaly#5.1.3",
|
"@misskey-dev/summaly": "github:MisskeyIO/summaly#5.1.3",
|
||||||
"@napi-rs/canvas": "0.1.65",
|
"@napi-rs/canvas": "0.1.68",
|
||||||
"@nestjs/common": "11.0.7",
|
"@nestjs/common": "11.0.12",
|
||||||
"@nestjs/core": "11.0.7",
|
"@nestjs/core": "11.0.12",
|
||||||
"@nestjs/testing": "11.0.7",
|
"@nestjs/testing": "11.0.12",
|
||||||
"@peertube/http-signature": "1.7.0",
|
"@peertube/http-signature": "1.7.0",
|
||||||
"@simplewebauthn/server": "13.1.1",
|
"@simplewebauthn/server": "13.1.1",
|
||||||
"@sinonjs/fake-timers": "11.3.1",
|
"@sinonjs/fake-timers": "11.3.1",
|
||||||
"@smithy/node-http-handler": "4.0.2",
|
"@smithy/node-http-handler": "4.0.4",
|
||||||
"@swc/cli": "0.6.0",
|
"@swc/cli": "0.6.0",
|
||||||
"@swc/core": "1.10.12",
|
"@swc/core": "1.11.13",
|
||||||
"@twemoji/parser": "15.1.1",
|
"@twemoji/parser": "15.1.1",
|
||||||
"accepts": "1.3.8",
|
"accepts": "1.3.8",
|
||||||
"ajv": "8.17.1",
|
"ajv": "8.17.1",
|
||||||
"archiver": "7.0.1",
|
"archiver": "7.0.1",
|
||||||
"async-mutex": "0.5.0",
|
"async-mutex": "0.5.0",
|
||||||
"bcryptjs": "2.4.3",
|
"bcryptjs": "3.0.2",
|
||||||
"blurhash": "2.0.5",
|
"blurhash": "2.0.5",
|
||||||
"body-parser": "1.20.3",
|
"body-parser": "2.2.0",
|
||||||
"bullmq": "5.39.1",
|
"bullmq": "5.45.2",
|
||||||
"cacheable-lookup": "7.0.0",
|
"cacheable-lookup": "7.0.0",
|
||||||
"cbor": "10.0.3",
|
"cbor": "10.0.3",
|
||||||
"chalk": "5.4.1",
|
"chalk": "5.4.1",
|
||||||
"chalk-template": "1.1.0",
|
"chalk-template": "1.1.0",
|
||||||
"chokidar": "4.0.3",
|
"chokidar": "4.0.3",
|
||||||
"cli-highlight": "2.1.11",
|
"cli-highlight": "2.1.11",
|
||||||
"color-convert": "2.0.1",
|
"color-convert": "3.0.1",
|
||||||
"content-disposition": "0.5.4",
|
"content-disposition": "0.5.4",
|
||||||
"date-fns": "4.1.0",
|
"date-fns": "4.1.0",
|
||||||
"deep-email-validator": "0.1.21",
|
"deep-email-validator": "0.1.21",
|
||||||
"fastify": "5.2.1",
|
"fastify": "5.2.2",
|
||||||
"fastify-http-errors-enhanced": "6.0.1",
|
"fastify-http-errors-enhanced": "6.0.1",
|
||||||
"fastify-raw-body": "5.0.0",
|
"fastify-raw-body": "5.0.0",
|
||||||
"feed": "4.2.2",
|
"feed": "4.2.2",
|
||||||
"file-type": "20.0.1",
|
"file-type": "20.4.1",
|
||||||
"fluent-ffmpeg": "2.1.3",
|
"fluent-ffmpeg": "2.1.3",
|
||||||
"form-data": "4.0.1",
|
"form-data": "4.0.2",
|
||||||
"got": "14.4.5",
|
"got": "14.4.6",
|
||||||
"hpagent": "1.2.0",
|
"hpagent": "1.2.0",
|
||||||
"htmlescape": "1.1.1",
|
"htmlescape": "1.1.1",
|
||||||
"http-link-header": "1.1.3",
|
"http-link-header": "1.1.3",
|
||||||
"ioredis": "5.4.2",
|
"ioredis": "5.6.0",
|
||||||
"ip-cidr": "4.0.2",
|
"ip-cidr": "4.0.2",
|
||||||
"ipaddr.js": "2.2.0",
|
"ipaddr.js": "2.2.0",
|
||||||
"is-svg": "5.1.0",
|
"is-svg": "5.1.0",
|
||||||
"jose": "5.9.6",
|
"jose": "6.0.10",
|
||||||
"js-yaml": "4.1.0",
|
"js-yaml": "4.1.0",
|
||||||
"jsdom": "26.0.0",
|
"jsdom": "26.0.0",
|
||||||
"json5": "2.2.3",
|
"json5": "2.2.3",
|
||||||
"jsonld": "8.3.3",
|
"jsonld": "8.3.3",
|
||||||
"jsrsasign": "11.1.0",
|
"jsrsasign": "11.1.0",
|
||||||
"meilisearch": "0.48.2",
|
"meilisearch": "0.49.0",
|
||||||
"mfm-js": "0.24.0",
|
"mfm-js": "0.24.0",
|
||||||
"microformats-parser": "2.0.2",
|
"microformats-parser": "2.0.2",
|
||||||
"mime-types": "2.1.35",
|
"mime-types": "3.0.1",
|
||||||
"misskey-js": "workspace:*",
|
"misskey-js": "workspace:*",
|
||||||
"misskey-reversi": "workspace:*",
|
"misskey-reversi": "workspace:*",
|
||||||
"ms": "3.0.0-canary.1",
|
"ms": "3.0.0-canary.1",
|
||||||
"nanoid": "5.0.9",
|
"nanoid": "5.1.5",
|
||||||
"nested-property": "4.0.0",
|
"nested-property": "4.0.0",
|
||||||
"node-fetch": "3.3.2",
|
"node-fetch": "3.3.2",
|
||||||
"node-forge": "1.3.1",
|
"node-forge": "1.3.1",
|
||||||
"nodemailer": "6.10.0",
|
"nodemailer": "6.10.0",
|
||||||
"nsfwjs": "4.2.0",
|
"nsfwjs": "4.2.0",
|
||||||
"oauth": "0.10.0",
|
"oauth": "0.10.2",
|
||||||
"oauth2orize": "1.12.0",
|
"oauth2orize": "1.12.0",
|
||||||
"oauth2orize-pkce": "0.1.2",
|
"oauth2orize-pkce": "0.1.2",
|
||||||
"os-utils": "0.0.14",
|
"os-utils": "0.0.14",
|
||||||
"otpauth": "9.3.6",
|
"otpauth": "9.4.0",
|
||||||
"parse5": "7.2.1",
|
"parse5": "7.2.1",
|
||||||
"pg": "8.13.1",
|
"pg": "8.14.1",
|
||||||
"pino": "9.6.0",
|
"pino": "9.6.0",
|
||||||
"pino-pretty": "13.0.0",
|
"pino-pretty": "13.0.0",
|
||||||
"pkce-challenge": "4.1.0",
|
"pkce-challenge": "4.1.0",
|
||||||
|
@ -166,10 +166,10 @@
|
||||||
"reflect-metadata": "0.2.2",
|
"reflect-metadata": "0.2.2",
|
||||||
"rename": "1.0.4",
|
"rename": "1.0.4",
|
||||||
"rss-parser": "3.13.0",
|
"rss-parser": "3.13.0",
|
||||||
"rxjs": "7.8.1",
|
"rxjs": "7.8.2",
|
||||||
"samlify": "2.8.11",
|
"samlify": "2.9.1",
|
||||||
"sanitize-html": "2.14.0",
|
"sanitize-html": "2.15.0",
|
||||||
"secure-json-parse": "3.0.2",
|
"secure-json-parse": "4.0.0",
|
||||||
"sharp": "0.33.5",
|
"sharp": "0.33.5",
|
||||||
"slacc": "0.0.10",
|
"slacc": "0.0.10",
|
||||||
"strict-event-emitter-types": "2.0.0",
|
"strict-event-emitter-types": "2.0.0",
|
||||||
|
@ -177,25 +177,24 @@
|
||||||
"systeminformation": "5.25.11",
|
"systeminformation": "5.25.11",
|
||||||
"tinycolor2": "1.6.0",
|
"tinycolor2": "1.6.0",
|
||||||
"tmp": "0.2.3",
|
"tmp": "0.2.3",
|
||||||
"tsc-alias": "1.8.10",
|
"tsc-alias": "1.8.13",
|
||||||
"tsconfig-paths": "4.2.0",
|
"tsconfig-paths": "4.2.0",
|
||||||
"typeorm": "0.3.20",
|
"typeorm": "0.3.21",
|
||||||
"typescript": "5.7.3",
|
"typescript": "5.8.2",
|
||||||
"ulid": "2.3.0",
|
"ulid": "3.0.0",
|
||||||
"vary": "1.1.2",
|
"vary": "1.1.2",
|
||||||
"web-push": "3.6.7",
|
"web-push": "3.6.7",
|
||||||
"ws": "8.18.0",
|
"ws": "8.18.1",
|
||||||
"xev": "3.0.2",
|
"xev": "3.0.2",
|
||||||
"xmlbuilder": "15.1.1"
|
"xmlbuilder": "15.1.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@jest/globals": "29.7.0",
|
"@jest/globals": "29.7.0",
|
||||||
"@misskey-dev/eslint-plugin": "1.0.0",
|
"@misskey-dev/eslint-plugin": "1.0.0",
|
||||||
"@nestjs/platform-express": "11.0.7",
|
"@nestjs/platform-express": "11.0.12",
|
||||||
"@swc/jest": "0.2.37",
|
"@swc/jest": "0.2.37",
|
||||||
"@types/accepts": "1.3.7",
|
"@types/accepts": "1.3.7",
|
||||||
"@types/archiver": "6.0.3",
|
"@types/archiver": "6.0.3",
|
||||||
"@types/bcryptjs": "2.4.6",
|
|
||||||
"@types/body-parser": "1.19.5",
|
"@types/body-parser": "1.19.5",
|
||||||
"@types/color-convert": "2.0.4",
|
"@types/color-convert": "2.0.4",
|
||||||
"@types/content-disposition": "0.5.8",
|
"@types/content-disposition": "0.5.8",
|
||||||
|
@ -209,7 +208,7 @@
|
||||||
"@types/jsrsasign": "10.5.15",
|
"@types/jsrsasign": "10.5.15",
|
||||||
"@types/mime-types": "2.1.4",
|
"@types/mime-types": "2.1.4",
|
||||||
"@types/ms": "2.1.0",
|
"@types/ms": "2.1.0",
|
||||||
"@types/node": "22.13.0",
|
"@types/node": "22.13.14",
|
||||||
"@types/node-forge": "1.3.11",
|
"@types/node-forge": "1.3.11",
|
||||||
"@types/nodemailer": "6.4.17",
|
"@types/nodemailer": "6.4.17",
|
||||||
"@types/oauth": "0.9.6",
|
"@types/oauth": "0.9.6",
|
||||||
|
@ -223,15 +222,15 @@
|
||||||
"@types/random-seed": "0.3.5",
|
"@types/random-seed": "0.3.5",
|
||||||
"@types/ratelimiter": "3.4.6",
|
"@types/ratelimiter": "3.4.6",
|
||||||
"@types/rename": "1.0.7",
|
"@types/rename": "1.0.7",
|
||||||
"@types/sanitize-html": "2.13.0",
|
"@types/sanitize-html": "2.15.0",
|
||||||
"@types/semver": "7.5.8",
|
"@types/semver": "7.7.0",
|
||||||
"@types/simple-oauth2": "5.0.7",
|
"@types/simple-oauth2": "5.0.7",
|
||||||
"@types/sinonjs__fake-timers": "8.1.5",
|
"@types/sinonjs__fake-timers": "8.1.5",
|
||||||
"@types/tinycolor2": "1.4.6",
|
"@types/tinycolor2": "1.4.6",
|
||||||
"@types/tmp": "0.2.6",
|
"@types/tmp": "0.2.6",
|
||||||
"@types/vary": "1.1.3",
|
"@types/vary": "1.1.3",
|
||||||
"@types/web-push": "3.6.4",
|
"@types/web-push": "3.6.4",
|
||||||
"@types/ws": "8.5.14",
|
"@types/ws": "8.18.0",
|
||||||
"@typescript-eslint/eslint-plugin": "7.10.0",
|
"@typescript-eslint/eslint-plugin": "7.10.0",
|
||||||
"@typescript-eslint/parser": "7.10.0",
|
"@typescript-eslint/parser": "7.10.0",
|
||||||
"aws-sdk-client-mock": "4.1.0",
|
"aws-sdk-client-mock": "4.1.0",
|
||||||
|
|
|
@ -44,6 +44,7 @@ import { correctFilename } from '@/misc/correct-filename.js';
|
||||||
import { isMimeImage } from '@/misc/is-mime-image.js';
|
import { isMimeImage } from '@/misc/is-mime-image.js';
|
||||||
import { ModerationLogService } from '@/core/ModerationLogService.js';
|
import { ModerationLogService } from '@/core/ModerationLogService.js';
|
||||||
import { LoggerService } from '@/core/LoggerService.js';
|
import { LoggerService } from '@/core/LoggerService.js';
|
||||||
|
import { NotificationService } from '@/core/NotificationService.js';
|
||||||
|
|
||||||
type AddFileArgs = {
|
type AddFileArgs = {
|
||||||
/** User who wish to add file */
|
/** User who wish to add file */
|
||||||
|
@ -129,6 +130,7 @@ export class DriveService {
|
||||||
private driveChart: DriveChart,
|
private driveChart: DriveChart,
|
||||||
private perUserDriveChart: PerUserDriveChart,
|
private perUserDriveChart: PerUserDriveChart,
|
||||||
private instanceChart: InstanceChart,
|
private instanceChart: InstanceChart,
|
||||||
|
private notificationService: NotificationService,
|
||||||
) {
|
) {
|
||||||
const logger = this.loggerService.getLogger('drive', 'blue');
|
const logger = this.loggerService.getLogger('drive', 'blue');
|
||||||
this.registerLogger = logger.createSubLogger('register', 'yellow');
|
this.registerLogger = logger.createSubLogger('register', 'yellow');
|
||||||
|
@ -146,8 +148,10 @@ export class DriveService {
|
||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
private async save(file: MiDriveFile, path: string, name: string, type: string, hash: string, size: number): Promise<MiDriveFile> {
|
private async save(file: MiDriveFile, path: string, name: string, type: string, hash: string, size: number): Promise<MiDriveFile> {
|
||||||
|
const fileType = type.split(';')[0];
|
||||||
|
|
||||||
// thunbnail, webpublic を必要なら生成
|
// thunbnail, webpublic を必要なら生成
|
||||||
const alts = await this.generateAlts(path, type, !file.uri);
|
const alts = await this.generateAlts(path, fileType, !file.uri);
|
||||||
|
|
||||||
const meta = await this.metaService.fetch();
|
const meta = await this.metaService.fetch();
|
||||||
|
|
||||||
|
@ -156,17 +160,17 @@ export class DriveService {
|
||||||
let [ext] = (name.match(/\.([a-zA-Z0-9_-]+)$/) ?? ['']);
|
let [ext] = (name.match(/\.([a-zA-Z0-9_-]+)$/) ?? ['']);
|
||||||
|
|
||||||
if (ext === '') {
|
if (ext === '') {
|
||||||
if (type === 'image/jpeg') ext = '.jpg';
|
if (fileType === 'image/jpeg') ext = '.jpg';
|
||||||
if (type === 'image/png') ext = '.png';
|
if (fileType === 'image/png') ext = '.png';
|
||||||
if (type === 'image/webp') ext = '.webp';
|
if (fileType === 'image/webp') ext = '.webp';
|
||||||
if (type === 'image/avif') ext = '.avif';
|
if (fileType === 'image/avif') ext = '.avif';
|
||||||
if (type === 'image/apng') ext = '.apng';
|
if (fileType === 'image/apng') ext = '.apng';
|
||||||
if (type === 'image/vnd.mozilla.apng') ext = '.apng';
|
if (fileType === 'image/vnd.mozilla.apng') ext = '.apng';
|
||||||
}
|
}
|
||||||
|
|
||||||
// 拡張子からContent-Typeを設定してそうな挙動を示すオブジェクトストレージ (upcloud?) も存在するので、
|
// 拡張子からContent-Typeを設定してそうな挙動を示すオブジェクトストレージ (upcloud?) も存在するので、
|
||||||
// 許可されているファイル形式でしかURLに拡張子をつけない
|
// 許可されているファイル形式でしかURLに拡張子をつけない
|
||||||
if (!FILE_TYPE_BROWSERSAFE.includes(type)) {
|
if (!FILE_TYPE_BROWSERSAFE.includes(fileType)) {
|
||||||
ext = '';
|
ext = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -373,8 +377,10 @@ export class DriveService {
|
||||||
*/
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
private async upload(key: string, stream: fs.ReadStream | Buffer, type: string, ext?: string | null, filename?: string) {
|
private async upload(key: string, stream: fs.ReadStream | Buffer, type: string, ext?: string | null, filename?: string) {
|
||||||
if (type === 'image/apng') type = 'image/png';
|
const fileType = type.split(';')[0];
|
||||||
if (!FILE_TYPE_BROWSERSAFE.includes(type)) type = 'application/octet-stream';
|
|
||||||
|
if (fileType === 'image/apng') type = 'image/png';
|
||||||
|
if (!FILE_TYPE_BROWSERSAFE.includes(fileType)) type = 'application/octet-stream';
|
||||||
|
|
||||||
const meta = await this.metaService.fetch();
|
const meta = await this.metaService.fetch();
|
||||||
|
|
||||||
|
@ -660,13 +666,15 @@ export class DriveService {
|
||||||
@bindThis
|
@bindThis
|
||||||
public async updateFile(file: MiDriveFile, values: Partial<MiDriveFile>, updater: MiUser) {
|
public async updateFile(file: MiDriveFile, values: Partial<MiDriveFile>, updater: MiUser) {
|
||||||
const alwaysMarkNsfw = (await this.roleService.getUserPolicies(file.userId)).alwaysMarkNsfw;
|
const alwaysMarkNsfw = (await this.roleService.getUserPolicies(file.userId)).alwaysMarkNsfw;
|
||||||
|
const isModerator = await this.roleService.isModerator(updater);
|
||||||
|
|
||||||
if (values.name != null && !this.driveFileEntityService.validateFileName(values.name)) {
|
if (values.name != null && !this.driveFileEntityService.validateFileName(values.name)) {
|
||||||
throw new DriveService.InvalidFileNameError();
|
throw new DriveService.InvalidFileNameError();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (values.isSensitive !== undefined && values.isSensitive !== file.isSensitive && alwaysMarkNsfw && !values.isSensitive) {
|
if (values.isSensitive !== undefined && values.isSensitive !== file.isSensitive && !values.isSensitive) {
|
||||||
throw new DriveService.CannotUnmarkSensitiveError();
|
if (alwaysMarkNsfw) throw new DriveService.CannotUnmarkSensitiveError();
|
||||||
|
if (file.isSensitiveByModerator && (file.userId === updater.id)) throw new DriveService.CannotUnmarkSensitiveError();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (values.folderId != null) {
|
if (values.folderId != null) {
|
||||||
|
@ -680,6 +688,10 @@ export class DriveService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isModerator && file.userId !== updater.id) {
|
||||||
|
values.isSensitiveByModerator = values.isSensitive;
|
||||||
|
}
|
||||||
|
|
||||||
await this.driveFilesRepository.update(file.id, values);
|
await this.driveFilesRepository.update(file.id, values);
|
||||||
|
|
||||||
const fileObj = await this.driveFileEntityService.pack(file.id, updater, { self: true });
|
const fileObj = await this.driveFileEntityService.pack(file.id, updater, { self: true });
|
||||||
|
@ -689,7 +701,7 @@ export class DriveService {
|
||||||
this.globalEventService.publishDriveStream(file.userId, 'fileUpdated', fileObj);
|
this.globalEventService.publishDriveStream(file.userId, 'fileUpdated', fileObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (await this.roleService.isModerator(updater) && (file.userId !== updater.id)) {
|
if (isModerator && (file.userId !== updater.id)) {
|
||||||
if (values.isSensitive !== undefined && values.isSensitive !== file.isSensitive) {
|
if (values.isSensitive !== undefined && values.isSensitive !== file.isSensitive) {
|
||||||
const user = file.userId ? await this.usersRepository.findOneByOrFail({ id: file.userId }) : null;
|
const user = file.userId ? await this.usersRepository.findOneByOrFail({ id: file.userId }) : null;
|
||||||
if (values.isSensitive) {
|
if (values.isSensitive) {
|
||||||
|
@ -699,6 +711,11 @@ export class DriveService {
|
||||||
fileUserUsername: user?.username ?? null,
|
fileUserUsername: user?.username ?? null,
|
||||||
fileUserHost: user?.host ?? null,
|
fileUserHost: user?.host ?? null,
|
||||||
});
|
});
|
||||||
|
if (file.userId) {
|
||||||
|
this.notificationService.createNotification(file.userId, 'sensitiveFlagAssigned', {
|
||||||
|
fileId: file.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
this.moderationLogService.log(updater, 'unmarkSensitiveDriveFile', {
|
this.moderationLogService.log(updater, 'unmarkSensitiveDriveFile', {
|
||||||
fileId: file.id,
|
fileId: file.id,
|
||||||
|
|
|
@ -69,7 +69,8 @@ function compileQuery(q: Q): string {
|
||||||
export class SearchService {
|
export class SearchService {
|
||||||
private readonly meilisearchIndexScope: 'local' | 'global' | string[] = 'local';
|
private readonly meilisearchIndexScope: 'local' | 'global' | string[] = 'local';
|
||||||
private meilisearchNoteIndex: Index | null = null;
|
private meilisearchNoteIndex: Index | null = null;
|
||||||
private elasticsearchNoteIndex: string | null = null;
|
private readonly elasticsearchNoteIndex: string;
|
||||||
|
private readonly elasticsearchIdField: string;
|
||||||
private logger: Logger;
|
private logger: Logger;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -121,7 +122,9 @@ export class SearchService {
|
||||||
},
|
},
|
||||||
});*/
|
});*/
|
||||||
} else if (this.elasticsearch) {
|
} else if (this.elasticsearch) {
|
||||||
this.elasticsearchNoteIndex = `${config.elasticsearch!.index}---notes`;
|
this.elasticsearchNoteIndex = `${config.elasticsearch!.index}`;
|
||||||
|
this.elasticsearchIdField = `${config.host}_id`;
|
||||||
|
/* 外部からindexさせるのでこの処理は不要
|
||||||
this.elasticsearch.indices.exists({
|
this.elasticsearch.indices.exists({
|
||||||
index: this.elasticsearchNoteIndex,
|
index: this.elasticsearchNoteIndex,
|
||||||
}).then((indexExists: boolean) => {
|
}).then((indexExists: boolean) => {
|
||||||
|
@ -175,6 +178,7 @@ export class SearchService {
|
||||||
}).catch((error: any) => {
|
}).catch((error: any) => {
|
||||||
this.logger.error('Error while checking if index exists', error);
|
this.logger.error('Error while checking if index exists', error);
|
||||||
});
|
});
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,6 +217,7 @@ export class SearchService {
|
||||||
primaryKey: 'id',
|
primaryKey: 'id',
|
||||||
});
|
});
|
||||||
} else if (this.elasticsearch) {
|
} else if (this.elasticsearch) {
|
||||||
|
/* 外部からindexさせるのでこの処理は不要
|
||||||
const body = {
|
const body = {
|
||||||
createdAt: createdAt.getTime(),
|
createdAt: createdAt.getTime(),
|
||||||
userId: note.userId,
|
userId: note.userId,
|
||||||
|
@ -229,6 +234,7 @@ export class SearchService {
|
||||||
}).catch((error: any) => {
|
}).catch((error: any) => {
|
||||||
this.logger.error(error);
|
this.logger.error(error);
|
||||||
});
|
});
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -239,12 +245,14 @@ export class SearchService {
|
||||||
if (this.meilisearch) {
|
if (this.meilisearch) {
|
||||||
await this.meilisearchNoteIndex?.deleteDocument(note.id);
|
await this.meilisearchNoteIndex?.deleteDocument(note.id);
|
||||||
} else if (this.elasticsearch) {
|
} else if (this.elasticsearch) {
|
||||||
|
/* 外部からindexさせるのでこの処理は不要
|
||||||
await this.elasticsearch.delete({
|
await this.elasticsearch.delete({
|
||||||
index: `${this.elasticsearchNoteIndex}-${this.idService.parse(note.id).date.toISOString().slice(0, 7).replace(/-/g, '')}`,
|
index: `${this.elasticsearchNoteIndex}-${this.idService.parse(note.id).date.toISOString().slice(0, 7).replace(/-/g, '')}`,
|
||||||
id: note.id,
|
id: note.id,
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
this.logger.error(error);
|
this.logger.error(error);
|
||||||
});
|
});
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -346,7 +354,7 @@ export class SearchService {
|
||||||
if (opts.channelId) esFilter.bool.must.push({ term: { channelId: opts.channelId } });
|
if (opts.channelId) esFilter.bool.must.push({ term: { channelId: opts.channelId } });
|
||||||
if (opts.host) {
|
if (opts.host) {
|
||||||
if (opts.host === '.') {
|
if (opts.host === '.') {
|
||||||
esFilter.bool.must.push({ bool: { must_not: [{ exists: { field: 'userHost' } }] } });
|
esFilter.bool.must.push({ term: { userHost: this.config.host } });
|
||||||
} else {
|
} else {
|
||||||
esFilter.bool.must.push({ term: { userHost: opts.host } });
|
esFilter.bool.must.push({ term: { userHost: opts.host } });
|
||||||
}
|
}
|
||||||
|
@ -358,8 +366,6 @@ export class SearchService {
|
||||||
should: [
|
should: [
|
||||||
{ wildcard: { 'text': { value: q } } },
|
{ wildcard: { 'text': { value: q } } },
|
||||||
{ simple_query_string: { fields: ['text'], 'query': q, default_operator: 'and' } },
|
{ simple_query_string: { fields: ['text'], 'query': q, default_operator: 'and' } },
|
||||||
{ wildcard: { 'cw': { value: q } } },
|
|
||||||
{ simple_query_string: { fields: ['cw'], 'query': q, default_operator: 'and' } },
|
|
||||||
],
|
],
|
||||||
minimum_should_match: 1,
|
minimum_should_match: 1,
|
||||||
},
|
},
|
||||||
|
@ -368,15 +374,16 @@ export class SearchService {
|
||||||
|
|
||||||
const res = await (this.elasticsearch.search)({
|
const res = await (this.elasticsearch.search)({
|
||||||
index: this.elasticsearchNoteIndex + '*' as string,
|
index: this.elasticsearchNoteIndex + '*' as string,
|
||||||
body: {
|
|
||||||
query: esFilter,
|
query: esFilter,
|
||||||
sort: [{ createdAt: { order: 'desc' } }],
|
sort: [{ createdAt: { order: 'desc' } }],
|
||||||
},
|
_source: ['id', 'createdAt', this.elasticsearchIdField],
|
||||||
_source: ['id', 'createdAt'],
|
|
||||||
size: pagination.limit,
|
size: pagination.limit,
|
||||||
});
|
});
|
||||||
|
|
||||||
const noteIds = res.hits.hits.map((hit: any) => hit._id);
|
const noteIds = res.hits.hits.map((hit) => {
|
||||||
|
const source = hit._source as Record<string, unknown>;
|
||||||
|
return (source[this.elasticsearchIdField] as string) || null;
|
||||||
|
}).filter((id): id is string => id !== null);
|
||||||
if (noteIds.length === 0) return [];
|
if (noteIds.length === 0) return [];
|
||||||
const notes = await this.notesRepository.findBy({
|
const notes = await this.notesRepository.findBy({
|
||||||
id: In(noteIds),
|
id: In(noteIds),
|
||||||
|
|
|
@ -210,6 +210,9 @@ export class DriveFileEntityService {
|
||||||
md5: file.md5,
|
md5: file.md5,
|
||||||
size: file.size,
|
size: file.size,
|
||||||
isSensitive: file.isSensitive,
|
isSensitive: file.isSensitive,
|
||||||
|
...(opts.detail ? {
|
||||||
|
isSensitiveByModerator: file.isSensitiveByModerator,
|
||||||
|
} : {}),
|
||||||
blurhash: file.blurhash,
|
blurhash: file.blurhash,
|
||||||
properties: opts.self ? file.properties : this.getPublicProperties(file),
|
properties: opts.self ? file.properties : this.getPublicProperties(file),
|
||||||
url: opts.self ? file.url : this.getPublicUrl(file),
|
url: opts.self ? file.url : this.getPublicUrl(file),
|
||||||
|
@ -246,6 +249,9 @@ export class DriveFileEntityService {
|
||||||
md5: file.md5,
|
md5: file.md5,
|
||||||
size: file.size,
|
size: file.size,
|
||||||
isSensitive: file.isSensitive,
|
isSensitive: file.isSensitive,
|
||||||
|
...(opts.detail ? {
|
||||||
|
isSensitiveByModerator: file.isSensitiveByModerator,
|
||||||
|
} : {}),
|
||||||
blurhash: file.blurhash,
|
blurhash: file.blurhash,
|
||||||
properties: opts.self ? file.properties : this.getPublicProperties(file),
|
properties: opts.self ? file.properties : this.getPublicProperties(file),
|
||||||
url: opts.self ? file.url : this.getPublicUrl(file),
|
url: opts.self ? file.url : this.getPublicUrl(file),
|
||||||
|
|
|
@ -183,6 +183,9 @@ export class NotificationEntityService implements OnModuleInit {
|
||||||
header: notification.customHeader,
|
header: notification.customHeader,
|
||||||
icon: notification.customIcon,
|
icon: notification.customIcon,
|
||||||
} : {}),
|
} : {}),
|
||||||
|
...(notification.type === 'sensitiveFlagAssigned' ? {
|
||||||
|
fileId: notification.fileId,
|
||||||
|
} : {}),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as fs from 'node:fs/promises';
|
import * as fs from 'node:fs/promises';
|
||||||
|
import { WritableStream } from 'node:stream/web';
|
||||||
import type { PathLike } from 'node:fs';
|
import type { PathLike } from 'node:fs';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -18,13 +18,13 @@ export async function genX509CertFromJWK(
|
||||||
cert.setIssuer(attrs);
|
cert.setIssuer(attrs);
|
||||||
cert.publicKey = await jose
|
cert.publicKey = await jose
|
||||||
.importJWK(JSON.parse(publicKey))
|
.importJWK(JSON.parse(publicKey))
|
||||||
.then((k) => jose.exportSPKI(k as jose.KeyLike))
|
.then((k) => jose.exportSPKI(k as jose.CryptoKey))
|
||||||
.then((k) => forge.pki.publicKeyFromPem(k));
|
.then((k) => forge.pki.publicKeyFromPem(k));
|
||||||
|
|
||||||
cert.sign(
|
cert.sign(
|
||||||
await jose
|
await jose
|
||||||
.importJWK(JSON.parse(privateKey))
|
.importJWK(JSON.parse(privateKey))
|
||||||
.then((k) => jose.exportPKCS8(k as jose.KeyLike))
|
.then((k) => jose.exportPKCS8(k as jose.CryptoKey))
|
||||||
.then((k) => forge.pki.privateKeyFromPem(k)),
|
.then((k) => forge.pki.privateKeyFromPem(k)),
|
||||||
forge.md.sha256.create(),
|
forge.md.sha256.create(),
|
||||||
);
|
);
|
||||||
|
|
|
@ -162,6 +162,12 @@ export class MiDriveFile {
|
||||||
})
|
})
|
||||||
public isSensitive: boolean;
|
public isSensitive: boolean;
|
||||||
|
|
||||||
|
@Index()
|
||||||
|
@Column('boolean', {
|
||||||
|
default: false,
|
||||||
|
})
|
||||||
|
public isSensitiveByModerator: boolean;
|
||||||
|
|
||||||
@Index()
|
@Index()
|
||||||
@Column('boolean', {
|
@Column('boolean', {
|
||||||
default: false,
|
default: false,
|
||||||
|
|
|
@ -93,6 +93,11 @@ export type MiNotification = {
|
||||||
id: string;
|
id: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
draftId: MiScheduledNote['id'];
|
draftId: MiScheduledNote['id'];
|
||||||
|
} | {
|
||||||
|
type: 'sensitiveFlagAssigned'
|
||||||
|
id: string;
|
||||||
|
fileId: string;
|
||||||
|
createdAt: string;
|
||||||
} | {
|
} | {
|
||||||
type: 'app';
|
type: 'app';
|
||||||
id: string;
|
id: string;
|
||||||
|
|
|
@ -42,6 +42,10 @@ export const packedDriveFileSchema = {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
},
|
},
|
||||||
|
isSensitiveByModerator: {
|
||||||
|
type: 'boolean',
|
||||||
|
optional: true, nullable: true,
|
||||||
|
},
|
||||||
blurhash: {
|
blurhash: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
optional: false, nullable: true,
|
optional: false, nullable: true,
|
||||||
|
|
|
@ -309,8 +309,8 @@ export const packedNotificationSchema = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
ref: 'NoteDraft',
|
ref: 'NoteDraft',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}, {
|
}, {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
|
@ -324,8 +324,8 @@ export const packedNotificationSchema = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
ref: 'Note',
|
ref: 'Note',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}, {
|
}, {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
|
@ -339,8 +339,21 @@ export const packedNotificationSchema = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
ref: 'NoteDraft',
|
ref: 'NoteDraft',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
|
}, {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
...baseSchema.properties,
|
||||||
|
type: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
enum: ['sensitiveFlagAssigned'],
|
||||||
|
},
|
||||||
|
fileId: {
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
}, {
|
}, {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
|
|
|
@ -349,7 +349,7 @@ export class FileServerService {
|
||||||
|
|
||||||
return reply.redirect(
|
return reply.redirect(
|
||||||
redirectUrl,
|
redirectUrl,
|
||||||
301,
|
302,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -186,7 +186,7 @@ export class ServerService implements OnApplicationShutdown {
|
||||||
|
|
||||||
return reply.redirect(
|
return reply.redirect(
|
||||||
url,
|
url,
|
||||||
301,
|
302,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -51,6 +51,12 @@ export const meta = {
|
||||||
code: 'RESTRICTED_BY_ROLE',
|
code: 'RESTRICTED_BY_ROLE',
|
||||||
id: '7f59dccb-f465-75ab-5cf4-3ce44e3282f7',
|
id: '7f59dccb-f465-75ab-5cf4-3ce44e3282f7',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
restrictedByModerator: {
|
||||||
|
message: 'The isSensitive specified by the administrator cannot be changed.',
|
||||||
|
code: 'RESTRICTED_BY_ADMINISTRATOR',
|
||||||
|
id: '20e6c501-e579-400d-97e4-1c7efc286f35',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
res: {
|
res: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
|
@ -105,7 +111,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
} else if (e instanceof DriveService.NoSuchFolderError) {
|
} else if (e instanceof DriveService.NoSuchFolderError) {
|
||||||
throw new ApiError(meta.errors.noSuchFolder);
|
throw new ApiError(meta.errors.noSuchFolder);
|
||||||
} else if (e instanceof DriveService.CannotUnmarkSensitiveError) {
|
} else if (e instanceof DriveService.CannotUnmarkSensitiveError) {
|
||||||
|
if (file.isSensitiveByModerator) {
|
||||||
|
throw new ApiError(meta.errors.restrictedByModerator);
|
||||||
|
} else {
|
||||||
throw new ApiError(meta.errors.restrictedByRole);
|
throw new ApiError(meta.errors.restrictedByRole);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,7 +105,7 @@ export default abstract class Channel {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract init(params: any): void;
|
public abstract init(params: any): Promise<void> | void;
|
||||||
|
|
||||||
public dispose?(): void;
|
public dispose?(): void;
|
||||||
|
|
||||||
|
|
|
@ -215,7 +215,7 @@ class OAuth2Store {
|
||||||
}
|
}
|
||||||
|
|
||||||
async load(req: OAuth2DecisionRequest, cb: (err: Error | null, txn?: OAuth2) => void): Promise<void> {
|
async load(req: OAuth2DecisionRequest, cb: (err: Error | null, txn?: OAuth2) => void): Promise<void> {
|
||||||
const { transaction_id } = req.body;
|
const { transaction_id } = req.body ?? {};
|
||||||
if (!transaction_id) {
|
if (!transaction_id) {
|
||||||
cb(new AuthorizationError('Missing transaction ID', 'invalid_request'));
|
cb(new AuthorizationError('Missing transaction ID', 'invalid_request'));
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -239,7 +239,7 @@ export class SAMLIdentifyProviderService {
|
||||||
metadata: await this.createIdPMetadataXml(ssoServiceProvider),
|
metadata: await this.createIdPMetadataXml(ssoServiceProvider),
|
||||||
privateKey: await jose
|
privateKey: await jose
|
||||||
.importJWK(JSON.parse(ssoServiceProvider.privateKey ?? '{}'))
|
.importJWK(JSON.parse(ssoServiceProvider.privateKey ?? '{}'))
|
||||||
.then(k => jose.exportPKCS8(k as jose.KeyLike)),
|
.then(k => jose.exportPKCS8(k as jose.CryptoKey)),
|
||||||
});
|
});
|
||||||
|
|
||||||
const sp = saml.ServiceProvider({
|
const sp = saml.ServiceProvider({
|
||||||
|
@ -393,7 +393,7 @@ export class SAMLIdentifyProviderService {
|
||||||
metadata: await this.createIdPMetadataXml(ssoServiceProvider),
|
metadata: await this.createIdPMetadataXml(ssoServiceProvider),
|
||||||
privateKey: await jose
|
privateKey: await jose
|
||||||
.importJWK(JSON.parse(ssoServiceProvider.privateKey ?? '{}'))
|
.importJWK(JSON.parse(ssoServiceProvider.privateKey ?? '{}'))
|
||||||
.then(k => jose.exportPKCS8(k as jose.KeyLike)),
|
.then(k => jose.exportPKCS8(k as jose.CryptoKey)),
|
||||||
loginResponseTemplate: { context: 'ignored' },
|
loginResponseTemplate: { context: 'ignored' },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ import type { MiNote } from '@/models/Note.js';
|
||||||
* noteScheduled - 予約投稿が予約された
|
* noteScheduled - 予約投稿が予約された
|
||||||
* scheduledNotePosted - 予約投稿が投稿された
|
* scheduledNotePosted - 予約投稿が投稿された
|
||||||
* scheduledNoteError - 予約投稿がエラーになった
|
* scheduledNoteError - 予約投稿がエラーになった
|
||||||
|
* sensitiveFlagAssigned - センシティブフラグが付与された
|
||||||
* app - アプリ通知
|
* app - アプリ通知
|
||||||
* test - テスト通知(サーバー側)
|
* test - テスト通知(サーバー側)
|
||||||
*/
|
*/
|
||||||
|
@ -45,6 +46,7 @@ export const notificationTypes = [
|
||||||
'noteScheduled',
|
'noteScheduled',
|
||||||
'scheduledNotePosted',
|
'scheduledNotePosted',
|
||||||
'scheduledNoteError',
|
'scheduledNoteError',
|
||||||
|
'sensitiveFlagAssigned',
|
||||||
'app',
|
'app',
|
||||||
'test',
|
'test',
|
||||||
] as const;
|
] as const;
|
||||||
|
|
|
@ -119,6 +119,7 @@ describe('Timelines', () => {
|
||||||
const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
|
const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
|
||||||
|
|
||||||
await api('following/create', { userId: bob.id }, alice);
|
await api('following/create', { userId: bob.id }, alice);
|
||||||
|
await api('following/create', { userId: carol.id }, bob);
|
||||||
await api('following/update', { userId: bob.id, withReplies: true }, alice);
|
await api('following/update', { userId: bob.id, withReplies: true }, alice);
|
||||||
await sleep(1000);
|
await sleep(1000);
|
||||||
const carolNote = await post(carol, { text: 'hi', visibility: 'followers' });
|
const carolNote = await post(carol, { text: 'hi', visibility: 'followers' });
|
||||||
|
|
|
@ -802,10 +802,10 @@ describe('ユーザー', () => {
|
||||||
{ label: '「見つけやすくする」がOFFのユーザーが含まれる', user: () => userNotExplorable },
|
{ label: '「見つけやすくする」がOFFのユーザーが含まれる', user: () => userNotExplorable },
|
||||||
{ label: 'ミュートユーザーが含まれない', user: () => userMutedByAlice, excluded: true },
|
{ label: 'ミュートユーザーが含まれない', user: () => userMutedByAlice, excluded: true },
|
||||||
{ label: 'ブロックされているユーザーが含まれる', user: () => userBlockedByAlice },
|
{ label: 'ブロックされているユーザーが含まれる', user: () => userBlockedByAlice },
|
||||||
{ label: 'ブロックしてきているユーザーが含まれない', user: () => userBlockingAlice, excluded: true },
|
// 無効なテスト { label: 'ブロックしてきているユーザーが含まれない', user: () => userBlockingAlice, excluded: true },
|
||||||
{ label: '承認制ユーザーが含まれる', user: () => userLocking },
|
{ label: '承認制ユーザーが含まれる', user: () => userLocking },
|
||||||
{ label: 'サイレンスユーザーが含まれる', user: () => userSilenced },
|
{ label: 'サイレンスユーザーが含まれる', user: () => userSilenced },
|
||||||
//{ label: 'サスペンドユーザーが含まれない', user: () => userSuspended, excluded: true },
|
// 無効なテスト { label: 'サスペンドユーザーが含まれない', user: () => userSuspended, excluded: true },
|
||||||
{ label: '削除済ユーザーが含まれる', user: () => userDeletedBySelf },
|
{ label: '削除済ユーザーが含まれる', user: () => userDeletedBySelf },
|
||||||
{ label: '削除済(byAdmin)ユーザーが含まれる', user: () => userDeletedByAdmin },
|
{ label: '削除済(byAdmin)ユーザーが含まれる', user: () => userDeletedByAdmin },
|
||||||
] as const)('がよくリプライをするユーザーのリストを取得でき、結果に$label', async ({ user, excluded }) => {
|
] as const)('がよくリプライをするユーザーのリストを取得でき、結果に$label', async ({ user, excluded }) => {
|
||||||
|
|
|
@ -95,6 +95,7 @@ describe('NoteCreateService', () => {
|
||||||
folderId: null,
|
folderId: null,
|
||||||
folder: null,
|
folder: null,
|
||||||
isSensitive: false,
|
isSensitive: false,
|
||||||
|
isSensitiveByModerator: false,
|
||||||
maybeSensitive: false,
|
maybeSensitive: false,
|
||||||
maybePorn: false,
|
maybePorn: false,
|
||||||
isLink: false,
|
isLink: false,
|
||||||
|
|
|
@ -141,7 +141,10 @@ export const post = async (user: UserToken, params: misskey.Endpoints['notes/cre
|
||||||
|
|
||||||
const res = await api('notes/create', q, user);
|
const res = await api('notes/create', q, user);
|
||||||
|
|
||||||
return res.body ? res.body.createdNote : null;
|
assert.strictEqual(res.status, 200);
|
||||||
|
assert.notEqual(res.body, null);
|
||||||
|
|
||||||
|
return res.body.createdNote;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createAppToken = async (user: UserToken, permissions: (typeof misskey.permissions)[number][]) => {
|
export const createAppToken = async (user: UserToken, permissions: (typeof misskey.permissions)[number][]) => {
|
||||||
|
|
14
packages/frontend/@types/vue-gtag.d.ts
vendored
14
packages/frontend/@types/vue-gtag.d.ts
vendored
|
@ -1,14 +0,0 @@
|
||||||
declare module 'vue-gtag' {
|
|
||||||
export type GtagConsent = (command: 'consent', arg: 'default' | 'update', params: GtagConsentParams) => void;
|
|
||||||
|
|
||||||
export interface GtagConsentParams {
|
|
||||||
ad_storage?: 'granted' | 'denied',
|
|
||||||
ad_user_data?: 'granted' | 'denied',
|
|
||||||
ad_personalization?: 'granted' | 'denied',
|
|
||||||
analytics_storage?: 'granted' | 'denied',
|
|
||||||
functionality_storage?: 'granted' | 'denied',
|
|
||||||
personalization_storage?: 'granted' | 'denied',
|
|
||||||
security_storage?: 'granted' | 'denied',
|
|
||||||
wait_for_update?: number
|
|
||||||
}
|
|
||||||
}
|
|
BIN
packages/frontend/assets/sounds/flush.mp3
Normal file
BIN
packages/frontend/assets/sounds/flush.mp3
Normal file
Binary file not shown.
|
@ -26,23 +26,23 @@
|
||||||
"@rollup/plugin-typescript": "12.1.2",
|
"@rollup/plugin-typescript": "12.1.2",
|
||||||
"@rollup/pluginutils": "5.1.4",
|
"@rollup/pluginutils": "5.1.4",
|
||||||
"@syuilo/aiscript": "0.19.0",
|
"@syuilo/aiscript": "0.19.0",
|
||||||
"@tabler/icons-webfont": "3.29.0",
|
"@tabler/icons-webfont": "3.31.0",
|
||||||
"@twemoji/parser": "15.1.1",
|
"@twemoji/parser": "15.1.1",
|
||||||
"@vitejs/plugin-vue": "5.2.1",
|
"@vitejs/plugin-vue": "5.2.3",
|
||||||
"@vue/compiler-sfc": "3.5.13",
|
"@vue/compiler-sfc": "3.5.13",
|
||||||
"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.15",
|
"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.15",
|
||||||
"astring": "1.9.0",
|
"astring": "1.9.0",
|
||||||
"broadcast-channel": "7.0.0",
|
"broadcast-channel": "7.1.0",
|
||||||
"buraha": "0.0.1",
|
"buraha": "0.0.1",
|
||||||
"canvas-confetti": "1.9.3",
|
"canvas-confetti": "1.9.3",
|
||||||
"chart.js": "4.4.7",
|
"chart.js": "4.4.8",
|
||||||
"chartjs-adapter-date-fns": "3.0.0",
|
"chartjs-adapter-date-fns": "3.0.0",
|
||||||
"chartjs-chart-matrix": "2.0.1",
|
"chartjs-chart-matrix": "3.0.0",
|
||||||
"chartjs-plugin-gradient": "0.6.1",
|
"chartjs-plugin-gradient": "0.6.1",
|
||||||
"chartjs-plugin-zoom": "2.2.0",
|
"chartjs-plugin-zoom": "2.2.0",
|
||||||
"chromatic": "11.25.2",
|
"chromatic": "11.27.0",
|
||||||
"compare-versions": "6.1.1",
|
"compare-versions": "6.1.1",
|
||||||
"cropperjs": "2.0.0-rc.0",
|
"cropperjs": "2.0.0",
|
||||||
"date-fns": "4.1.0",
|
"date-fns": "4.1.0",
|
||||||
"escape-regexp": "0.0.1",
|
"escape-regexp": "0.0.1",
|
||||||
"estree-walker": "3.0.3",
|
"estree-walker": "3.0.3",
|
||||||
|
@ -58,88 +58,88 @@
|
||||||
"misskey-reversi": "workspace:*",
|
"misskey-reversi": "workspace:*",
|
||||||
"photoswipe": "5.4.4",
|
"photoswipe": "5.4.4",
|
||||||
"punycode.js": "2.3.1",
|
"punycode.js": "2.3.1",
|
||||||
"rollup": "4.34.0",
|
"rollup": "4.38.0",
|
||||||
"sanitize-html": "2.14.0",
|
"sanitize-html": "2.15.0",
|
||||||
"sass": "1.83.4",
|
"sass": "1.86.0",
|
||||||
"shiki": "2.2.0",
|
"shiki": "3.2.1",
|
||||||
"strict-event-emitter-types": "2.0.0",
|
"strict-event-emitter-types": "2.0.0",
|
||||||
"textarea-caret": "3.1.0",
|
"textarea-caret": "3.1.0",
|
||||||
"three": "0.173.0",
|
"three": "0.175.0",
|
||||||
"throttle-debounce": "5.0.2",
|
"throttle-debounce": "5.0.2",
|
||||||
"tinycolor2": "1.6.0",
|
"tinycolor2": "1.6.0",
|
||||||
"tsc-alias": "1.8.10",
|
"tsc-alias": "1.8.13",
|
||||||
"tsconfig-paths": "4.2.0",
|
"tsconfig-paths": "4.2.0",
|
||||||
"typescript": "5.7.3",
|
"typescript": "5.8.2",
|
||||||
"uuid": "11.0.5",
|
"uuid": "11.1.0",
|
||||||
"v-code-diff": "1.13.1",
|
"v-code-diff": "1.13.1",
|
||||||
"vite": "6.0.11",
|
"vite": "6.2.3",
|
||||||
"vue": "3.5.13",
|
"vue": "3.5.13",
|
||||||
"vue-gtag": "2.0.1",
|
"vue-gtag": "3.1.1",
|
||||||
"vuedraggable": "next",
|
"vuedraggable": "next",
|
||||||
"webgl-audiovisualizer": "github:tar-bin/webgl-audiovisualizer"
|
"webgl-audiovisualizer": "github:tar-bin/webgl-audiovisualizer"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@misskey-dev/eslint-plugin": "1.0.0",
|
"@misskey-dev/eslint-plugin": "1.0.0",
|
||||||
"@misskey-dev/summaly": "github:MisskeyIO/summaly#5.1.3",
|
"@misskey-dev/summaly": "github:MisskeyIO/summaly#5.1.3",
|
||||||
"@storybook/addon-actions": "8.5.2",
|
"@storybook/addon-actions": "8.6.11",
|
||||||
"@storybook/addon-essentials": "8.5.2",
|
"@storybook/addon-essentials": "8.6.11",
|
||||||
"@storybook/addon-interactions": "8.5.2",
|
"@storybook/addon-interactions": "8.6.11",
|
||||||
"@storybook/addon-links": "8.5.2",
|
"@storybook/addon-links": "8.6.11",
|
||||||
"@storybook/addon-mdx-gfm": "8.5.2",
|
"@storybook/addon-mdx-gfm": "8.6.11",
|
||||||
"@storybook/addon-storysource": "8.5.2",
|
"@storybook/addon-storysource": "8.6.11",
|
||||||
"@storybook/blocks": "8.5.2",
|
"@storybook/blocks": "8.6.11",
|
||||||
"@storybook/components": "8.5.2",
|
"@storybook/components": "8.6.11",
|
||||||
"@storybook/core-events": "8.5.2",
|
"@storybook/core-events": "8.6.11",
|
||||||
"@storybook/manager-api": "8.5.2",
|
"@storybook/manager-api": "8.6.11",
|
||||||
"@storybook/preview-api": "8.5.2",
|
"@storybook/preview-api": "8.6.11",
|
||||||
"@storybook/react": "8.5.2",
|
"@storybook/react": "8.6.11",
|
||||||
"@storybook/react-vite": "8.5.2",
|
"@storybook/react-vite": "8.6.11",
|
||||||
"@storybook/test": "8.5.2",
|
"@storybook/test": "8.6.11",
|
||||||
"@storybook/theming": "8.5.2",
|
"@storybook/theming": "8.6.11",
|
||||||
"@storybook/types": "8.5.2",
|
"@storybook/types": "8.6.11",
|
||||||
"@storybook/vue3": "8.5.2",
|
"@storybook/vue3": "8.6.11",
|
||||||
"@storybook/vue3-vite": "8.5.2",
|
"@storybook/vue3-vite": "8.6.11",
|
||||||
"@testing-library/vue": "8.1.0",
|
"@testing-library/vue": "8.1.0",
|
||||||
"@types/canvas-confetti": "^1.6.4",
|
"@types/canvas-confetti": "^1.6.4",
|
||||||
"@types/escape-regexp": "0.0.3",
|
"@types/escape-regexp": "0.0.3",
|
||||||
"@types/estree": "1.0.6",
|
"@types/estree": "1.0.7",
|
||||||
"@types/matter-js": "0.19.8",
|
"@types/matter-js": "0.19.8",
|
||||||
"@types/micromatch": "4.0.9",
|
"@types/micromatch": "4.0.9",
|
||||||
"@types/node": "22.13.0",
|
"@types/node": "22.13.14",
|
||||||
"@types/punycode.js": "npm:@types/punycode@2.1.4",
|
"@types/punycode.js": "npm:@types/punycode@2.1.4",
|
||||||
"@types/sanitize-html": "2.13.0",
|
"@types/sanitize-html": "2.15.0",
|
||||||
"@types/three": "0.173.0",
|
"@types/three": "0.175.0",
|
||||||
"@types/throttle-debounce": "5.0.2",
|
"@types/throttle-debounce": "5.0.2",
|
||||||
"@types/tinycolor2": "1.4.6",
|
"@types/tinycolor2": "1.4.6",
|
||||||
"@types/ws": "8.5.14",
|
"@types/ws": "8.18.0",
|
||||||
"@typescript-eslint/eslint-plugin": "7.10.0",
|
"@typescript-eslint/eslint-plugin": "7.10.0",
|
||||||
"@typescript-eslint/parser": "7.10.0",
|
"@typescript-eslint/parser": "7.10.0",
|
||||||
"@vitest/coverage-v8": "3.0.4",
|
"@vitest/coverage-v8": "3.0.9",
|
||||||
"@vue/runtime-core": "3.5.13",
|
"@vue/runtime-core": "3.5.13",
|
||||||
"acorn": "8.14.0",
|
"acorn": "8.14.1",
|
||||||
"cross-env": "7.0.3",
|
"cross-env": "7.0.3",
|
||||||
"cypress": "14.0.1",
|
"cypress": "14.2.1",
|
||||||
"eslint": "8.57.1",
|
"eslint": "8.57.1",
|
||||||
"eslint-plugin-import": "2.31.0",
|
"eslint-plugin-import": "2.31.0",
|
||||||
"eslint-plugin-vue": "9.32.0",
|
"eslint-plugin-vue": "9.32.0",
|
||||||
"fast-glob": "3.3.3",
|
"fast-glob": "3.3.3",
|
||||||
"happy-dom": "16.8.1",
|
"happy-dom": "17.4.4",
|
||||||
"intersection-observer": "0.12.2",
|
"intersection-observer": "0.12.2",
|
||||||
"micromatch": "4.0.8",
|
"micromatch": "4.0.8",
|
||||||
"msw": "2.7.0",
|
"msw": "2.7.3",
|
||||||
"msw-storybook-addon": "2.0.4",
|
"msw-storybook-addon": "2.0.4",
|
||||||
"nodemon": "3.1.9",
|
"nodemon": "3.1.9",
|
||||||
"prettier": "3.4.2",
|
"prettier": "3.5.3",
|
||||||
"react": "19.0.0",
|
"react": "19.1.0",
|
||||||
"react-dom": "19.0.0",
|
"react-dom": "19.1.0",
|
||||||
"start-server-and-test": "2.0.10",
|
"start-server-and-test": "2.0.11",
|
||||||
"storybook": "8.5.2",
|
"storybook": "8.6.11",
|
||||||
"storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme",
|
"storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme",
|
||||||
"vite-plugin-turbosnap": "1.0.3",
|
"vite-plugin-turbosnap": "1.0.3",
|
||||||
"vitest": "3.0.4",
|
"vitest": "3.0.9",
|
||||||
"vitest-fetch-mock": "0.3.0",
|
"vitest-fetch-mock": "0.3.0",
|
||||||
"vue-component-type-helpers": "2.2.0",
|
"vue-component-type-helpers": "2.2.8",
|
||||||
"vue-eslint-parser": "9.4.3",
|
"vue-eslint-parser": "9.4.3",
|
||||||
"vue-tsc": "2.2.0"
|
"vue-tsc": "2.2.8"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,8 @@ import { miLocalStorage } from '@/local-storage.js';
|
||||||
import { fetchCustomEmojis } from '@/custom-emojis.js';
|
import { fetchCustomEmojis } from '@/custom-emojis.js';
|
||||||
import { setupRouter } from '@/router/definition.js';
|
import { setupRouter } from '@/router/definition.js';
|
||||||
import { mainRouter } from '@/router/main.js';
|
import { mainRouter } from '@/router/main.js';
|
||||||
import VueGtag, { bootstrap as gtagBootstrap, GtagConsent, GtagConsentParams } from 'vue-gtag';
|
import { createGtag, addGtag, consent as gtagConsent } from 'vue-gtag';
|
||||||
|
import type { GtagConsentParams } from '@/types/gtag.js';
|
||||||
|
|
||||||
export async function common(createVue: () => App<Element>) {
|
export async function common(createVue: () => App<Element>) {
|
||||||
console.info(`Misskey v${version}`);
|
console.info(`Misskey v${version}`);
|
||||||
|
@ -283,21 +284,21 @@ export async function common(createVue: () => App<Element>) {
|
||||||
components(app);
|
components(app);
|
||||||
|
|
||||||
if (instance.googleAnalyticsId) {
|
if (instance.googleAnalyticsId) {
|
||||||
app.use(VueGtag, {
|
app.use(createGtag( {
|
||||||
bootstrap: false,
|
tagId: instance.googleAnalyticsId,
|
||||||
appName: `Misskey v${version}`,
|
|
||||||
pageTrackerEnabled: true,
|
|
||||||
pageTrackerScreenviewEnabled: true,
|
|
||||||
config: {
|
config: {
|
||||||
id: instance.googleAnalyticsId,
|
|
||||||
params: {
|
|
||||||
anonymize_ip: false,
|
anonymize_ip: false,
|
||||||
send_page_view: true,
|
send_page_view: true,
|
||||||
},
|
},
|
||||||
|
pageTracker: {
|
||||||
|
router: mainRouter,
|
||||||
|
useScreenview: true,
|
||||||
},
|
},
|
||||||
}, mainRouter);
|
initMode: 'manual',
|
||||||
|
appName: `Misskey v${version}`,
|
||||||
|
}));
|
||||||
|
|
||||||
const gtagConsent = miLocalStorage.getItemAsJson('gtagConsent') as GtagConsentParams ?? {
|
const gtagConsentParams = miLocalStorage.getItemAsJson('gtagConsent') as GtagConsentParams ?? {
|
||||||
ad_storage: 'denied',
|
ad_storage: 'denied',
|
||||||
ad_user_data: 'denied',
|
ad_user_data: 'denied',
|
||||||
ad_personalization: 'denied',
|
ad_personalization: 'denied',
|
||||||
|
@ -306,13 +307,12 @@ export async function common(createVue: () => App<Element>) {
|
||||||
personalization_storage: 'denied',
|
personalization_storage: 'denied',
|
||||||
security_storage: 'granted',
|
security_storage: 'granted',
|
||||||
};
|
};
|
||||||
miLocalStorage.setItemAsJson('gtagConsent', gtagConsent);
|
miLocalStorage.setItemAsJson('gtagConsent', gtagConsentParams);
|
||||||
|
gtagConsent('default', gtagConsentParams);
|
||||||
if (typeof window['gtag'] === 'function') (window['gtag'] as GtagConsent)('consent', 'default', gtagConsent);
|
|
||||||
|
|
||||||
if (miLocalStorage.getItem('gaConsent') === 'true') {
|
if (miLocalStorage.getItem('gaConsent') === 'true') {
|
||||||
// noinspection ES6MissingAwait
|
// noinspection ES6MissingAwait
|
||||||
gtagBootstrap();
|
addGtag();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ import { i18n } from '@/i18n.js';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { defaultStore } from '@/store.js';
|
import { defaultStore } from '@/store.js';
|
||||||
import { MisskeyEntity } from '@/types/date-separated-list.js';
|
import { MisskeyEntity } from '@/types/date-separated-list.js';
|
||||||
|
import { isAprilFoolsDay } from '@/scripts/seasonal-events';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
|
@ -136,14 +137,15 @@ export default defineComponent({
|
||||||
[$style['date-separated-list-nogap']]: props.noGap,
|
[$style['date-separated-list-nogap']]: props.noGap,
|
||||||
[$style['direction-down']]: props.direction === 'down',
|
[$style['direction-down']]: props.direction === 'down',
|
||||||
[$style['direction-up']]: props.direction === 'up',
|
[$style['direction-up']]: props.direction === 'up',
|
||||||
|
[$style['april-fool']]: defaultStore.state.animation ? isAprilFoolsDay() : false,
|
||||||
};
|
};
|
||||||
|
|
||||||
return () => defaultStore.state.animation ? h(TransitionGroup, {
|
return () => defaultStore.state.animation ? h(TransitionGroup, {
|
||||||
class: classes,
|
class: classes,
|
||||||
name: 'list',
|
name: 'list',
|
||||||
tag: 'div',
|
tag: 'div',
|
||||||
onBeforeLeave,
|
onBeforeLeave: !isAprilFoolsDay() ? onBeforeLeave : undefined,
|
||||||
onLeaveCancelled,
|
onLeaveCancelled: !isAprilFoolsDay() ? onLeaveCancelled : undefined,
|
||||||
}, { default: renderChildren }) : h('div', {
|
}, { default: renderChildren }) : h('div', {
|
||||||
class: classes,
|
class: classes,
|
||||||
}, { default: renderChildren });
|
}, { default: renderChildren });
|
||||||
|
@ -210,6 +212,13 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.april-fool {
|
||||||
|
&:global > .list-enter-from,
|
||||||
|
&:global > .list-leave-to {
|
||||||
|
animation: global-spin-shrink 3s ease-in forwards;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.separator {
|
.separator {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,14 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<div :class="$style.head">
|
<div :class="$style.head">
|
||||||
<MkAvatar v-if="['pollEnded', 'note'].includes(notification.type) && notification.note" :class="$style.icon" :user="notification.note.user" link preview/>
|
<MkAvatar v-if="['pollEnded', 'note'].includes(notification.type) && notification.note" :class="$style.icon" :user="notification.note.user" link preview/>
|
||||||
<MkAvatar v-else-if="['roleAssigned', 'achievementEarned', 'noteScheduled', 'scheduledNotePosted', 'scheduledNoteError'].includes(notification.type)" :class="$style.icon" :user="$i" link preview/>
|
<MkAvatar v-else-if="['roleAssigned', 'achievementEarned', 'noteScheduled', 'scheduledNotePosted', 'scheduledNoteError'].includes(notification.type)" :class="$style.icon" :user="$i" link preview/>
|
||||||
|
<div
|
||||||
|
v-else-if="notification.type === 'sensitiveFlagAssigned'"
|
||||||
|
:class="$style.iconFrame"
|
||||||
|
>
|
||||||
|
<div :class="[$style.iconInner]">
|
||||||
|
<img :class="$style.iconImg" src="/fluent-emoji/1f6a9.png">
|
||||||
|
</div>
|
||||||
|
</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' && 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>
|
||||||
|
@ -71,6 +79,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<span v-else-if="notification.type === 'reaction:grouped'" :class="$style.headerName">{{ i18n.tsx._notification.reactedBySomeUsers({ n: getActualReactedUsersCount(notification) }) }}</span>
|
<span v-else-if="notification.type === 'reaction:grouped'" :class="$style.headerName">{{ i18n.tsx._notification.reactedBySomeUsers({ n: getActualReactedUsersCount(notification) }) }}</span>
|
||||||
<span v-else-if="notification.type === 'renote:grouped'" :class="$style.headerName">{{ i18n.tsx._notification.renotedBySomeUsers({ n: notification.users.length }) }}</span>
|
<span v-else-if="notification.type === 'renote:grouped'" :class="$style.headerName">{{ i18n.tsx._notification.renotedBySomeUsers({ n: notification.users.length }) }}</span>
|
||||||
<span v-else-if="notification.type === 'app'" :class="$style.headerName">{{ notification.header }}</span>
|
<span v-else-if="notification.type === 'app'" :class="$style.headerName">{{ notification.header }}</span>
|
||||||
|
<MkA v-else-if="notification.type === 'sensitiveFlagAssigned'" :to="'/my/drive/file/'+notification.fileId" :class="$style.headerName">{{ i18n.ts._notification.sensitiveFlagAssigned }}</MkA>
|
||||||
<MkTime v-if="withTime" :time="notification.createdAt" :class="$style.headerTime"/>
|
<MkTime v-if="withTime" :time="notification.createdAt" :class="$style.headerTime"/>
|
||||||
</header>
|
</header>
|
||||||
<div>
|
<div>
|
||||||
|
@ -159,6 +168,10 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<MkAvatar :class="$style.reactionsItemAvatar" :user="user" link preview/>
|
<MkAvatar :class="$style.reactionsItemAvatar" :user="user" link preview/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<Mfm v-else-if="notification.type === 'sensitiveFlagAssigned'" :text="i18n.ts.sensitiveByModerator"/>
|
||||||
|
<span v-if="['sensitiveFlagAssigned'].includes(notification.type)" :class="$style.text" style="opacity: 0.6;">
|
||||||
|
{{ i18n.ts.thisInfoIsNotVisibleOtherUser }}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -341,6 +354,12 @@ function getActualReactedUsersCount(notification: Misskey.entities.Notification)
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.t_sensitiveFlagAssigned {
|
||||||
|
padding: 3px;
|
||||||
|
background: var(--eventOther);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
.tail {
|
.tail {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
@ -430,6 +449,42 @@ function getActualReactedUsersCount(notification: Misskey.entities.Notification)
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.iconFrame {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding: 4px;
|
||||||
|
border-radius: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
pointer-events: none;
|
||||||
|
user-select: none;
|
||||||
|
filter: drop-shadow(0px 2px 2px #00000044);
|
||||||
|
box-shadow: 0 1px 0px #ffffff88 inset;
|
||||||
|
overflow: clip;
|
||||||
|
background: linear-gradient(0deg, #703827, #d37566);
|
||||||
|
}
|
||||||
|
|
||||||
|
.iconImg {
|
||||||
|
width: calc(100% - 12px);
|
||||||
|
height: calc(100% - 12px);
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
margin: auto;
|
||||||
|
filter: drop-shadow(0px 1px 2px #000000aa);
|
||||||
|
}
|
||||||
|
|
||||||
|
.iconInner {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 100%;
|
||||||
|
box-shadow: 0 1px 0px #ffffff88 inset;
|
||||||
|
background: linear-gradient(0deg, #d37566, #703827);
|
||||||
|
}
|
||||||
|
|
||||||
@container (max-width: 600px) {
|
@container (max-width: 600px) {
|
||||||
.root {
|
.root {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
|
|
|
@ -24,7 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onUnmounted, onMounted, computed, shallowRef, onActivated } from 'vue';
|
import { onUnmounted, onMounted, computed, ref, shallowRef, onActivated } from 'vue';
|
||||||
import MkPagination from '@/components/MkPagination.vue';
|
import MkPagination from '@/components/MkPagination.vue';
|
||||||
import XNotification from '@/components/MkNotification.vue';
|
import XNotification from '@/components/MkNotification.vue';
|
||||||
import MkDateSeparatedList from '@/components/MkDateSeparatedList.vue';
|
import MkDateSeparatedList from '@/components/MkDateSeparatedList.vue';
|
||||||
|
@ -43,6 +43,7 @@ const props = defineProps<{
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const pagingComponent = shallowRef<InstanceType<typeof MkPagination>>();
|
const pagingComponent = shallowRef<InstanceType<typeof MkPagination>>();
|
||||||
|
const hasNewNotificationWhileTabHidden = ref(false);
|
||||||
|
|
||||||
const pagination = computed(() => defaultStore.reactiveState.useGroupedNotifications.value ? {
|
const pagination = computed(() => defaultStore.reactiveState.useGroupedNotifications.value ? {
|
||||||
endpoint: 'i/notifications-grouped' as const,
|
endpoint: 'i/notifications-grouped' as const,
|
||||||
|
@ -64,9 +65,13 @@ function onNotification(notification) {
|
||||||
useStream().send('readNotification');
|
useStream().send('readNotification');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isMuted && filterMutedNotification(notification)) {
|
if (!document.hidden && !isMuted && filterMutedNotification(notification)) {
|
||||||
pagingComponent.value?.prepend(notification);
|
pagingComponent.value?.prepend(notification);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (document.hidden && !hasNewNotificationWhileTabHidden.value) {
|
||||||
|
hasNewNotificationWhileTabHidden.value = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function reload() {
|
function reload() {
|
||||||
|
@ -77,12 +82,22 @@ function reload() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onVisibilityChange() {
|
||||||
|
if (document.visibilityState === 'visible') {
|
||||||
|
if (hasNewNotificationWhileTabHidden.value) {
|
||||||
|
hasNewNotificationWhileTabHidden.value = false;
|
||||||
|
reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let connection: Misskey.ChannelConnection<Misskey.Channels['main']>;
|
let connection: Misskey.ChannelConnection<Misskey.Channels['main']>;
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
connection = useStream().useChannel('main');
|
connection = useStream().useChannel('main');
|
||||||
connection.on('notification', onNotification);
|
connection.on('notification', onNotification);
|
||||||
connection.on('notificationFlushed', reload);
|
connection.on('notificationFlushed', reload);
|
||||||
|
document.addEventListener('visibilitychange', onVisibilityChange);
|
||||||
});
|
});
|
||||||
|
|
||||||
onActivated(() => {
|
onActivated(() => {
|
||||||
|
@ -91,6 +106,7 @@ onActivated(() => {
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
if (connection) connection.dispose();
|
if (connection) connection.dispose();
|
||||||
|
document.removeEventListener('visibilitychange', onVisibilityChange);
|
||||||
});
|
});
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
|
|
|
@ -209,6 +209,10 @@ const reload = (): Promise<void> => {
|
||||||
return init();
|
return init();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const deleteItem = () => {
|
||||||
|
items.value = [];
|
||||||
|
};
|
||||||
|
|
||||||
const fetchMore = async (): Promise<void> => {
|
const fetchMore = async (): Promise<void> => {
|
||||||
if (!more.value || fetching.value || moreFetching.value || items.value.length === 0) return;
|
if (!more.value || fetching.value || moreFetching.value || items.value.length === 0) return;
|
||||||
moreFetching.value = true;
|
moreFetching.value = true;
|
||||||
|
@ -414,9 +418,16 @@ defineExpose({
|
||||||
more,
|
more,
|
||||||
reload,
|
reload,
|
||||||
prepend,
|
prepend,
|
||||||
|
deleteItem,
|
||||||
append: appendItem,
|
append: appendItem,
|
||||||
removeItem,
|
removeItem,
|
||||||
updateItem,
|
updateItem,
|
||||||
|
stopFetch: () => {
|
||||||
|
more.value = false;
|
||||||
|
},
|
||||||
|
startFetch: () => {
|
||||||
|
more.value = true;
|
||||||
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, watch, onUnmounted, provide, shallowRef } from 'vue';
|
import { computed, watch, onUnmounted, provide, shallowRef, onMounted } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import MkNotes from '@/components/MkNotes.vue';
|
import MkNotes from '@/components/MkNotes.vue';
|
||||||
import MkPullToRefresh from '@/components/MkPullToRefresh.vue';
|
import MkPullToRefresh from '@/components/MkPullToRefresh.vue';
|
||||||
|
@ -69,12 +69,14 @@ const prComponent = shallowRef<InstanceType<typeof MkPullToRefresh>>();
|
||||||
const tlComponent = shallowRef<InstanceType<typeof MkNotes>>();
|
const tlComponent = shallowRef<InstanceType<typeof MkNotes>>();
|
||||||
|
|
||||||
let tlNotesCount = 0;
|
let tlNotesCount = 0;
|
||||||
|
const notVisibleNoteData = new Array<object>();
|
||||||
|
|
||||||
async function prepend(data) {
|
async function prepend(data) {
|
||||||
if (tlComponent.value == null) return;
|
if (tlComponent.value == null) return;
|
||||||
|
|
||||||
let note = data;
|
let note = data;
|
||||||
|
|
||||||
|
if (!document.hidden) {
|
||||||
// チェックするプロパティはなんでも良い
|
// チェックするプロパティはなんでも良い
|
||||||
// minimizeが有効でid以外が存在しない場合は取得する
|
// minimizeが有効でid以外が存在しない場合は取得する
|
||||||
if (!data.visibility) {
|
if (!data.visibility) {
|
||||||
|
@ -97,6 +99,13 @@ async function prepend(data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
tlComponent.value.pagingComponent?.prepend(note);
|
tlComponent.value.pagingComponent?.prepend(note);
|
||||||
|
} else {
|
||||||
|
notVisibleNoteData.push(data);
|
||||||
|
|
||||||
|
if (notVisibleNoteData.length > 10) {
|
||||||
|
notVisibleNoteData.shift();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
emit('note');
|
emit('note');
|
||||||
|
|
||||||
|
@ -105,6 +114,23 @@ async function prepend(data) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function loadUnloadedNotes() {
|
||||||
|
if (document.hidden) return;
|
||||||
|
if (tlComponent.value == null) return;
|
||||||
|
if (notVisibleNoteData.length === 0) return;
|
||||||
|
|
||||||
|
if (notVisibleNoteData.length >= 10) {
|
||||||
|
tlComponent.value.pagingComponent?.deleteItem();
|
||||||
|
tlComponent.value.pagingComponent?.stopFetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
while (notVisibleNoteData.length > 0) {
|
||||||
|
await prepend(notVisibleNoteData.shift());
|
||||||
|
}
|
||||||
|
|
||||||
|
tlComponent.value.pagingComponent?.startFetch();
|
||||||
|
}
|
||||||
|
|
||||||
let connection: Misskey.ChannelConnection | null = null;
|
let connection: Misskey.ChannelConnection | null = null;
|
||||||
let connection2: Misskey.ChannelConnection | null = null;
|
let connection2: Misskey.ChannelConnection | null = null;
|
||||||
let paginationQuery: Paging | null = null;
|
let paginationQuery: Paging | null = null;
|
||||||
|
@ -291,8 +317,13 @@ watch(() => [props.list, props.antenna, props.channel, props.role, props.withRen
|
||||||
// 初回表示用
|
// 初回表示用
|
||||||
refreshEndpointAndChannel();
|
refreshEndpointAndChannel();
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
document.addEventListener('visibilitychange', loadUnloadedNotes);
|
||||||
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
disconnectChannel();
|
disconnectChannel();
|
||||||
|
document.removeEventListener('visibilitychange', loadUnloadedNotes);
|
||||||
});
|
});
|
||||||
|
|
||||||
function reloadTimeline() {
|
function reloadTimeline() {
|
||||||
|
|
|
@ -59,12 +59,8 @@ import { host } from '@/config.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { $i } from '@/account.js';
|
import { $i } from '@/account.js';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import {
|
import { addGtag, consent as gtagConsent, set as gtagSet } from 'vue-gtag';
|
||||||
bootstrap as gtagBootstrap,
|
import type { GtagConsentParams } from '@/types/gtag.js';
|
||||||
GtagConsent,
|
|
||||||
GtagConsentParams,
|
|
||||||
set as gtagSet
|
|
||||||
} from 'vue-gtag';
|
|
||||||
|
|
||||||
const emit = defineEmits<(ev: 'closed') => void>();
|
const emit = defineEmits<(ev: 'closed') => void>();
|
||||||
|
|
||||||
|
@ -76,7 +72,7 @@ const gtagConsentPersonalization = ref(false);
|
||||||
|
|
||||||
function consentAll() {
|
function consentAll() {
|
||||||
miLocalStorage.setItem('gaConsent', 'true');
|
miLocalStorage.setItem('gaConsent', 'true');
|
||||||
const gtagConsent = <GtagConsentParams>{
|
const params = <GtagConsentParams>{
|
||||||
ad_storage: 'granted',
|
ad_storage: 'granted',
|
||||||
ad_user_data: 'granted',
|
ad_user_data: 'granted',
|
||||||
ad_personalization: 'granted',
|
ad_personalization: 'granted',
|
||||||
|
@ -85,9 +81,8 @@ function consentAll() {
|
||||||
personalization_storage: 'granted',
|
personalization_storage: 'granted',
|
||||||
security_storage: 'granted',
|
security_storage: 'granted',
|
||||||
};
|
};
|
||||||
miLocalStorage.setItemAsJson('gtagConsent', gtagConsent);
|
miLocalStorage.setItemAsJson('gtagConsent', params);
|
||||||
|
gtagConsent('update', params);
|
||||||
if (typeof window['gtag'] === 'function') (window['gtag'] as GtagConsent)('consent', 'update', gtagConsent);
|
|
||||||
bootstrap();
|
bootstrap();
|
||||||
|
|
||||||
emit('closed');
|
emit('closed');
|
||||||
|
@ -95,7 +90,7 @@ function consentAll() {
|
||||||
|
|
||||||
function consentEssential() {
|
function consentEssential() {
|
||||||
miLocalStorage.setItem('gaConsent', 'true');
|
miLocalStorage.setItem('gaConsent', 'true');
|
||||||
const gtagConsent = <GtagConsentParams>{
|
const params = <GtagConsentParams>{
|
||||||
ad_storage: 'denied',
|
ad_storage: 'denied',
|
||||||
ad_user_data: 'denied',
|
ad_user_data: 'denied',
|
||||||
ad_personalization: 'denied',
|
ad_personalization: 'denied',
|
||||||
|
@ -104,9 +99,8 @@ function consentEssential() {
|
||||||
personalization_storage: 'denied',
|
personalization_storage: 'denied',
|
||||||
security_storage: 'granted',
|
security_storage: 'granted',
|
||||||
};
|
};
|
||||||
miLocalStorage.setItemAsJson('gtagConsent', gtagConsent);
|
miLocalStorage.setItemAsJson('gtagConsent', params);
|
||||||
|
gtagConsent('update', params);
|
||||||
if (typeof window['gtag'] === 'function') (window['gtag'] as GtagConsent)('consent', 'update', gtagConsent);
|
|
||||||
bootstrap();
|
bootstrap();
|
||||||
|
|
||||||
emit('closed');
|
emit('closed');
|
||||||
|
@ -114,7 +108,7 @@ function consentEssential() {
|
||||||
|
|
||||||
function consentSelected() {
|
function consentSelected() {
|
||||||
miLocalStorage.setItem('gaConsent', 'true');
|
miLocalStorage.setItem('gaConsent', 'true');
|
||||||
const gtagConsent = <GtagConsentParams>{
|
const params = <GtagConsentParams>{
|
||||||
ad_storage: gtagConsentAnalytics.value ? 'granted' : 'denied',
|
ad_storage: gtagConsentAnalytics.value ? 'granted' : 'denied',
|
||||||
ad_user_data: gtagConsentFunctionality.value ? 'granted' : 'denied',
|
ad_user_data: gtagConsentFunctionality.value ? 'granted' : 'denied',
|
||||||
ad_personalization: gtagConsentPersonalization.value ? 'granted' : 'denied',
|
ad_personalization: gtagConsentPersonalization.value ? 'granted' : 'denied',
|
||||||
|
@ -123,16 +117,15 @@ function consentSelected() {
|
||||||
personalization_storage: gtagConsentPersonalization.value ? 'granted' : 'denied',
|
personalization_storage: gtagConsentPersonalization.value ? 'granted' : 'denied',
|
||||||
security_storage: 'granted',
|
security_storage: 'granted',
|
||||||
};
|
};
|
||||||
miLocalStorage.setItemAsJson('gtagConsent', gtagConsent);
|
miLocalStorage.setItemAsJson('gtagConsent', params);
|
||||||
|
gtagConsent('update', params);
|
||||||
if (typeof window['gtag'] === 'function') (window['gtag'] as GtagConsent)('consent', 'update', gtagConsent);
|
|
||||||
bootstrap();
|
bootstrap();
|
||||||
|
|
||||||
emit('closed');
|
emit('closed');
|
||||||
}
|
}
|
||||||
|
|
||||||
function bootstrap() {
|
function bootstrap() {
|
||||||
gtagBootstrap();
|
addGtag();
|
||||||
gtagSet({
|
gtagSet({
|
||||||
'client_id': miLocalStorage.getItem('id'),
|
'client_id': miLocalStorage.getItem('id'),
|
||||||
'user_id': $i?.id,
|
'user_id': $i?.id,
|
||||||
|
|
|
@ -70,6 +70,7 @@ export const notificationTypes = [
|
||||||
'noteScheduled',
|
'noteScheduled',
|
||||||
'scheduledNotePosted',
|
'scheduledNotePosted',
|
||||||
'scheduledNoteError',
|
'scheduledNoteError',
|
||||||
|
'sensitiveFlagAssigned',
|
||||||
'app',
|
'app',
|
||||||
] as const;
|
] as const;
|
||||||
export const obsoleteNotificationTypes = ['pollVote', 'groupInvited'] as const;
|
export const obsoleteNotificationTypes = ['pollVote', 'groupInvited'] as const;
|
||||||
|
|
|
@ -6,6 +6,9 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<template>
|
<template>
|
||||||
<div class="_gaps">
|
<div class="_gaps">
|
||||||
<MkInfo>{{ i18n.ts._fileViewer.thisPageCanBeSeenFromTheAuthor }}</MkInfo>
|
<MkInfo>{{ i18n.ts._fileViewer.thisPageCanBeSeenFromTheAuthor }}</MkInfo>
|
||||||
|
<MkInfo v-if="file && file.isSensitiveByModerator" :warn="true">
|
||||||
|
<Mfm :text="i18n.ts.sensitiveByModerator"/>
|
||||||
|
</MkInfo>
|
||||||
<MkLoading v-if="fetching"/>
|
<MkLoading v-if="fetching"/>
|
||||||
<div v-else-if="file" class="_gaps">
|
<div v-else-if="file" class="_gaps">
|
||||||
<div :class="$style.filePreviewRoot">
|
<div :class="$style.filePreviewRoot">
|
||||||
|
@ -23,12 +26,14 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<button v-if="isImage" v-tooltip="i18n.ts.cropImage" class="_button" :class="$style.fileQuickActionsOthersButton" @click="crop()">
|
<button v-if="isImage" v-tooltip="i18n.ts.cropImage" class="_button" :class="$style.fileQuickActionsOthersButton" @click="crop()">
|
||||||
<i class="ti ti-crop"></i>
|
<i class="ti ti-crop"></i>
|
||||||
</button>
|
</button>
|
||||||
|
<span v-if="!file.isSensitiveByModerator">
|
||||||
<button v-if="file.isSensitive" v-tooltip="i18n.ts.unmarkAsSensitive" class="_button" :class="$style.fileQuickActionsOthersButton" @click="toggleSensitive()">
|
<button v-if="file.isSensitive" v-tooltip="i18n.ts.unmarkAsSensitive" class="_button" :class="$style.fileQuickActionsOthersButton" @click="toggleSensitive()">
|
||||||
<i class="ti ti-eye"></i>
|
<i class="ti ti-eye"></i>
|
||||||
</button>
|
</button>
|
||||||
<button v-else v-tooltip="i18n.ts.markAsSensitive" class="_button" :class="$style.fileQuickActionsOthersButton" @click="toggleSensitive()">
|
<button v-else v-tooltip="i18n.ts.markAsSensitive" class="_button" :class="$style.fileQuickActionsOthersButton" @click="toggleSensitive()">
|
||||||
<i class="ti ti-eye-exclamation"></i>
|
<i class="ti ti-eye-exclamation"></i>
|
||||||
</button>
|
</button>
|
||||||
|
</span>
|
||||||
<a v-tooltip="i18n.ts.download" :href="file.url" :download="file.name" class="_button" :class="$style.fileQuickActionsOthersButton">
|
<a v-tooltip="i18n.ts.download" :href="file.url" :download="file.name" class="_button" :class="$style.fileQuickActionsOthersButton">
|
||||||
<i class="ti ti-download"></i>
|
<i class="ti ti-download"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -21,6 +21,9 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</div>
|
</div>
|
||||||
<div class="_margin _gaps_s">
|
<div class="_margin _gaps_s">
|
||||||
<MkRemoteCaution v-if="note.user.host != null" :href="note.url ?? note.uri"/>
|
<MkRemoteCaution v-if="note.user.host != null" :href="note.url ?? note.uri"/>
|
||||||
|
<MkInfo v-if="hasSensitiveFiles" :warn="true">
|
||||||
|
<Mfm :text="i18n.ts.sensitiveByModerator"/>
|
||||||
|
</MkInfo>
|
||||||
<MkNoteDetailed :key="note.id" v-model:note="note" :initialTab="initialTab" :class="$style.note"/>
|
<MkNoteDetailed :key="note.id" v-model:note="note" :initialTab="initialTab" :class="$style.note"/>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="clips && clips.length > 0" class="_margin">
|
<div v-if="clips && clips.length > 0" class="_margin">
|
||||||
|
@ -63,7 +66,8 @@ import { dateString } from '@/filters/date.js';
|
||||||
import MkClipPreview from '@/components/MkClipPreview.vue';
|
import MkClipPreview from '@/components/MkClipPreview.vue';
|
||||||
import { defaultStore } from '@/store.js';
|
import { defaultStore } from '@/store.js';
|
||||||
import MkNoteNotFound from '@/components/MkNoteNotFound.vue';
|
import MkNoteNotFound from '@/components/MkNoteNotFound.vue';
|
||||||
import { iAmModerator } from '@/account.js';
|
import { $i, iAmModerator } from '@/account.js';
|
||||||
|
import MkInfo from '@/components/MkInfo.vue';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
noteId: string;
|
noteId: string;
|
||||||
|
@ -72,6 +76,7 @@ const props = defineProps<{
|
||||||
|
|
||||||
const note = ref<null | Misskey.entities.Note>();
|
const note = ref<null | Misskey.entities.Note>();
|
||||||
const clips = ref<Misskey.entities.Clip[]>();
|
const clips = ref<Misskey.entities.Clip[]>();
|
||||||
|
const hasSensitiveFiles = ref(false);
|
||||||
const showPrev = ref<'user' | 'channel' | false>(false);
|
const showPrev = ref<'user' | 'channel' | false>(false);
|
||||||
const showNext = ref<'user' | 'channel' | false>(false);
|
const showNext = ref<'user' | 'channel' | false>(false);
|
||||||
const error = ref();
|
const error = ref();
|
||||||
|
@ -125,6 +130,15 @@ function fetchNote() {
|
||||||
noteId: props.noteId,
|
noteId: props.noteId,
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
note.value = res;
|
note.value = res;
|
||||||
|
|
||||||
|
if (note.value.userId === $i?.id && note.value.fileIds) {
|
||||||
|
Promise.all(note.value.fileIds.map(fileId =>
|
||||||
|
misskeyApi('drive/files/show', { fileId: fileId }),
|
||||||
|
)).then(files => {
|
||||||
|
hasSensitiveFiles.value = files.some(file => file.isSensitiveByModerator);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// 古いノートは被クリップ数をカウントしていないので、2023-10-01以前のものは強制的にnotes/clipsを叩く
|
// 古いノートは被クリップ数をカウントしていないので、2023-10-01以前のものは強制的にnotes/clipsを叩く
|
||||||
if (note.value.clippedCount > 0 || new Date(note.value.createdAt).getTime() < new Date('2023-10-01').getTime()) {
|
if (note.value.clippedCount > 0 || new Date(note.value.createdAt).getTime() < new Date('2023-10-01').getTime()) {
|
||||||
misskeyApi('notes/clips', {
|
misskeyApi('notes/clips', {
|
||||||
|
|
|
@ -109,7 +109,8 @@ import { unisonReload } from '@/scripts/unison-reload.js';
|
||||||
import { miLocalStorage } from '@/local-storage.js';
|
import { miLocalStorage } from '@/local-storage.js';
|
||||||
import { instance } from '@/instance.js';
|
import { instance } from '@/instance.js';
|
||||||
import { host } from '@/config.js';
|
import { host } from '@/config.js';
|
||||||
import { GtagConsent, GtagConsentParams } from 'vue-gtag';
|
import { consent as gtagConsent } from 'vue-gtag';
|
||||||
|
import type { GtagConsentParams } from '@/types/gtag.js';
|
||||||
|
|
||||||
const $i = signinRequired();
|
const $i = signinRequired();
|
||||||
|
|
||||||
|
@ -136,8 +137,7 @@ const gaConsent = computed({
|
||||||
gaConsentInternal.value = value;
|
gaConsentInternal.value = value;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const gtagConsentInternal = ref(
|
const gtagConsentInternal = ref({
|
||||||
(miLocalStorage.getItemAsJson('gtagConsent') as GtagConsentParams) ?? {
|
|
||||||
ad_storage: 'denied',
|
ad_storage: 'denied',
|
||||||
ad_user_data: 'denied',
|
ad_user_data: 'denied',
|
||||||
ad_personalization: 'denied',
|
ad_personalization: 'denied',
|
||||||
|
@ -145,9 +145,9 @@ const gtagConsentInternal = ref(
|
||||||
functionality_storage: 'denied',
|
functionality_storage: 'denied',
|
||||||
personalization_storage: 'denied',
|
personalization_storage: 'denied',
|
||||||
security_storage: 'granted',
|
security_storage: 'granted',
|
||||||
},
|
...(miLocalStorage.getItemAsJson('gtagConsent') as GtagConsentParams),
|
||||||
);
|
});
|
||||||
const gtagConsent = computed({
|
const gtagConsentParams = computed({
|
||||||
get: () => gtagConsentInternal.value,
|
get: () => gtagConsentInternal.value,
|
||||||
set: (value: GtagConsentParams) => {
|
set: (value: GtagConsentParams) => {
|
||||||
miLocalStorage.setItemAsJson('gtagConsent', value);
|
miLocalStorage.setItemAsJson('gtagConsent', value);
|
||||||
|
@ -155,10 +155,10 @@ const gtagConsent = computed({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const gtagConsentAnalytics = computed({
|
const gtagConsentAnalytics = computed({
|
||||||
get: () => gtagConsent.value.analytics_storage === 'granted',
|
get: () => gtagConsentParams.value.analytics_storage === 'granted',
|
||||||
set: (value: boolean) => {
|
set: (value: boolean) => {
|
||||||
gtagConsent.value = {
|
gtagConsentParams.value = {
|
||||||
...gtagConsent.value,
|
...gtagConsentParams.value,
|
||||||
ad_storage: value ? 'granted' : 'denied',
|
ad_storage: value ? 'granted' : 'denied',
|
||||||
ad_user_data: value ? 'granted' : 'denied',
|
ad_user_data: value ? 'granted' : 'denied',
|
||||||
analytics_storage: value ? 'granted' : 'denied',
|
analytics_storage: value ? 'granted' : 'denied',
|
||||||
|
@ -166,19 +166,19 @@ const gtagConsentAnalytics = computed({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const gtagConsentFunctionality = computed({
|
const gtagConsentFunctionality = computed({
|
||||||
get: () => gtagConsent.value.functionality_storage === 'granted',
|
get: () => gtagConsentParams.value.functionality_storage === 'granted',
|
||||||
set: (value: boolean) => {
|
set: (value: boolean) => {
|
||||||
gtagConsent.value = {
|
gtagConsentParams.value = {
|
||||||
...gtagConsent.value,
|
...gtagConsentParams.value,
|
||||||
functionality_storage: value ? 'granted' : 'denied',
|
functionality_storage: value ? 'granted' : 'denied',
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const gtagConsentPersonalization = computed({
|
const gtagConsentPersonalization = computed({
|
||||||
get: () => gtagConsent.value.personalization_storage === 'granted',
|
get: () => gtagConsentParams.value.personalization_storage === 'granted',
|
||||||
set: (value: boolean) => {
|
set: (value: boolean) => {
|
||||||
gtagConsent.value = {
|
gtagConsentParams.value = {
|
||||||
...gtagConsent.value,
|
...gtagConsentParams.value,
|
||||||
ad_personalization: value ? 'granted' : 'denied',
|
ad_personalization: value ? 'granted' : 'denied',
|
||||||
personalization_storage: value ? 'granted' : 'denied',
|
personalization_storage: value ? 'granted' : 'denied',
|
||||||
};
|
};
|
||||||
|
@ -199,8 +199,8 @@ watch(gaConsent, async () => {
|
||||||
await reloadAsk();
|
await reloadAsk();
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(gtagConsent, async () => {
|
watch(gtagConsentParams, async () => {
|
||||||
if (typeof window['gtag'] === 'function') (window['gtag'] as GtagConsent)('consent', 'update', gtagConsent.value);
|
gtagConsent('update', gtagConsentParams.value as GtagConsentParams);
|
||||||
});
|
});
|
||||||
|
|
||||||
function save() {
|
function save() {
|
||||||
|
|
|
@ -20,6 +20,8 @@ import { clipsCache } from '@/cache.js';
|
||||||
import { MenuItem } from '@/types/menu.js';
|
import { MenuItem } from '@/types/menu.js';
|
||||||
import MkRippleEffect from '@/components/MkRippleEffect.vue';
|
import MkRippleEffect from '@/components/MkRippleEffect.vue';
|
||||||
import { isSupportShare } from '@/scripts/navigator.js';
|
import { isSupportShare } from '@/scripts/navigator.js';
|
||||||
|
import { isMute, playUrl } from '@/scripts/sound.js';
|
||||||
|
import { isAprilFoolsDay } from '@/scripts/seasonal-events.js';
|
||||||
|
|
||||||
export async function getNoteClipMenu(props: {
|
export async function getNoteClipMenu(props: {
|
||||||
note: Misskey.entities.Note;
|
note: Misskey.entities.Note;
|
||||||
|
@ -180,10 +182,14 @@ export function getNoteMenu(props: {
|
||||||
function del(): void {
|
function del(): void {
|
||||||
os.confirm({
|
os.confirm({
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
text: i18n.ts.noteDeleteConfirm,
|
text: isAprilFoolsDay() ? i18n.ts.deleteNotWash + '\n' + i18n.ts.noteDeleteConfirm : i18n.ts.noteDeleteConfirm,
|
||||||
}).then(({ canceled }) => {
|
}).then(({ canceled }) => {
|
||||||
if (canceled) return;
|
if (canceled) return;
|
||||||
|
|
||||||
|
if (isAprilFoolsDay() && !isMute()) {
|
||||||
|
playUrl('/client-assets/sounds/flush.mp3', { volume: defaultStore.state.sound_masterVolume });
|
||||||
|
}
|
||||||
|
|
||||||
misskeyApi('notes/delete', {
|
misskeyApi('notes/delete', {
|
||||||
noteId: appearNote.id,
|
noteId: appearNote.id,
|
||||||
});
|
});
|
||||||
|
@ -456,8 +462,8 @@ export function getNoteMenu(props: {
|
||||||
action: delEdit,
|
action: delEdit,
|
||||||
} : undefined,
|
} : undefined,
|
||||||
{
|
{
|
||||||
icon: 'ti ti-trash',
|
icon: isAprilFoolsDay() ? 'ti ti-whirl' : 'ti ti-trash',
|
||||||
text: i18n.ts.delete,
|
text: isAprilFoolsDay() ? i18n.ts.flushItAway : i18n.ts.delete,
|
||||||
disabled: !appearNote.isDeletable,
|
disabled: !appearNote.isDeletable,
|
||||||
danger: true,
|
danger: true,
|
||||||
action: del,
|
action: del,
|
||||||
|
|
47
packages/frontend/src/scripts/seasonal-events.ts
Normal file
47
packages/frontend/src/scripts/seasonal-events.ts
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
enum EventTypes {
|
||||||
|
Christmas = 0,
|
||||||
|
NewYear = 1,
|
||||||
|
ValentinesDay = 2,
|
||||||
|
Halloween = 3,
|
||||||
|
AprilFoolsDay = 4,
|
||||||
|
Unknown = 5
|
||||||
|
}
|
||||||
|
|
||||||
|
export const isEventDay = (): EventTypes => {
|
||||||
|
const date = new Date();
|
||||||
|
const month = date.getMonth();
|
||||||
|
const day = date.getDate();
|
||||||
|
|
||||||
|
// jsで月を0から始まる
|
||||||
|
// 例えば4月は月の値が3になる
|
||||||
|
if (month === 11 && day === 25) return EventTypes.Christmas;
|
||||||
|
if (month === 0 && day === 1) return EventTypes.NewYear;
|
||||||
|
if (month === 1 && day === 14) return EventTypes.ValentinesDay;
|
||||||
|
if (month === 9 && day === 31) return EventTypes.Halloween;
|
||||||
|
if (month === 3 && day === 1) return EventTypes.AprilFoolsDay;
|
||||||
|
return EventTypes.Unknown;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isAprilFoolsDay = (): boolean => {
|
||||||
|
return isEventDay() === EventTypes.AprilFoolsDay;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isChristmas = (): boolean => {
|
||||||
|
return isEventDay() === EventTypes.Christmas;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isNewYear = (): boolean => {
|
||||||
|
return isEventDay() === EventTypes.NewYear;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isValentinesDay = (): boolean => {
|
||||||
|
return isEventDay() === EventTypes.ValentinesDay;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isHalloween = (): boolean => {
|
||||||
|
return isEventDay() === EventTypes.Halloween;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isUnknown = (): boolean => {
|
||||||
|
return isEventDay() === EventTypes.Unknown;
|
||||||
|
};
|
|
@ -8,8 +8,8 @@
|
||||||
|
|
||||||
import { generateClientTransactionId } from '@/scripts/misskey-api.js';
|
import { generateClientTransactionId } from '@/scripts/misskey-api.js';
|
||||||
import { miLocalStorage } from '@/local-storage.js';
|
import { miLocalStorage } from '@/local-storage.js';
|
||||||
import { GtagConsentParams } from 'vue-gtag';
|
|
||||||
import { instance } from '@/instance.js';
|
import { instance } from '@/instance.js';
|
||||||
|
import type { GtagConsentParams } from '@/types/gtag.js';
|
||||||
|
|
||||||
export interface UsageReport {
|
export interface UsageReport {
|
||||||
t: number;
|
t: number;
|
||||||
|
|
|
@ -534,6 +534,17 @@ html[data-color-mode=dark] ._woodenFrame {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes global-spin-shrink {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0deg) scale(1);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotate(2160deg) scale(0);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
._anime_bounce {
|
._anime_bounce {
|
||||||
will-change: transform;
|
will-change: transform;
|
||||||
animation: global-bounce ease 0.7s;
|
animation: global-bounce ease 0.7s;
|
||||||
|
|
11
packages/frontend/src/types/gtag.ts
Normal file
11
packages/frontend/src/types/gtag.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
export interface GtagConsentParams {
|
||||||
|
ad_storage?: 'granted' | 'denied',
|
||||||
|
ad_user_data?: 'granted' | 'denied',
|
||||||
|
ad_personalization?: 'granted' | 'denied',
|
||||||
|
analytics_storage?: 'granted' | 'denied',
|
||||||
|
functionality_storage?: 'granted' | 'denied',
|
||||||
|
personalization_storage?: 'granted' | 'denied',
|
||||||
|
security_storage?: 'granted' | 'denied',
|
||||||
|
wait_for_update?: number,
|
||||||
|
region?: string[],
|
||||||
|
}
|
|
@ -26,19 +26,19 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@misskey-dev/eslint-plugin": "1.0.0",
|
"@misskey-dev/eslint-plugin": "1.0.0",
|
||||||
"@types/matter-js": "0.19.8",
|
"@types/matter-js": "0.19.8",
|
||||||
"@types/node": "22.13.0",
|
"@types/node": "22.13.14",
|
||||||
"@types/seedrandom": "3.0.8",
|
"@types/seedrandom": "3.0.8",
|
||||||
"@typescript-eslint/eslint-plugin": "7.10.0",
|
"@typescript-eslint/eslint-plugin": "7.10.0",
|
||||||
"@typescript-eslint/parser": "7.10.0",
|
"@typescript-eslint/parser": "7.10.0",
|
||||||
"eslint": "8.57.1",
|
"eslint": "8.57.1",
|
||||||
"nodemon": "3.1.9",
|
"nodemon": "3.1.9",
|
||||||
"typescript": "5.7.3"
|
"typescript": "5.8.2"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"built"
|
"built"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "0.24.2",
|
"esbuild": "0.25.2",
|
||||||
"eventemitter3": "5.0.1",
|
"eventemitter3": "5.0.1",
|
||||||
"glob": "11.0.1",
|
"glob": "11.0.1",
|
||||||
"matter-js": "0.20.0",
|
"matter-js": "0.20.0",
|
||||||
|
|
|
@ -8,16 +8,16 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@misskey-dev/eslint-plugin": "1.0.0",
|
"@misskey-dev/eslint-plugin": "1.0.0",
|
||||||
"@readme/openapi-parser": "2.7.0",
|
"@readme/openapi-parser": "3.0.1",
|
||||||
"@types/node": "22.13.0",
|
"@types/node": "22.13.14",
|
||||||
"@typescript-eslint/eslint-plugin": "7.10.0",
|
"@typescript-eslint/eslint-plugin": "7.10.0",
|
||||||
"@typescript-eslint/parser": "7.10.0",
|
"@typescript-eslint/parser": "7.10.0",
|
||||||
"eslint": "8.57.1",
|
"eslint": "8.57.1",
|
||||||
"openapi-types": "12.1.3",
|
"openapi-types": "12.1.3",
|
||||||
"openapi-typescript": "6.7.6",
|
"openapi-typescript": "6.7.6",
|
||||||
"ts-case-convert": "2.1.0",
|
"ts-case-convert": "2.1.0",
|
||||||
"tsx": "4.19.2",
|
"tsx": "4.19.3",
|
||||||
"typescript": "5.7.3"
|
"typescript": "5.8.2"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"built"
|
"built"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { mkdir, writeFile } from 'fs/promises';
|
import { mkdir, 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 { parse as OpenAPIParser } from '@readme/openapi-parser';
|
||||||
import openapiTS from 'openapi-typescript';
|
import openapiTS from 'openapi-typescript';
|
||||||
|
|
||||||
async function generateBaseTypes(
|
async function generateBaseTypes(
|
||||||
|
@ -319,7 +319,7 @@ async function main() {
|
||||||
await mkdir(generatePath, { recursive: true });
|
await mkdir(generatePath, { recursive: true });
|
||||||
|
|
||||||
const openApiJsonPath = './api.json';
|
const openApiJsonPath = './api.json';
|
||||||
const openApiDocs = await OpenAPIParser.parse(openApiJsonPath) as OpenAPIV3_1.Document;
|
const openApiDocs = await OpenAPIParser(openApiJsonPath) as OpenAPIV3_1.Document;
|
||||||
|
|
||||||
const typeFileName = './built/autogen/types.ts';
|
const typeFileName = './built/autogen/types.ts';
|
||||||
await generateBaseTypes(openApiDocs, openApiJsonPath, typeFileName);
|
await generateBaseTypes(openApiDocs, openApiJsonPath, typeFileName);
|
||||||
|
|
|
@ -35,11 +35,11 @@
|
||||||
"url": "git+https://github.com/misskey-dev/misskey.js.git"
|
"url": "git+https://github.com/misskey-dev/misskey.js.git"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@microsoft/api-extractor": "7.49.1",
|
"@microsoft/api-extractor": "7.52.2",
|
||||||
"@misskey-dev/eslint-plugin": "1.0.0",
|
"@misskey-dev/eslint-plugin": "1.0.0",
|
||||||
"@swc/jest": "0.2.37",
|
"@swc/jest": "0.2.37",
|
||||||
"@types/jest": "29.5.14",
|
"@types/jest": "29.5.14",
|
||||||
"@types/node": "22.10.7",
|
"@types/node": "22.13.14",
|
||||||
"@typescript-eslint/eslint-plugin": "7.10.0",
|
"@typescript-eslint/eslint-plugin": "7.10.0",
|
||||||
"@typescript-eslint/parser": "7.10.0",
|
"@typescript-eslint/parser": "7.10.0",
|
||||||
"eslint": "8.57.1",
|
"eslint": "8.57.1",
|
||||||
|
@ -50,7 +50,7 @@
|
||||||
"ncp": "2.0.0",
|
"ncp": "2.0.0",
|
||||||
"nodemon": "3.1.9",
|
"nodemon": "3.1.9",
|
||||||
"tsd": "0.31.2",
|
"tsd": "0.31.2",
|
||||||
"typescript": "5.7.3"
|
"typescript": "5.8.2"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"built",
|
"built",
|
||||||
|
@ -59,7 +59,7 @@
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@swc/cli": "0.6.0",
|
"@swc/cli": "0.6.0",
|
||||||
"@swc/core": "1.10.7",
|
"@swc/core": "1.11.13",
|
||||||
"eventemitter3": "5.0.1",
|
"eventemitter3": "5.0.1",
|
||||||
"reconnecting-websocket": "4.4.0"
|
"reconnecting-websocket": "4.4.0"
|
||||||
}
|
}
|
||||||
|
|
|
@ -4616,6 +4616,14 @@ export type components = {
|
||||||
/** @enum {string} */
|
/** @enum {string} */
|
||||||
type: 'scheduledNoteError';
|
type: 'scheduledNoteError';
|
||||||
draft: components['schemas']['NoteDraft'];
|
draft: components['schemas']['NoteDraft'];
|
||||||
|
} | {
|
||||||
|
/** Format: id */
|
||||||
|
id: string;
|
||||||
|
/** Format: date-time */
|
||||||
|
createdAt: string;
|
||||||
|
/** @enum {string} */
|
||||||
|
type: 'sensitiveFlagAssigned';
|
||||||
|
fileId: unknown;
|
||||||
} | {
|
} | {
|
||||||
/** Format: id */
|
/** Format: id */
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -4675,6 +4683,7 @@ export type components = {
|
||||||
/** @example 51469 */
|
/** @example 51469 */
|
||||||
size: number;
|
size: number;
|
||||||
isSensitive: boolean;
|
isSensitive: boolean;
|
||||||
|
isSensitiveByModerator?: boolean | null;
|
||||||
blurhash: string | null;
|
blurhash: string | null;
|
||||||
properties: {
|
properties: {
|
||||||
/** @example 1280 */
|
/** @example 1280 */
|
||||||
|
@ -20781,8 +20790,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' | 'noteScheduled' | 'scheduledNotePosted' | 'scheduledNoteError' | 'app' | 'test' | 'pollVote' | 'groupInvited')[];
|
includeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'receiveFollowRequest' | 'followRequestAccepted' | 'roleAssigned' | 'achievementEarned' | 'noteScheduled' | 'scheduledNotePosted' | 'scheduledNoteError' | 'sensitiveFlagAssigned' | 'app' | 'test' | 'pollVote' | 'groupInvited')[];
|
||||||
excludeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'receiveFollowRequest' | 'followRequestAccepted' | 'roleAssigned' | 'achievementEarned' | 'noteScheduled' | 'scheduledNotePosted' | 'scheduledNoteError' | 'app' | 'test' | 'pollVote' | 'groupInvited')[];
|
excludeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'receiveFollowRequest' | 'followRequestAccepted' | 'roleAssigned' | 'achievementEarned' | 'noteScheduled' | 'scheduledNotePosted' | 'scheduledNoteError' | 'sensitiveFlagAssigned' | 'app' | 'test' | 'pollVote' | 'groupInvited')[];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -20849,8 +20858,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' | 'noteScheduled' | 'scheduledNotePosted' | 'scheduledNoteError' | 'app' | 'test' | 'reaction:grouped' | 'renote:grouped' | 'pollVote' | 'groupInvited')[];
|
includeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'receiveFollowRequest' | 'followRequestAccepted' | 'roleAssigned' | 'achievementEarned' | 'noteScheduled' | 'scheduledNotePosted' | 'scheduledNoteError' | 'sensitiveFlagAssigned' | 'app' | 'test' | 'reaction:grouped' | 'renote:grouped' | 'pollVote' | 'groupInvited')[];
|
||||||
excludeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'receiveFollowRequest' | 'followRequestAccepted' | 'roleAssigned' | 'achievementEarned' | 'noteScheduled' | 'scheduledNotePosted' | 'scheduledNoteError' | 'app' | 'test' | 'reaction:grouped' | 'renote:grouped' | 'pollVote' | 'groupInvited')[];
|
excludeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'receiveFollowRequest' | 'followRequestAccepted' | 'roleAssigned' | 'achievementEarned' | 'noteScheduled' | 'scheduledNotePosted' | 'scheduledNoteError' | 'sensitiveFlagAssigned' | 'app' | 'test' | 'reaction:grouped' | 'renote:grouped' | 'pollVote' | 'groupInvited')[];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -25,16 +25,16 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@misskey-dev/eslint-plugin": "1.0.0",
|
"@misskey-dev/eslint-plugin": "1.0.0",
|
||||||
"@types/node": "22.13.0",
|
"@types/node": "22.13.14",
|
||||||
"@typescript-eslint/eslint-plugin": "7.10.0",
|
"@typescript-eslint/eslint-plugin": "7.10.0",
|
||||||
"@typescript-eslint/parser": "7.10.0",
|
"@typescript-eslint/parser": "7.10.0",
|
||||||
"eslint": "8.57.1",
|
"eslint": "8.57.1",
|
||||||
"nodemon": "3.1.9",
|
"nodemon": "3.1.9",
|
||||||
"typescript": "5.7.3"
|
"typescript": "5.8.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"crc-32": "1.2.2",
|
"crc-32": "1.2.2",
|
||||||
"esbuild": "0.24.2",
|
"esbuild": "0.25.2",
|
||||||
"glob": "11.0.1"
|
"glob": "11.0.1"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
|
|
|
@ -9,18 +9,18 @@
|
||||||
"lint": "pnpm typecheck && pnpm eslint"
|
"lint": "pnpm typecheck && pnpm eslint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "0.24.2",
|
"esbuild": "0.25.2",
|
||||||
"idb-keyval": "6.2.1",
|
"idb-keyval": "6.2.1",
|
||||||
"misskey-js": "workspace:*"
|
"misskey-js": "workspace:*"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@misskey-dev/eslint-plugin": "1.0.0",
|
"@misskey-dev/eslint-plugin": "1.0.0",
|
||||||
"@types/serviceworker": "0.0.118",
|
"@types/serviceworker": "0.0.127",
|
||||||
"@typescript-eslint/parser": "7.10.0",
|
"@typescript-eslint/parser": "7.10.0",
|
||||||
"eslint": "8.57.1",
|
"eslint": "8.57.1",
|
||||||
"eslint-plugin-import": "2.31.0",
|
"eslint-plugin-import": "2.31.0",
|
||||||
"nodemon": "3.1.9",
|
"nodemon": "3.1.9",
|
||||||
"typescript": "5.7.3"
|
"typescript": "5.8.2"
|
||||||
},
|
},
|
||||||
"type": "module"
|
"type": "module"
|
||||||
}
|
}
|
||||||
|
|
5259
pnpm-lock.yaml
generated
5259
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue