Merge remote-tracking branch 'upstream/develop' into develop
This commit is contained in:
commit
d686862467
|
@ -106,7 +106,7 @@ redis:
|
|||
# ┌───────────────────────────┐
|
||||
#───┘ MeiliSearch configuration └─────────────────────────────
|
||||
|
||||
# You can set scope to local (default value) or global
|
||||
# You can set scope to local (default value) or global
|
||||
# (include notes from remote).
|
||||
|
||||
#meilisearch:
|
||||
|
@ -151,7 +151,7 @@ id: 'aidx'
|
|||
|
||||
# Job rate limiter
|
||||
# deliverJobPerSec: 128
|
||||
# inboxJobPerSec: 16
|
||||
# inboxJobPerSec: 32
|
||||
|
||||
# Job attempts
|
||||
# deliverJobMaxAttempts: 12
|
||||
|
@ -194,7 +194,7 @@ proxyRemoteFiles: true
|
|||
signToActivityPubGet: true
|
||||
|
||||
# For security reasons, uploading attachments from the intranet is prohibited,
|
||||
# but exceptions can be made from the following settings. Default value is "undefined".
|
||||
# but exceptions can be made from the following settings. Default value is "undefined".
|
||||
# Read changelog to learn more (Improvements of 12.90.0 (2021/09/04)).
|
||||
#allowedPrivateNetworks: [
|
||||
# '127.0.0.1/32'
|
||||
|
|
|
@ -166,7 +166,7 @@ id: 'aidx'
|
|||
|
||||
# Job rate limiter
|
||||
#deliverJobPerSec: 128
|
||||
#inboxJobPerSec: 16
|
||||
#inboxJobPerSec: 32
|
||||
#relashionshipJobPerSec: 64
|
||||
|
||||
# Job attempts
|
||||
|
|
|
@ -147,7 +147,7 @@ id: 'aidx'
|
|||
|
||||
# Job rate limiter
|
||||
# deliverJobPerSec: 128
|
||||
# inboxJobPerSec: 16
|
||||
# inboxJobPerSec: 32
|
||||
|
||||
# Job attempts
|
||||
# deliverJobMaxAttempts: 12
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
- Feat: メールアドレスの認証にverifymail.ioを使えるように (cherry-pick from https://github.com/TeamNijimiss/misskey/commit/971ba07a44550f68d2ba31c62066db2d43a0caed)
|
||||
- Feat: モデレーターがユーザーのアイコンもしくはバナー画像を未設定状態にできる機能を追加 (cherry-pick from https://github.com/TeamNijimiss/misskey/commit/e0eb5a752f6e5616d6312bb7c9790302f9dbff83)
|
||||
- Feat: TL上からノートが見えなくなるワードミュートであるハードミュートを追加
|
||||
- Enhance: アイコンデコレーションを複数設定できるように
|
||||
- Fix: MFM `$[unixtime ]` に不正な値を入力した際に発生する各種エラーを修正
|
||||
|
||||
### Client
|
||||
|
@ -35,6 +36,7 @@
|
|||
- Enhance: ノートプレビューに「内容を隠す」が反映されるように
|
||||
- Enhance: データセーバーの適用範囲を個別で設定できるように
|
||||
- 従来のデータセーバーの設定はリセットされます
|
||||
- Enhance: タイムライン上のタブからリスト、アンテナ、チャンネルの管理ページにジャンプできるように
|
||||
- Feat: センシティブと判断されたウェブサイトのサムネイルをぼかすように
|
||||
- ウェブサイトをセンシティブと判断する仕組みが動いていないため、summalyProxyを使用しないと機能しません。
|
||||
- fix: 「設定のバックアップ」で一部の項目がバックアップに含まれていなかった問題を修正
|
||||
|
@ -46,9 +48,12 @@
|
|||
- Fix: 共有機能をサポートしていないブラウザの場合は共有ボタンを非表示にする #11305
|
||||
- Fix: 通知のグルーピング設定を変更してもリロードされるまで表示が変わらない問題を修正 #12470
|
||||
- Fix: 長い名前のチャンネルにおける投稿フォームの表示が崩れる問題を修正
|
||||
- Fix: セキュリティ向上のためAiScriptの`Mk:apiExternal`を無効化
|
||||
- Fix: ノート中の絵文字をタップして「リアクションする」からリアクションした際にリアクションサウンドが鳴らない不具合を修正
|
||||
|
||||
### Server
|
||||
- Enhance: MFM `$[ruby ]` が他ソフトウェアと連合されるように
|
||||
- Enhance: Meilisearchを有効にした検索で、ユーザーのミュートやブロックを考慮するように
|
||||
- Fix: 時間経過により無効化されたアンテナを再有効化したとき、サーバ再起動までその状況が反映されないのを修正 #12303
|
||||
- Fix: ロールタイムラインが保存されない問題を修正
|
||||
- Fix: api.jsonの生成ロジックを改善 #12402
|
||||
|
@ -61,6 +66,8 @@
|
|||
- Fix: ユーザのノート一覧にてインスタンスミュートが効かない問題
|
||||
- Fix: チャンネルのノート一覧にてインスタンスミュートが効かない問題
|
||||
- Fix: 「みつける」が年越し時に壊れる問題を修正
|
||||
- Fix: アカウントをブロックした際に、自身のユーザーのページでノートが相手に表示される問題を修正
|
||||
- Fix: モデレーションログがモデレーターは閲覧できないように修正
|
||||
|
||||
## 2023.11.1
|
||||
|
||||
|
|
|
@ -36,7 +36,6 @@ Misskey의 전체 변경 사항을 확인하려면, [CHANGELOG.md#2023xx](CHANGE
|
|||
### General
|
||||
- Change: 노트를 번역할 때 유저가 고양이로 설정되어 있으면 nyaize를 적용
|
||||
- Feat: 리모트 서버의 이모지를 즉시 가져올 수 있음 ([pikokr/cherrypicnic@03d536c0](https://github.com/pikokr/cherrypicnic/commit/03d536c00212f2dfbebecf75e5d58e0ddb749444), [pikokr/cherrypicnic@8a2d6f3b](https://github.com/pikokr/cherrypicnic/commit/8a2d6f3b518fc13a6c32364780fba3be5eea3e5d))
|
||||
- Feat: 아이콘 장식을 여러 개 겹칠 수 있음 ([Secineralyr/misskey.dream@d929c8bf](https://github.com/Secineralyr/misskey.dream/commit/d929c8bf97add7fac64c12e0bcdfaa164031f864))
|
||||
- Feat: 아이콘 장식을 세부 조정할 수 있음 ([Secineralyr/misskey.dream@b3299181](https://github.com/Secineralyr/misskey.dream/commit/b329918194f1991c84633361d8a1319cf203641c), [Secineralyr/misskey.dream@1a9642bb](https://github.com/Secineralyr/misskey.dream/commit/1a9642bb9087a256522767e113c3bbfa87ec2e47))
|
||||
- 위치, 크기, 불투명도를 추가로 조정할 수 있습니다.
|
||||
- Feat: 노트를 클릭하여 자세히 볼 수 있음
|
||||
|
@ -46,6 +45,7 @@ Misskey의 전체 변경 사항을 확인하려면, [CHANGELOG.md#2023xx](CHANGE
|
|||
- Feat: 데이터 절약 모드로 코드 하이라이트 로드를 줄일 수 있음 (misskey-dev/misskey#12526)
|
||||
- Feat: InstanceTicker를 클릭해 노트를 자세히 볼 수 있음
|
||||
- 리모트에서 수신된 노트인 경우, '리모트에서 보기'로 작동함
|
||||
- Feat: 신규 도전 과제 추가
|
||||
- Enhance: 사운드 설정을 기본값으로 복원하거나 저장할 때 확실하게 표시함
|
||||
- Enhance: 리모트 서버와 동일한 이모지가 존재하지 않는 경우 '이모지 복사'를 비활성화함
|
||||
- Enhance: 아이콘 장식을 바로 업로드 하거나 드라이브에서 불러올 수 있음 ([Secineralyr/misskey.dream@e358212d](https://github.com/Secineralyr/misskey.dream/commit/e358212da93256749e31d9e0ca9dd2ed37fd548e), [Secineralyr/misskey.dream@52592fea](https://github.com/Secineralyr/misskey.dream/commit/52592fea52684497ba7e07f173aac2b1083afcb1))
|
||||
|
|
|
@ -117,6 +117,10 @@ command.
|
|||
- Server-side source files and automatically builds them if they are modified. Automatically start the server process(es).
|
||||
- Vite HMR (just the `vite` command) is available. The behavior may be different from production.
|
||||
- Service Worker is watched by esbuild.
|
||||
- The front end can be viewed by accessing `http://localhost:5173`.
|
||||
- The backend listens on the port configured with `port` in .config/default.yml.
|
||||
If you have not changed it from the default, it will be "http://localhost:3000".
|
||||
If "port" in .config/default.yml is set to something other than 3000, you need to change the proxy settings in packages/frontend/vite.config.local-dev.ts.
|
||||
|
||||
### Dev Container
|
||||
Instead of running `pnpm` locally, you can use Dev Container to set up your development environment.
|
||||
|
|
|
@ -167,7 +167,7 @@ id: "aidx"
|
|||
|
||||
# Job rate limiter
|
||||
# deliverJobPerSec: 128
|
||||
# inboxJobPerSec: 16
|
||||
# inboxJobPerSec: 32
|
||||
|
||||
# Job attempts
|
||||
# deliverJobMaxAttempts: 12
|
||||
|
|
42
docker-compose.local-db.yml
Normal file
42
docker-compose.local-db.yml
Normal file
|
@ -0,0 +1,42 @@
|
|||
version: "3"
|
||||
|
||||
# このconfigは、 dockerでMisskey本体を起動せず、 redisとpostgresql などだけを起動します
|
||||
|
||||
services:
|
||||
redis:
|
||||
restart: always
|
||||
image: redis:7-alpine
|
||||
ports:
|
||||
- "6379:6379"
|
||||
volumes:
|
||||
- ./redis:/data
|
||||
healthcheck:
|
||||
test: "redis-cli ping"
|
||||
interval: 5s
|
||||
retries: 20
|
||||
|
||||
db:
|
||||
restart: always
|
||||
image: postgres:15-alpine
|
||||
ports:
|
||||
- "5432:5432"
|
||||
env_file:
|
||||
- .config/docker.env
|
||||
volumes:
|
||||
- ./db:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"
|
||||
interval: 5s
|
||||
retries: 20
|
||||
|
||||
# meilisearch:
|
||||
# restart: always
|
||||
# image: getmeili/meilisearch:v1.3.4
|
||||
# environment:
|
||||
# - MEILI_NO_ANALYTICS=true
|
||||
# - MEILI_ENV=production
|
||||
# env_file:
|
||||
# - .config/meilisearch.env
|
||||
# volumes:
|
||||
# - ./meili_data:/meili_data
|
||||
|
|
@ -1345,6 +1345,9 @@ _achievements:
|
|||
_setNameToSyuilo:
|
||||
title: "Boží komplex"
|
||||
description: "Nastavte si jméno na \"syuilo\""
|
||||
_setNameToNoriDev:
|
||||
title: "Boží komplex (CherryPick)"
|
||||
description: "Nastavte si jméno na \"noridev\""
|
||||
_passedSinceAccountCreated1:
|
||||
title: "Roční výročí"
|
||||
description: "Od vytvoření vašeho účtu uplynul jeden rok"
|
||||
|
|
|
@ -1435,6 +1435,9 @@ _achievements:
|
|||
_setNameToSyuilo:
|
||||
title: "Gottkomplex"
|
||||
description: "Setze deinen Namen auf \"syuilo\""
|
||||
_setNameToNoriDev:
|
||||
title: "Gottkomplex (CherryPick)"
|
||||
description: "Setze deinen Namen auf \"noridev\""
|
||||
_passedSinceAccountCreated1:
|
||||
title: "Einjahresjubiläum"
|
||||
description: "Seit der Erstellung deines Kontos ist 1 Jahr vergangen"
|
||||
|
@ -1458,7 +1461,7 @@ _achievements:
|
|||
_brainDiver:
|
||||
title: "Brain Diver"
|
||||
description: "Sende den Link zu Brain Diver"
|
||||
flavor: "CherryPick-CherryPick La-Tu-Ma"
|
||||
flavor: "Misskey-Misskey La-Tu-Ma"
|
||||
_smashTestNotificationButton:
|
||||
title: "Testüberfluss"
|
||||
description: "Betätige den Benachrichtigungstest mehrfach innerhalb einer extrem kurzen Zeitspanne"
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
---
|
||||
_lang_: "English"
|
||||
cannotBeUsedFunc: "This feature is currently unavailable."
|
||||
maxinumLayerError: "You cannot stack more than 6 layers. Please delete other layers."
|
||||
layer: "Layer"
|
||||
Xcoordinate: "X-Coordinate"
|
||||
Ycoordinate: "Y-Coordinate"
|
||||
scale: "Scale"
|
||||
|
@ -1711,6 +1709,9 @@ _achievements:
|
|||
_setNameToSyuilo:
|
||||
title: "God Complex"
|
||||
description: "Set your name to \"syuilo\""
|
||||
_setNameToNoriDev:
|
||||
title: "God Complex (CherryPick)"
|
||||
description: "Set your name to \"noridev\""
|
||||
_passedSinceAccountCreated1:
|
||||
title: "One Year Anniversary"
|
||||
description: "One year has passed since your account was created"
|
||||
|
@ -1734,7 +1735,7 @@ _achievements:
|
|||
_brainDiver:
|
||||
title: "Brain Diver"
|
||||
description: "Post the link to Brain Diver"
|
||||
flavor: "LycheeBridge-LycheeBridge La-Tu-Ma"
|
||||
flavor: "Misskey-Misskey La-Tu-Ma"
|
||||
_smashTestNotificationButton:
|
||||
title: "Test overflow"
|
||||
description: "Trigger the notification test repeatedly within an extremely short time"
|
||||
|
|
|
@ -1418,6 +1418,9 @@ _achievements:
|
|||
_setNameToSyuilo:
|
||||
title: "Complejo de superioridad"
|
||||
description: "Configurar el nombre como 'Syuilo'"
|
||||
_setNameToNoriDev:
|
||||
title: "Complejo de superioridad (CherryPick)"
|
||||
description: "Configurar el nombre como 'NoriDev'"
|
||||
_passedSinceAccountCreated1:
|
||||
title: "Primer aniversario"
|
||||
description: "Pasó un año desde la creación de la cuenta"
|
||||
|
@ -1441,7 +1444,7 @@ _achievements:
|
|||
_brainDiver:
|
||||
title: "Brain Diver"
|
||||
description: "Publicaste un vínculo a \"Brain Diver\""
|
||||
flavor: "CherryPick-CherryPick La-Tu-Ma"
|
||||
flavor: "Misskey-Misskey La-Tu-Ma"
|
||||
_smashTestNotificationButton:
|
||||
title: "Sobrecarga de pruebas"
|
||||
description: "Envía muchas notificaciones de prueba en un corto espacio de tiempo"
|
||||
|
|
|
@ -1310,6 +1310,8 @@ _achievements:
|
|||
title: "Référence circulaire"
|
||||
_setNameToSyuilo:
|
||||
description: "Vous avez spécifié « syuilo » comme nom"
|
||||
_setNameToNoriDev:
|
||||
description: "Vous avez spécifié « noridev » comme nom"
|
||||
_passedSinceAccountCreated1:
|
||||
title: "Premier anniversaire"
|
||||
_passedSinceAccountCreated2:
|
||||
|
@ -1324,7 +1326,7 @@ _achievements:
|
|||
_cookieClicked:
|
||||
flavor: "Attendez une minute, vous êtes sur le mauvais site web ?"
|
||||
_brainDiver:
|
||||
flavor: "CherryPick-CherryPick La-Tu-Ma"
|
||||
flavor: "Misskey-Misskey La-Tu-Ma"
|
||||
_smashTestNotificationButton:
|
||||
title: "Débordement de tests"
|
||||
description: "Détruire le bouton de test de notifications dans un intervalle extrêmement court"
|
||||
|
|
|
@ -1418,6 +1418,9 @@ _achievements:
|
|||
_setNameToSyuilo:
|
||||
title: "God Complex"
|
||||
description: "Atur namamu jadi \"syuilo\""
|
||||
_setNameToNoriDev:
|
||||
title: "God Complex (CherryPick)"
|
||||
description: "Atur namamu jadi \"noridev\""
|
||||
_passedSinceAccountCreated1:
|
||||
title: "Perayaan Satu Tahun"
|
||||
description: "Satu tahun telah lewat sejak akunmu dibuat"
|
||||
|
@ -1441,7 +1444,7 @@ _achievements:
|
|||
_brainDiver:
|
||||
title: "Brain Diver"
|
||||
description: "Posting tautan mengenai Brain Diver"
|
||||
flavor: "CherryPick-CherryPick La-Tu-Ma"
|
||||
flavor: "Misskey-Misskey La-Tu-Ma"
|
||||
_smashTestNotificationButton:
|
||||
title: "Tes overflow"
|
||||
description: "Picu tes notifikasi secara berulang dalam waktu yang sangat pendek"
|
||||
|
|
11
locales/index.d.ts
vendored
11
locales/index.d.ts
vendored
|
@ -4,8 +4,6 @@
|
|||
export interface Locale {
|
||||
"_lang_": string;
|
||||
"cannotBeUsedFunc": string;
|
||||
"maxinumLayerError": string;
|
||||
"layer": string;
|
||||
"Xcoordinate": string;
|
||||
"Ycoordinate": string;
|
||||
"scale": string;
|
||||
|
@ -337,6 +335,7 @@ export interface Locale {
|
|||
"removeAreYouSure": string;
|
||||
"deleteAreYouSure": string;
|
||||
"resetAreYouSure": string;
|
||||
"areYouSure": string;
|
||||
"saved": string;
|
||||
"messaging": string;
|
||||
"upload": string;
|
||||
|
@ -1271,6 +1270,7 @@ export interface Locale {
|
|||
"avatarDecorations": string;
|
||||
"attach": string;
|
||||
"detach": string;
|
||||
"detachAll": string;
|
||||
"angle": string;
|
||||
"flip": string;
|
||||
"showAvatarDecorations": string;
|
||||
|
@ -1284,6 +1284,7 @@ export interface Locale {
|
|||
"doReaction": string;
|
||||
"code": string;
|
||||
"reloadRequiredToApplySettings": string;
|
||||
"remainingN": string;
|
||||
"showUnreadNotificationsCount": string;
|
||||
"showCatOnly": string;
|
||||
"additionalPermissionsForFlash": string;
|
||||
|
@ -1817,6 +1818,10 @@ export interface Locale {
|
|||
"title": string;
|
||||
"description": string;
|
||||
};
|
||||
"_setNameToNoriDev": {
|
||||
"title": string;
|
||||
"description": string;
|
||||
};
|
||||
"_passedSinceAccountCreated1": {
|
||||
"title": string;
|
||||
"description": string;
|
||||
|
@ -1921,6 +1926,7 @@ export interface Locale {
|
|||
"canHideAds": string;
|
||||
"canSearchNotes": string;
|
||||
"canUseTranslator": string;
|
||||
"avatarDecorationLimit": string;
|
||||
};
|
||||
"_condition": {
|
||||
"isLocal": string;
|
||||
|
@ -2502,6 +2508,7 @@ export interface Locale {
|
|||
"changeAvatar": string;
|
||||
"changeBanner": string;
|
||||
"verifiedLinkDescription": string;
|
||||
"avatarDecorationMax": string;
|
||||
};
|
||||
"_exportOrImport": {
|
||||
"allNotes": string;
|
||||
|
|
|
@ -1510,6 +1510,9 @@ _achievements:
|
|||
_setNameToSyuilo:
|
||||
title: "Complesso divino"
|
||||
description: "Hai impostati il tuo nome in «syuilo»"
|
||||
_setNameToNoriDev:
|
||||
title: "Complesso divino (CherryPick)"
|
||||
description: "Hai impostati il tuo nome in «noridev»"
|
||||
_passedSinceAccountCreated1:
|
||||
title: "Primo Anniversario"
|
||||
description: "È passato un anno da quando hai creato il profilo"
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
_lang_: "日本語"
|
||||
|
||||
cannotBeUsedFunc: "この機能は現在使用できません。"
|
||||
maxinumLayerError: "6枚以上重ねることはできません。他のレイヤーを削除してください。"
|
||||
layer: "レイヤー"
|
||||
Xcoordinate: "X座標"
|
||||
Ycoordinate: "Y座標"
|
||||
scale: "大きさ"
|
||||
|
@ -334,6 +332,7 @@ removed: "削除しました"
|
|||
removeAreYouSure: "「{x}」を削除しますか?"
|
||||
deleteAreYouSure: "「{x}」を削除しますか?"
|
||||
resetAreYouSure: "リセットしますか?"
|
||||
areYouSure: "よろしいですか?"
|
||||
saved: "保存しました"
|
||||
messaging: "チャット"
|
||||
upload: "アップロード"
|
||||
|
@ -1268,6 +1267,7 @@ tosAndPrivacyPolicy: "利用規約・プライバシーポリシー"
|
|||
avatarDecorations: "アイコンデコレーション"
|
||||
attach: "付ける"
|
||||
detach: "外す"
|
||||
detachAll: "全て外す"
|
||||
angle: "角度"
|
||||
flip: "反転"
|
||||
showAvatarDecorations: "アイコンのデコレーションを表示"
|
||||
|
@ -1281,6 +1281,7 @@ cwNotationRequired: "「内容を隠す」がオンの場合は注釈の記述
|
|||
doReaction: "リアクションする"
|
||||
code: "コード"
|
||||
reloadRequiredToApplySettings: "設定の反映にはリロードが必要です。"
|
||||
remainingN: "残り: {n}"
|
||||
showUnreadNotificationsCount: "未読の通知の数を表示する"
|
||||
showCatOnly: "キャット付きのみ"
|
||||
additionalPermissionsForFlash: "Playへの追加許可"
|
||||
|
@ -1736,6 +1737,9 @@ _achievements:
|
|||
_setNameToSyuilo:
|
||||
title: "神様コンプレックス"
|
||||
description: "名前を syuilo に設定した"
|
||||
_setNameToNoriDev:
|
||||
title: "神様コンプレックス(CherryPick)"
|
||||
description: "名前を noridev に設定した"
|
||||
_passedSinceAccountCreated1:
|
||||
title: "一周年"
|
||||
description: "アカウント作成から1年経過した"
|
||||
|
@ -1759,7 +1763,7 @@ _achievements:
|
|||
_brainDiver:
|
||||
title: "Brain Diver"
|
||||
description: "Brain Diverへのリンクを投稿した"
|
||||
flavor: "LycheeBridge-LycheeBridge La-Tu-Ma"
|
||||
flavor: "Misskey-Misskey La-Tu-Ma"
|
||||
_smashTestNotificationButton:
|
||||
title: "テスト過剰"
|
||||
description: "通知のテストをごく短時間のうちに連続して行った"
|
||||
|
@ -1829,6 +1833,7 @@ _role:
|
|||
canHideAds: "広告の非表示"
|
||||
canSearchNotes: "ノート検索の利用"
|
||||
canUseTranslator: "翻訳機能の利用"
|
||||
avatarDecorationLimit: "アイコンデコレーションの最大取付個数"
|
||||
_condition:
|
||||
isLocal: "ローカルユーザー"
|
||||
isRemote: "リモートユーザー"
|
||||
|
@ -2399,6 +2404,7 @@ _profile:
|
|||
changeAvatar: "アイコン画像を変更"
|
||||
changeBanner: "バナー画像を変更"
|
||||
verifiedLinkDescription: "内容にURLを設定すると、リンク先のWebサイトに自分のプロフィールへのリンクが含まれている場合に所有者確認済みアイコンを表示させることができます。"
|
||||
avatarDecorationMax: "最大{max}つまでデコレーションを付けられます。"
|
||||
|
||||
_exportOrImport:
|
||||
allNotes: "全てのノート"
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
---
|
||||
_lang_: "日本語 (関西弁)"
|
||||
maxinumLayerError: "6枚以上重ねられんで。他のレイヤーを削除してなー"
|
||||
layer: "レイヤー"
|
||||
headlineMisskey: "ノートでつながるネットワーク"
|
||||
introMisskey: "ようお越し!CherryPickは、オープンソースの分散型マイクロブログサービスやねん。\n「ノート」を作って、いま起こっとることを共有したり、あんたについて皆に発信しよう📡\n「ツッコミ」機能で、皆のノートに素早く反応を追加したりもできるで✌\nほな、新しい世界を探検しよか🚀"
|
||||
poweredByMisskeyDescription: "{name}は、オープンソースのプラットフォーム<b>CherryPick</b>のサーバーのひとつなんやで。"
|
||||
|
@ -1459,6 +1457,9 @@ _achievements:
|
|||
_setNameToSyuilo:
|
||||
title: "神様コンプレックス"
|
||||
description: "名前を syuilo に設定した"
|
||||
_setNameToNoriDev:
|
||||
title: "神様コンプレックス(CherryPick)"
|
||||
description: "名前を noridev に設定した"
|
||||
_passedSinceAccountCreated1:
|
||||
title: "一周年"
|
||||
description: "アカウント作成から1年経過した"
|
||||
|
@ -1482,7 +1483,7 @@ _achievements:
|
|||
_brainDiver:
|
||||
title: "Brain Diver"
|
||||
description: "Brain Diverへのリンクを投稿したった"
|
||||
flavor: "CherryPick-CherryPick La-Tu-Ma"
|
||||
flavor: "Misskey-Misskey La-Tu-Ma"
|
||||
_smashTestNotificationButton:
|
||||
title: "テスト過剰"
|
||||
description: "通知テストをごく短時間のうちに連続して行ったねん"
|
||||
|
|
|
@ -1715,17 +1715,21 @@ _achievements:
|
|||
flavor: "정말로 드리긴 어려울 것 같아요..."
|
||||
_setNameToSyuilo:
|
||||
title: "신 콤플렉스"
|
||||
description: "이름을 syuilo로 설정했습니다. 어라...?"
|
||||
description: "이름을 syuilo로 설정했습니다."
|
||||
_setNameToNoriDev:
|
||||
title: "사랑해요, CherryPick!"
|
||||
description: "표시 이름을 "NoriDev"로 설정했습니다."
|
||||
flavor: "위대하신 CherryPick 개발자님께 경례!"
|
||||
_passedSinceAccountCreated1:
|
||||
title: "#My1stLycheeBridgeAnniversary"
|
||||
title: "#My1stLBAnniversary"
|
||||
description: "계정을 생성하고 1년이 지났습니다."
|
||||
flavor: "LycheeBridge에 계정을 언제 생성했는지 기억하시나요? 저희는 기억한답니다!"
|
||||
_passedSinceAccountCreated2:
|
||||
title: "#My2ndLycheeBridgeAnniversary"
|
||||
title: "#My2ndLBAnniversary"
|
||||
description: "계정을 생성하고 2년이 지났습니다."
|
||||
flavor: "LycheeBridge에 계정을 언제 생성했는지 기억하시나요? 저희는 기억한답니다!"
|
||||
_passedSinceAccountCreated3:
|
||||
title: "#My3rdLycheeBridgeAnniversary"
|
||||
title: "#My3rdLBAnniversary"
|
||||
description: "계정을 생성하고 3년이 지났습니다."
|
||||
flavor: "LycheeBridge에 계정을 언제 생성했는지 기억하시나요? 저희는 기억한답니다!"
|
||||
_loggedInOnBirthday:
|
||||
|
@ -1742,7 +1746,7 @@ _achievements:
|
|||
_brainDiver:
|
||||
title: "Brain Diver"
|
||||
description: "Brain Diver로의 링크를 게시했습니다."
|
||||
flavor: "LycheeBridge-LycheeBridge La-Tu-Ma"
|
||||
flavor: "Misskey-Misskey La-Tu-Ma"
|
||||
_smashTestNotificationButton:
|
||||
title: "과유불급"
|
||||
description: "매우 짧은 시간 안에 알림 테스트를 여러 번 수행했습니다."
|
||||
|
|
|
@ -517,6 +517,8 @@ _achievements:
|
|||
title: "Rett og slett heldig"
|
||||
_setNameToSyuilo:
|
||||
description: "Du satte navnet ditt til \"syuilo\""
|
||||
_setNameToNoriDev:
|
||||
description: "Du satte navnet ditt til \"noridev\""
|
||||
_passedSinceAccountCreated1:
|
||||
title: "Ett års jubileum"
|
||||
description: "Det har gått ett år siden kontoen din ble opprettet"
|
||||
|
@ -537,7 +539,7 @@ _achievements:
|
|||
flavor: "Er du på riktig nettsted?"
|
||||
_brainDiver:
|
||||
title: "Brain Diver"
|
||||
flavor: "CherryPick-CherryPick La-Tu-Ma"
|
||||
flavor: "Misskey-Misskey La-Tu-Ma"
|
||||
_role:
|
||||
options: "Alternativ"
|
||||
_priority:
|
||||
|
|
|
@ -1313,6 +1313,9 @@ _achievements:
|
|||
_setNameToSyuilo:
|
||||
title: "Комплекс бога"
|
||||
description: "Установлено «syuilo» в качестве имени"
|
||||
_setNameToNoriDev:
|
||||
title: "Комплекс бога (CherryPick)"
|
||||
description: "Установлено «noridev» в качестве имени"
|
||||
_passedSinceAccountCreated1:
|
||||
title: "Первая годовщина"
|
||||
description: "Прошёл 1 год с момента регистрации"
|
||||
|
|
|
@ -1408,6 +1408,9 @@ _achievements:
|
|||
_setNameToSyuilo:
|
||||
title: "พระเจ้าคอมเพล็กซ์"
|
||||
description: "ตั้งชื่อของคุณเป็น \"syuilo\""
|
||||
_setNameToNoriDev:
|
||||
title: "พระเจ้าคอมเพล็กซ์ (CherryPick)"
|
||||
description: "ตั้งชื่อของคุณเป็น \"noridev\""
|
||||
_passedSinceAccountCreated1:
|
||||
title: "ครบรอบหนึ่งปี"
|
||||
description: "ผ่านไปหนึ่งปีแล้วนะตั้งแต่บัญชีของคุณถูกสร้างขึ้นมาน่ะ"
|
||||
|
@ -1431,7 +1434,7 @@ _achievements:
|
|||
_brainDiver:
|
||||
title: "Brain Diver"
|
||||
description: "โพสต์ลิงก์ไปยัง Brain Diver"
|
||||
flavor: "CherryPick-CherryPick La-Tu-Ma"
|
||||
flavor: "Misskey-Misskey La-Tu-Ma"
|
||||
_smashTestNotificationButton:
|
||||
title: "ทดสอบโอเวอร์โฟลว์"
|
||||
description: "ทดสอบการแจ้งเตือนทริกเกอร์ซ้ำๆ ภายในระยะเวลาอันสั้นๆ"
|
||||
|
|
|
@ -1121,6 +1121,9 @@ _achievements:
|
|||
_setNameToSyuilo:
|
||||
title: "Комплекс бога"
|
||||
description: "Встановлено ім'я \"syuilo\""
|
||||
_setNameToNoriDev:
|
||||
title: "Комплекс бога (CherryPick)"
|
||||
description: "Встановлено ім'я \"noridev\""
|
||||
_passedSinceAccountCreated1:
|
||||
title: "Перша річниця"
|
||||
description: "Минув рік з моменту створення акаунта"
|
||||
|
@ -1141,7 +1144,7 @@ _achievements:
|
|||
_brainDiver:
|
||||
title: "Brain Diver"
|
||||
description: "Відправити посилання на \"Brain Diver\""
|
||||
flavor: "CherryPick-CherryPick La-Tu-Ma"
|
||||
flavor: "Misskey-Misskey La-Tu-Ma"
|
||||
_role:
|
||||
new: "Нова роль"
|
||||
edit: "Змінити роль"
|
||||
|
|
|
@ -1270,6 +1270,9 @@ _achievements:
|
|||
_setNameToSyuilo:
|
||||
title: "Ngưỡng mộ với vị thần"
|
||||
description: "Đạt tên là syuilo"
|
||||
_setNameToNoriDev:
|
||||
title: "Ngưỡng mộ với vị thần (CherryPick)"
|
||||
description: "Đạt tên là noridev"
|
||||
_passedSinceAccountCreated1:
|
||||
title: "Kỷ niệm một năm"
|
||||
_loggedInOnBirthday:
|
||||
|
|
|
@ -1410,6 +1410,9 @@ _achievements:
|
|||
_setNameToSyuilo:
|
||||
title: "像神一样呐"
|
||||
description: "将名称设定为 syuilo"
|
||||
_setNameToNoriDev:
|
||||
title: "像神一样呐 (CherryPick)"
|
||||
description: "将名称设定为 noridev"
|
||||
_passedSinceAccountCreated1:
|
||||
title: "一周年"
|
||||
description: "账户创建时间超过 1 年"
|
||||
|
@ -1433,7 +1436,7 @@ _achievements:
|
|||
_brainDiver:
|
||||
title: "Brain Diver"
|
||||
description: "发布了包含 Brain Diver 链接的帖子"
|
||||
flavor: "CherryPick-CherryPick La-Tu-Ma"
|
||||
flavor: "Misskey-Misskey La-Tu-Ma"
|
||||
_smashTestNotificationButton:
|
||||
title: "过度测试"
|
||||
description: "短时间内连续测试通知"
|
||||
|
|
|
@ -1512,6 +1512,9 @@ _achievements:
|
|||
_setNameToSyuilo:
|
||||
title: "神與您同在"
|
||||
description: "將名稱設定為 syuilo"
|
||||
_setNameToNoriDev:
|
||||
title: "神與您同在 (CherryPick)"
|
||||
description: "將名稱設定為 noridev"
|
||||
_passedSinceAccountCreated1:
|
||||
title: "一週年"
|
||||
description: "帳戶加入時間已超過一年"
|
||||
|
@ -1535,7 +1538,7 @@ _achievements:
|
|||
_brainDiver:
|
||||
title: "Brain Driver"
|
||||
description: "發佈一篇含歌曲《Brain Driver》連結的貼文"
|
||||
flavor: "CherryPick-CherryPick La-Tu-Ma"
|
||||
flavor: "Misskey-Misskey La-Tu-Ma"
|
||||
_smashTestNotificationButton:
|
||||
title: "過度測試"
|
||||
description: "極短時間內連續測試通知"
|
||||
|
|
15
package.json
15
package.json
|
@ -1,7 +1,8 @@
|
|||
{
|
||||
"name": "lycheebridge",
|
||||
"version": "4.6.3",
|
||||
"basedMisskeyVersion": "2023.12.0-beta.2",
|
||||
"version": "4.6.4",
|
||||
"basedMisskeyVersion": "2023.12.0-beta.3",
|
||||
"basedCherryPickVersion": "4.6.0-beta.3",
|
||||
"codename": "nasubi",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -30,7 +31,7 @@
|
|||
"migrateandstart": "pnpm migrate && pnpm start",
|
||||
"migrateandstart:docker": "pnpm migrate && exec pnpm start:docker",
|
||||
"watch": "pnpm dev",
|
||||
"dev": "node ./scripts/dev.mjs",
|
||||
"dev": "pnpm -r dev",
|
||||
"lint": "pnpm -r lint",
|
||||
"cy:open": "pnpm cypress open --browser --e2e --config-file=cypress.config.ts",
|
||||
"cy:run": "pnpm cypress run",
|
||||
|
@ -54,13 +55,13 @@
|
|||
"js-yaml": "4.1.0",
|
||||
"postcss": "8.4.32",
|
||||
"terser": "5.24.0",
|
||||
"typescript": "5.3.2"
|
||||
"typescript": "5.3.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "6.13.1",
|
||||
"@typescript-eslint/parser": "6.13.1",
|
||||
"@typescript-eslint/eslint-plugin": "6.13.2",
|
||||
"@typescript-eslint/parser": "6.13.2",
|
||||
"cross-env": "7.0.3",
|
||||
"cypress": "13.6.0",
|
||||
"cypress": "13.6.1",
|
||||
"eslint": "8.55.0",
|
||||
"start-server-and-test": "2.0.3",
|
||||
"ncp": "2.0.0"
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
"watch:swc": "swc src -d built -D -w",
|
||||
"build:tsc": "tsc -p tsconfig.json && tsc-alias -p tsconfig.json",
|
||||
"watch": "node watch.mjs",
|
||||
"dev": "node ./built/boot/entry.js",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"eslint": "eslint --quiet \"src/**/*.ts\"",
|
||||
"lint": "pnpm typecheck && pnpm eslint",
|
||||
|
@ -61,13 +62,13 @@
|
|||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "3.412.0",
|
||||
"@aws-sdk/lib-storage": "3.412.0",
|
||||
"@bull-board/api": "5.10.1",
|
||||
"@bull-board/fastify": "5.10.1",
|
||||
"@bull-board/ui": "5.10.1",
|
||||
"@bull-board/api": "5.10.2",
|
||||
"@bull-board/fastify": "5.10.2",
|
||||
"@bull-board/ui": "5.10.2",
|
||||
"@discordapp/twemoji": "14.1.2",
|
||||
"@fastify/accepts": "4.2.0",
|
||||
"@fastify/accepts": "4.3.0",
|
||||
"@fastify/cookie": "9.2.0",
|
||||
"@fastify/cors": "8.4.1",
|
||||
"@fastify/cors": "8.4.2",
|
||||
"@fastify/express": "2.3.0",
|
||||
"@fastify/http-proxy": "9.3.0",
|
||||
"@fastify/multipart": "8.0.0",
|
||||
|
@ -92,7 +93,7 @@
|
|||
"bcryptjs": "2.4.3",
|
||||
"blurhash": "2.0.5",
|
||||
"body-parser": "1.20.2",
|
||||
"bullmq": "4.14.4",
|
||||
"bullmq": "4.15.2",
|
||||
"cacheable-lookup": "7.0.0",
|
||||
"cbor": "9.0.1",
|
||||
"chalk": "5.3.0",
|
||||
|
@ -122,7 +123,7 @@
|
|||
"js-yaml": "4.1.0",
|
||||
"jsdom": "23.0.1",
|
||||
"json5": "2.2.3",
|
||||
"jsonld": "8.3.1",
|
||||
"jsonld": "8.3.2",
|
||||
"jsrsasign": "10.9.0",
|
||||
"meilisearch": "0.36.0",
|
||||
"microformats-parser": "1.5.2",
|
||||
|
@ -137,7 +138,7 @@
|
|||
"oauth2orize": "1.12.0",
|
||||
"oauth2orize-pkce": "0.1.2",
|
||||
"os-utils": "0.0.14",
|
||||
"otpauth": "9.2.0",
|
||||
"otpauth": "9.2.1",
|
||||
"parse5": "7.1.2",
|
||||
"pg": "8.11.3",
|
||||
"pkce-challenge": "4.0.1",
|
||||
|
@ -151,7 +152,7 @@
|
|||
"ratelimiter": "3.4.1",
|
||||
"re2": "1.20.9",
|
||||
"redis-lock": "0.1.4",
|
||||
"reflect-metadata": "0.1.13",
|
||||
"reflect-metadata": "0.1.14",
|
||||
"rename": "1.0.4",
|
||||
"rss-parser": "3.13.0",
|
||||
"rxjs": "7.8.1",
|
||||
|
@ -171,7 +172,7 @@
|
|||
"tsconfig-paths": "4.2.0",
|
||||
"twemoji-parser": "14.0.0",
|
||||
"typeorm": "0.3.17",
|
||||
"typescript": "5.3.2",
|
||||
"typescript": "5.3.3",
|
||||
"ulid": "2.3.0",
|
||||
"vary": "1.1.2",
|
||||
"web-push": "3.6.6",
|
||||
|
@ -191,14 +192,14 @@
|
|||
"@types/content-disposition": "0.5.8",
|
||||
"@types/fluent-ffmpeg": "2.1.24",
|
||||
"@types/http-link-header": "1.0.5",
|
||||
"@types/jest": "29.5.10",
|
||||
"@types/jest": "29.5.11",
|
||||
"@types/js-yaml": "4.0.9",
|
||||
"@types/jsdom": "21.1.6",
|
||||
"@types/jsonld": "1.5.13",
|
||||
"@types/jsrsasign": "10.5.12",
|
||||
"@types/mime-types": "2.1.4",
|
||||
"@types/ms": "0.7.34",
|
||||
"@types/node": "20.10.3",
|
||||
"@types/node": "20.10.4",
|
||||
"@types/node-fetch": "3.0.3",
|
||||
"@types/nodemailer": "6.4.14",
|
||||
"@types/oauth": "0.9.4",
|
||||
|
@ -221,8 +222,8 @@
|
|||
"@types/vary": "1.1.3",
|
||||
"@types/web-push": "3.6.3",
|
||||
"@types/ws": "8.5.10",
|
||||
"@typescript-eslint/eslint-plugin": "6.13.1",
|
||||
"@typescript-eslint/parser": "6.13.1",
|
||||
"@typescript-eslint/eslint-plugin": "6.13.2",
|
||||
"@typescript-eslint/parser": "6.13.2",
|
||||
"aws-sdk-client-mock": "3.0.0",
|
||||
"cross-env": "7.0.3",
|
||||
"eslint": "8.55.0",
|
||||
|
|
|
@ -83,6 +83,7 @@ export const ACHIEVEMENT_TYPES = [
|
|||
'clickedClickHere',
|
||||
'justPlainLucky',
|
||||
'setNameToSyuilo',
|
||||
'setNameToNoriDev',
|
||||
'cookieClicked',
|
||||
'brainDiver',
|
||||
'smashTestNotificationButton',
|
||||
|
|
|
@ -158,7 +158,7 @@ export class AvatarDecorationService implements OnApplicationShutdown {
|
|||
let name;
|
||||
let description;
|
||||
for (const decoration of allDecorations) {
|
||||
if (decoration.id == avatarDecorationId) {
|
||||
if (decoration.id === avatarDecorationId) {
|
||||
name = decoration.name;
|
||||
description = decoration.description;
|
||||
break;
|
||||
|
|
|
@ -28,6 +28,7 @@ type TimelineOptions = {
|
|||
redisTimelines: FanoutTimelineName[],
|
||||
noteFilter?: (note: MiNote) => boolean,
|
||||
alwaysIncludeMyNotes?: boolean;
|
||||
ignoreAuthorFromBlock?: boolean;
|
||||
ignoreAuthorFromMute?: boolean;
|
||||
excludeNoFiles?: boolean;
|
||||
excludeReplies?: boolean;
|
||||
|
@ -61,11 +62,15 @@ export class FanoutTimelineEndpointService {
|
|||
// 呼び出し元と以下の処理をシンプルにするためにdbFallbackを置き換える
|
||||
if (!ps.useDbFallback) ps.dbFallback = () => Promise.resolve([]);
|
||||
|
||||
const shouldPrepend = ps.sinceId && !ps.untilId;
|
||||
const idCompare: (a: string, b: string) => number = shouldPrepend ? (a, b) => a < b ? -1 : 1 : (a, b) => a > b ? -1 : 1;
|
||||
|
||||
const redisResult = await this.fanoutTimelineService.getMulti(ps.redisTimelines, ps.untilId, ps.sinceId);
|
||||
|
||||
// TODO: いい感じにgetMulti内でソート済だからuniqするときにredisResultが全てソート済なのを利用して再ソートを避けたい
|
||||
const redisResultIds = Array.from(new Set(redisResult.flat(1)));
|
||||
|
||||
redisResultIds.sort((a, b) => a > b ? -1 : 1);
|
||||
redisResultIds.sort(idCompare);
|
||||
noteIds = redisResultIds.slice(0, ps.limit);
|
||||
|
||||
shouldFallbackToDb = shouldFallbackToDb || (noteIds.length === 0);
|
||||
|
@ -115,7 +120,7 @@ export class FanoutTimelineEndpointService {
|
|||
|
||||
const parentFilter = filter;
|
||||
filter = (note) => {
|
||||
if (isUserRelated(note, userIdsWhoBlockingMe, ps.ignoreAuthorFromMute)) return false;
|
||||
if (isUserRelated(note, userIdsWhoBlockingMe, ps.ignoreAuthorFromBlock)) return false;
|
||||
if (isUserRelated(note, userIdsWhoMeMuting, ps.ignoreAuthorFromMute)) return false;
|
||||
if (isPureRenote(note) && isUserRelated(note, userIdsWhoMeMutingRenotes, ps.ignoreAuthorFromMute)) return false;
|
||||
if (isInstanceMuted(note, userMutedInstances)) return false;
|
||||
|
@ -132,32 +137,43 @@ export class FanoutTimelineEndpointService {
|
|||
const remainingToRead = ps.limit - redisTimeline.length;
|
||||
|
||||
// DBからの取り直しを減らす初回と同じ割合以上で成功すると仮定するが、クエリの長さを考えて三倍まで
|
||||
const countToGet = remainingToRead * Math.ceil(Math.min(1.1 / lastSuccessfulRate, 3));
|
||||
const countToGet = Math.ceil(remainingToRead * Math.min(1.1 / lastSuccessfulRate, 3));
|
||||
noteIds = redisResultIds.slice(readFromRedis, readFromRedis + countToGet);
|
||||
|
||||
readFromRedis += noteIds.length;
|
||||
|
||||
const gotFromDb = await this.getAndFilterFromDb(noteIds, filter);
|
||||
const gotFromDb = await this.getAndFilterFromDb(noteIds, filter, idCompare);
|
||||
redisTimeline.push(...gotFromDb);
|
||||
lastSuccessfulRate = gotFromDb.length / noteIds.length;
|
||||
|
||||
if (ps.allowPartial ? redisTimeline.length !== 0 : redisTimeline.length >= ps.limit) {
|
||||
// 十分Redisからとれた
|
||||
return redisTimeline.slice(0, ps.limit);
|
||||
const result = redisTimeline.slice(0, ps.limit);
|
||||
if (shouldPrepend) result.reverse();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// まだ足りない分はDBにフォールバック
|
||||
const remainingToRead = ps.limit - redisTimeline.length;
|
||||
const gotFromDb = await ps.dbFallback(noteIds[noteIds.length - 1], ps.sinceId, remainingToRead);
|
||||
redisTimeline.push(...gotFromDb);
|
||||
return redisTimeline;
|
||||
let dbUntil: string | null;
|
||||
let dbSince: string | null;
|
||||
if (shouldPrepend) {
|
||||
redisTimeline.reverse();
|
||||
dbUntil = ps.untilId;
|
||||
dbSince = noteIds[noteIds.length - 1];
|
||||
} else {
|
||||
dbUntil = noteIds[noteIds.length - 1];
|
||||
dbSince = ps.sinceId;
|
||||
}
|
||||
const gotFromDb = await ps.dbFallback(dbUntil, dbSince, remainingToRead);
|
||||
return shouldPrepend ? [...gotFromDb, ...redisTimeline] : [...redisTimeline, ...gotFromDb];
|
||||
}
|
||||
|
||||
return await ps.dbFallback(ps.untilId, ps.sinceId, ps.limit);
|
||||
}
|
||||
|
||||
private async getAndFilterFromDb(noteIds: string[], noteFilter: (note: MiNote) => boolean): Promise<MiNote[]> {
|
||||
private async getAndFilterFromDb(noteIds: string[], noteFilter: (note: MiNote) => boolean, idCompare: (a: string, b: string) => number): Promise<MiNote[]> {
|
||||
const query = this.notesRepository.createQueryBuilder('note')
|
||||
.where('note.id IN (:...noteIds)', { noteIds: noteIds })
|
||||
.innerJoinAndSelect('note.user', 'user')
|
||||
|
@ -169,7 +185,7 @@ export class FanoutTimelineEndpointService {
|
|||
|
||||
const notes = (await query.getMany()).filter(noteFilter);
|
||||
|
||||
notes.sort((a, b) => a.id > b.id ? -1 : 1);
|
||||
notes.sort((a, b) => idCompare(a.id, b.id));
|
||||
|
||||
return notes;
|
||||
}
|
||||
|
|
|
@ -220,7 +220,8 @@ export class NoteUpdateService implements OnApplicationShutdown {
|
|||
//#region AP deliver
|
||||
if (this.userEntityService.isLocalUser(user)) {
|
||||
await (async () => {
|
||||
// @ts-ignore
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-expect-error
|
||||
const noteActivity = await this.renderNoteActivity(note, user);
|
||||
|
||||
await this.deliverToConcerned(user, note, noteActivity);
|
||||
|
|
|
@ -48,6 +48,7 @@ export type RolePolicies = {
|
|||
userListLimit: number;
|
||||
userEachUserListsLimit: number;
|
||||
rateLimitFactor: number;
|
||||
avatarDecorationLimit: number;
|
||||
};
|
||||
|
||||
export const DEFAULT_POLICIES: RolePolicies = {
|
||||
|
@ -75,6 +76,7 @@ export const DEFAULT_POLICIES: RolePolicies = {
|
|||
userListLimit: 10,
|
||||
userEachUserListsLimit: 50,
|
||||
rateLimitFactor: 1,
|
||||
avatarDecorationLimit: 1,
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
|
@ -329,6 +331,7 @@ export class RoleService implements OnApplicationShutdown {
|
|||
userListLimit: calc('userListLimit', vs => Math.max(...vs)),
|
||||
userEachUserListsLimit: calc('userEachUserListsLimit', vs => Math.max(...vs)),
|
||||
rateLimitFactor: calc('rateLimitFactor', vs => Math.max(...vs)),
|
||||
avatarDecorationLimit: calc('avatarDecorationLimit', vs => Math.max(...vs)),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,8 @@ import { MiNote } from '@/models/Note.js';
|
|||
import { MiUser } from '@/models/_.js';
|
||||
import type { NotesRepository } from '@/models/_.js';
|
||||
import { sqlLikeEscape } from '@/misc/sql-like-escape.js';
|
||||
import { isUserRelated } from '@/misc/is-user-related.js';
|
||||
import { CacheService } from '@/core/CacheService.js';
|
||||
import { QueryService } from '@/core/QueryService.js';
|
||||
import { IdService } from '@/core/IdService.js';
|
||||
import type { Index, MeiliSearch } from 'meilisearch';
|
||||
|
@ -74,6 +76,7 @@ export class SearchService {
|
|||
@Inject(DI.notesRepository)
|
||||
private notesRepository: NotesRepository,
|
||||
|
||||
private cacheService: CacheService,
|
||||
private queryService: QueryService,
|
||||
private idService: IdService,
|
||||
) {
|
||||
|
@ -193,8 +196,19 @@ export class SearchService {
|
|||
limit: pagination.limit,
|
||||
});
|
||||
if (res.hits.length === 0) return [];
|
||||
const notes = await this.notesRepository.findBy({
|
||||
const [
|
||||
userIdsWhoMeMuting,
|
||||
userIdsWhoBlockingMe,
|
||||
] = me ? await Promise.all([
|
||||
this.cacheService.userMutingsCache.fetch(me.id),
|
||||
this.cacheService.userBlockedCache.fetch(me.id),
|
||||
]) : [new Set<string>(), new Set<string>()];
|
||||
const notes = (await this.notesRepository.findBy({
|
||||
id: In(res.hits.map(x => x.id)),
|
||||
})).filter(note => {
|
||||
if (me && isUserRelated(note, userIdsWhoBlockingMe)) return false;
|
||||
if (me && isUserRelated(note, userIdsWhoMeMuting)) return false;
|
||||
return true;
|
||||
});
|
||||
return notes.sort((a, b) => a.id > b.id ? -1 : 1);
|
||||
} else {
|
||||
|
|
|
@ -106,7 +106,7 @@ export class NotificationEntityService implements OnModuleInit {
|
|||
// }),
|
||||
// } : {}),
|
||||
...(notification.type === 'groupInvited' ? {
|
||||
invitation: this.userGroupInvitationEntityService.pack(notification.userGroupInvitationId!),
|
||||
invitation: this.userGroupInvitationEntityService.pack(notification.userGroupInvitationId),
|
||||
} : {}),
|
||||
...(notification.type === 'achievementEarned' ? {
|
||||
achievement: notification.achievement,
|
||||
|
@ -158,10 +158,10 @@ export class NotificationEntityService implements OnModuleInit {
|
|||
validNotifications = validNotifications.filter(x => (x.type !== 'receiveFollowRequest') || reqs.some(r => r.followerId === x.notifierId));
|
||||
}
|
||||
|
||||
const groupInvitedNotifications = validNotifications.filter(x => x.type === 'groupInvited');
|
||||
const groupInvitedNotifications = validNotifications.filter((x): x is FilterUnionByProperty<MiGroupedNotification, 'type', 'groupInvited'> => x.type === 'groupInvited');
|
||||
if (groupInvitedNotifications.length > 0) {
|
||||
const existingInvitationIds = await this.userGroupInvitationsRepository.find({
|
||||
where: { id: In(groupInvitedNotifications.map(x => x.userGroupInvitationId!)) },
|
||||
where: { id: In(groupInvitedNotifications.map(x => x.userGroupInvitationId)) },
|
||||
});
|
||||
validNotifications = validNotifications.filter(x => (x.type !== 'groupInvited') || existingInvitationIds.some(r => r.id === x.userGroupInvitationId));
|
||||
}
|
||||
|
@ -251,7 +251,7 @@ export class NotificationEntityService implements OnModuleInit {
|
|||
reaction: notification.reaction,
|
||||
} : {}),
|
||||
...(notification.type === 'groupInvited' ? {
|
||||
invitation: this.userGroupInvitationEntityService.pack(notification.userGroupInvitationId!),
|
||||
invitation: this.userGroupInvitationEntityService.pack(notification.userGroupInvitationId),
|
||||
} : {}),
|
||||
...(notification.type === 'achievementEarned' ? {
|
||||
achievement: notification.achievement,
|
||||
|
@ -308,10 +308,10 @@ export class NotificationEntityService implements OnModuleInit {
|
|||
validNotifications = validNotifications.filter(x => (x.type !== 'receiveFollowRequest') || reqs.some(r => r.followerId === x.notifierId));
|
||||
}
|
||||
|
||||
const groupInvitedNotifications = validNotifications.filter(x => x.type === 'groupInvited');
|
||||
const groupInvitedNotifications = validNotifications.filter((x): x is FilterUnionByProperty<MiGroupedNotification, 'type', 'groupInvited'> => x.type === 'groupInvited');
|
||||
if (groupInvitedNotifications.length > 0) {
|
||||
const existingInvitationIds = await this.userGroupInvitationsRepository.find({
|
||||
where: { id: In(groupInvitedNotifications.map(x => x.userGroupInvitationId!)) },
|
||||
where: { id: In(groupInvitedNotifications.map(x => x.userGroupInvitationId)) },
|
||||
});
|
||||
validNotifications = validNotifications.filter(x => (x.type !== 'groupInvited') || existingInvitationIds.some(r => r.id === x.userGroupInvitationId));
|
||||
}
|
||||
|
|
|
@ -70,10 +70,10 @@ export type MiNotification = {
|
|||
createdAt: string;
|
||||
notifierId: MiUser['id'];
|
||||
} | {
|
||||
type: 'groupInvited';
|
||||
id: string;
|
||||
createdAt: string;
|
||||
notifierId: MiUser['id'];
|
||||
type: 'groupInvited';
|
||||
id: string;
|
||||
createdAt: string;
|
||||
notifierId: MiUser['id'];
|
||||
userGroupInvitationId: MiUserGroupInvitation['id'];
|
||||
} | {
|
||||
type: 'achievementEarned';
|
||||
|
|
|
@ -145,6 +145,7 @@ export const packedRoleSchema = {
|
|||
userEachUserListsLimit: rolePolicyValue,
|
||||
canManageAvatarDecorations: rolePolicyValue,
|
||||
canUseTranslator: rolePolicyValue,
|
||||
avatarDecorationLimit: rolePolicyValue,
|
||||
},
|
||||
},
|
||||
usersCount: {
|
||||
|
|
|
@ -693,6 +693,10 @@ export const packedMeDetailedOnlySchema = {
|
|||
type: 'number',
|
||||
nullable: false, optional: false,
|
||||
},
|
||||
avatarDecorationLimit: {
|
||||
type: 'number',
|
||||
nullable: false, optional: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
//#region secrets
|
||||
|
|
|
@ -229,7 +229,7 @@ export class QueueProcessorService implements OnApplicationShutdown {
|
|||
autorun: false,
|
||||
concurrency: this.config.inboxJobConcurrency ?? 16,
|
||||
limiter: {
|
||||
max: this.config.inboxJobPerSec ?? 16,
|
||||
max: this.config.inboxJobPerSec ?? 32,
|
||||
duration: 1000,
|
||||
},
|
||||
settings: {
|
||||
|
|
|
@ -14,7 +14,7 @@ export const meta = {
|
|||
tags: ['admin'],
|
||||
|
||||
requireCredential: true,
|
||||
requireModerator: true,
|
||||
requireAdmin: true,
|
||||
|
||||
res: {
|
||||
type: 'array',
|
||||
|
|
|
@ -137,7 +137,7 @@ export const paramDef = {
|
|||
birthday: { ...birthdaySchema, nullable: true },
|
||||
lang: { type: 'string', enum: [null, ...Object.keys(langmap)] as string[], nullable: true },
|
||||
avatarId: { type: 'string', format: 'misskey:id', nullable: true },
|
||||
avatarDecorations: { type: 'array', maxItems: 5, items: {
|
||||
avatarDecorations: { type: 'array', maxItems: 16, items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', format: 'misskey:id' },
|
||||
|
@ -333,12 +333,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
|
||||
if (ps.avatarDecorations) {
|
||||
const decorations = await this.avatarDecorationService.getAll(true);
|
||||
const myRoles = await this.roleService.getUserRoles(user.id);
|
||||
const [myRoles, myPolicies] = await Promise.all([this.roleService.getUserRoles(user.id), this.roleService.getUserPolicies(user.id)]);
|
||||
const allRoles = await this.roleService.getRoles();
|
||||
const decorationIds = decorations
|
||||
.filter(d => d.roleIdsThatCanBeUsedThisDecoration.filter(roleId => allRoles.some(r => r.id === roleId)).length === 0 || myRoles.some(r => d.roleIdsThatCanBeUsedThisDecoration.includes(r.id)))
|
||||
.map(d => d.id);
|
||||
|
||||
if (ps.avatarDecorations.length > myPolicies.avatarDecorationLimit) throw new ApiError(meta.errors.restrictedByRole);
|
||||
|
||||
updates.avatarDecorations = ps.avatarDecorations.filter(d => decorationIds.includes(d.id)).map(d => ({
|
||||
id: d.id,
|
||||
angle: d.angle ?? 0,
|
||||
|
|
|
@ -9,6 +9,8 @@ import { Endpoint } from '@/server/api/endpoint-base.js';
|
|||
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { FeaturedService } from '@/core/FeaturedService.js';
|
||||
import { CacheService } from '@/core/CacheService.js';
|
||||
import { isUserRelated } from '@/misc/is-user-related.js';
|
||||
|
||||
export const meta = {
|
||||
tags: ['notes'],
|
||||
|
@ -46,6 +48,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
|
||||
private noteEntityService: NoteEntityService,
|
||||
private featuredService: FeaturedService,
|
||||
private cacheService: CacheService,
|
||||
) {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
let noteIds = await this.featuredService.getPerUserNotesRanking(ps.userId, 50);
|
||||
|
@ -60,6 +63,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
return [];
|
||||
}
|
||||
|
||||
const [
|
||||
userIdsWhoMeMuting,
|
||||
userIdsWhoBlockingMe,
|
||||
] = me ? await Promise.all([
|
||||
this.cacheService.userMutingsCache.fetch(me.id),
|
||||
this.cacheService.userBlockedCache.fetch(me.id),
|
||||
]) : [new Set<string>(), new Set<string>()];
|
||||
|
||||
const query = this.notesRepository.createQueryBuilder('note')
|
||||
.where('note.id IN (:...noteIds)', { noteIds: noteIds })
|
||||
.innerJoinAndSelect('note.user', 'user')
|
||||
|
@ -69,10 +80,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
.leftJoinAndSelect('renote.user', 'renoteUser')
|
||||
.leftJoinAndSelect('note.channel', 'channel');
|
||||
|
||||
const notes = await query.getMany();
|
||||
notes.sort((a, b) => a.id > b.id ? -1 : 1);
|
||||
const notes = (await query.getMany()).filter(note => {
|
||||
if (me && isUserRelated(note, userIdsWhoBlockingMe, false)) return false;
|
||||
if (me && isUserRelated(note, userIdsWhoMeMuting, true)) return false;
|
||||
|
||||
// TODO: ミュート等考慮
|
||||
return true;
|
||||
});
|
||||
|
||||
notes.sort((a, b) => a.id > b.id ? -1 : 1);
|
||||
|
||||
return await this.noteEntityService.packMany(notes, me);
|
||||
});
|
||||
|
|
|
@ -44,7 +44,7 @@ export function genOpenapiSpec(config: Config) {
|
|||
|
||||
// 書き換えたりするのでディープコピーしておく。そのまま編集するとメモリ上の値が汚れて次回以降の出力に影響する
|
||||
const copiedEndpoints = JSON.parse(JSON.stringify(endpoints)) as IEndpoint[];
|
||||
for (const endpoint of copiedEndpoints.filter(ep => !ep.meta.secure)) {
|
||||
for (const endpoint of copiedEndpoints) {
|
||||
const errors = {} as any;
|
||||
|
||||
if (endpoint.meta.errors) {
|
||||
|
@ -60,6 +60,11 @@ export function genOpenapiSpec(config: Config) {
|
|||
const resSchema = endpoint.meta.res ? convertSchemaToOpenApiSchema(endpoint.meta.res) : {};
|
||||
|
||||
let desc = (endpoint.meta.description ? endpoint.meta.description : 'No description provided.') + '\n\n';
|
||||
|
||||
if (endpoint.meta.secure) {
|
||||
desc += '**Internal Endpoint**: This endpoint is an API for the cherrypick mainframe and is not intended for use by third parties.\n';
|
||||
}
|
||||
|
||||
desc += `**Credential required**: *${endpoint.meta.requireCredential ? 'Yes' : 'No'}*`;
|
||||
if (endpoint.meta.kind) {
|
||||
const kind = endpoint.meta.kind;
|
||||
|
|
|
@ -146,6 +146,9 @@ type AdminEmojiDeleteBulkRequest = operations['admin/emoji/delete-bulk']['reques
|
|||
// @public (undocumented)
|
||||
type AdminEmojiDeleteRequest = operations['admin/emoji/delete']['requestBody']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type AdminEmojiImportZipRequest = operations['admin/emoji/import-zip']['requestBody']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type AdminEmojiListRemoteRequest = operations['admin/emoji/list-remote']['requestBody']['content']['application/json'];
|
||||
|
||||
|
@ -402,8 +405,6 @@ class APIClient {
|
|||
fetch: FetchLike;
|
||||
// (undocumented)
|
||||
origin: string;
|
||||
// (undocumented)
|
||||
request<E extends keyof Endpoints, P extends Endpoints[E]['req']>(endpoint: E, params?: P, credential?: string | null): Promise<SwitchCaseResponseType<E, P>>;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
|
@ -436,6 +437,9 @@ type ApShowRequest = operations['ap/show']['requestBody']['content']['applicatio
|
|||
// @public (undocumented)
|
||||
type ApShowResponse = operations['ap/show']['responses']['200']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type AuthAcceptRequest = operations['auth/accept']['requestBody']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type AuthSessionGenerateRequest = operations['auth/session/generate']['requestBody']['content']['application/json'];
|
||||
|
||||
|
@ -1121,6 +1125,7 @@ declare namespace entities {
|
|||
AdminEmojiCopyResponse,
|
||||
AdminEmojiDeleteBulkRequest,
|
||||
AdminEmojiDeleteRequest,
|
||||
AdminEmojiImportZipRequest,
|
||||
AdminEmojiListRemoteRequest,
|
||||
AdminEmojiListRemoteResponse,
|
||||
AdminEmojiListRequest,
|
||||
|
@ -1199,6 +1204,7 @@ declare namespace entities {
|
|||
AppCreateResponse,
|
||||
AppShowRequest,
|
||||
AppShowResponse,
|
||||
AuthAcceptRequest,
|
||||
AuthSessionGenerateRequest,
|
||||
AuthSessionGenerateResponse,
|
||||
AuthSessionShowRequest,
|
||||
|
@ -1358,13 +1364,31 @@ declare namespace entities {
|
|||
HashtagsUsersRequest,
|
||||
HashtagsUsersResponse,
|
||||
IResponse,
|
||||
I2faDoneRequest,
|
||||
I2faKeyDoneRequest,
|
||||
I2faPasswordLessRequest,
|
||||
I2faRegisterKeyRequest,
|
||||
I2faRegisterRequest,
|
||||
I2faUpdateKeyRequest,
|
||||
I2faRemoveKeyRequest,
|
||||
I2faUnregisterRequest,
|
||||
IAppsRequest,
|
||||
IAuthorizedAppsRequest,
|
||||
IClaimAchievementRequest,
|
||||
IChangePasswordRequest,
|
||||
IDeleteAccountRequest,
|
||||
IExportFollowingRequest,
|
||||
IFavoritesRequest,
|
||||
IFavoritesResponse,
|
||||
IGalleryLikesRequest,
|
||||
IGalleryLikesResponse,
|
||||
IGalleryPostsRequest,
|
||||
IGalleryPostsResponse,
|
||||
IImportBlockingRequest,
|
||||
IImportFollowingRequest,
|
||||
IImportMutingRequest,
|
||||
IImportUserListsRequest,
|
||||
IImportAntennasRequest,
|
||||
INotificationsRequest,
|
||||
INotificationsResponse,
|
||||
INotificationsGroupedRequest,
|
||||
|
@ -1376,6 +1400,7 @@ declare namespace entities {
|
|||
IPinRequest,
|
||||
IPinResponse,
|
||||
IReadAnnouncementRequest,
|
||||
IRegenerateTokenRequest,
|
||||
IRegistryGetAllRequest,
|
||||
IRegistryGetDetailRequest,
|
||||
IRegistryGetRequest,
|
||||
|
@ -1383,12 +1408,17 @@ declare namespace entities {
|
|||
IRegistryKeysRequest,
|
||||
IRegistryRemoveRequest,
|
||||
IRegistrySetRequest,
|
||||
IRevokeTokenRequest,
|
||||
ISigninHistoryRequest,
|
||||
ISigninHistoryResponse,
|
||||
IUnpinRequest,
|
||||
IUnpinResponse,
|
||||
IUpdateEmailRequest,
|
||||
IUpdateRequest,
|
||||
IUpdateResponse,
|
||||
IUserGroupInvitesRequest,
|
||||
IUserGroupInvitesResponse,
|
||||
IMoveRequest,
|
||||
IWebhooksCreateRequest,
|
||||
IWebhooksShowRequest,
|
||||
IWebhooksUpdateRequest,
|
||||
|
@ -1411,6 +1441,8 @@ declare namespace entities {
|
|||
EmojisResponse,
|
||||
EmojiRequest,
|
||||
EmojiResponse,
|
||||
MiauthGenTokenRequest,
|
||||
MiauthGenTokenResponse,
|
||||
MuteCreateRequest,
|
||||
MuteDeleteRequest,
|
||||
MuteListRequest,
|
||||
|
@ -1476,6 +1508,7 @@ declare namespace entities {
|
|||
NotesUserListTimelineRequest,
|
||||
NotesUserListTimelineResponse,
|
||||
NotificationsCreateRequest,
|
||||
PagePushRequest,
|
||||
PagesCreateRequest,
|
||||
PagesCreateResponse,
|
||||
PagesDeleteRequest,
|
||||
|
@ -1488,6 +1521,8 @@ declare namespace entities {
|
|||
FlashCreateRequest,
|
||||
FlashDeleteRequest,
|
||||
FlashFeaturedResponse,
|
||||
FlashGenTokenRequest,
|
||||
FlashGenTokenResponse,
|
||||
FlashLikeRequest,
|
||||
FlashShowRequest,
|
||||
FlashShowResponse,
|
||||
|
@ -1713,6 +1748,12 @@ type FlashDeleteRequest = operations['flash/delete']['requestBody']['content']['
|
|||
// @public (undocumented)
|
||||
type FlashFeaturedResponse = operations['flash/featured']['responses']['200']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type FlashGenTokenRequest = operations['flash/gen-token']['requestBody']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type FlashGenTokenResponse = operations['flash/gen-token']['responses']['200']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type FlashLikeRequest = operations['flash/like']['requestBody']['content']['application/json'];
|
||||
|
||||
|
@ -1866,12 +1907,51 @@ type HashtagsUsersRequest = operations['hashtags/users']['requestBody']['content
|
|||
// @public (undocumented)
|
||||
type HashtagsUsersResponse = operations['hashtags/users']['responses']['200']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type I2faDoneRequest = operations['i/2fa/done']['requestBody']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type I2faKeyDoneRequest = operations['i/2fa/key-done']['requestBody']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type I2faPasswordLessRequest = operations['i/2fa/password-less']['requestBody']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type I2faRegisterKeyRequest = operations['i/2fa/register-key']['requestBody']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type I2faRegisterRequest = operations['i/2fa/register']['requestBody']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type I2faRemoveKeyRequest = operations['i/2fa/remove-key']['requestBody']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type I2faUnregisterRequest = operations['i/2fa/unregister']['requestBody']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type I2faUpdateKeyRequest = operations['i/2fa/update-key']['requestBody']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type IAppsRequest = operations['i/apps']['requestBody']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type IAuthorizedAppsRequest = operations['i/authorized-apps']['requestBody']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type IChangePasswordRequest = operations['i/change-password']['requestBody']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type IClaimAchievementRequest = operations['i/claim-achievement']['requestBody']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type ID = string;
|
||||
|
||||
// @public (undocumented)
|
||||
type IDeleteAccountRequest = operations['i/delete-account']['requestBody']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type IExportFollowingRequest = operations['i/export-following']['requestBody']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type IFavoritesRequest = operations['i/favorites']['requestBody']['content']['application/json'];
|
||||
|
||||
|
@ -1890,6 +1970,24 @@ type IGalleryPostsRequest = operations['i/gallery/posts']['requestBody']['conten
|
|||
// @public (undocumented)
|
||||
type IGalleryPostsResponse = operations['i/gallery/posts']['responses']['200']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type IImportAntennasRequest = operations['i/import-antennas']['requestBody']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type IImportBlockingRequest = operations['i/import-blocking']['requestBody']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type IImportFollowingRequest = operations['i/import-following']['requestBody']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type IImportMutingRequest = operations['i/import-muting']['requestBody']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type IImportUserListsRequest = operations['i/import-user-lists']['requestBody']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type IMoveRequest = operations['i/move']['requestBody']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type INotificationsGroupedRequest = operations['i/notifications-grouped']['requestBody']['content']['application/json'];
|
||||
|
||||
|
@ -1941,6 +2039,9 @@ type IPinResponse = operations['i/pin']['responses']['200']['content']['applicat
|
|||
// @public (undocumented)
|
||||
type IReadAnnouncementRequest = operations['i/read-announcement']['requestBody']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type IRegenerateTokenRequest = operations['i/regenerate-token']['requestBody']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type IRegistryGetAllRequest = operations['i/registry/get-all']['requestBody']['content']['application/json'];
|
||||
|
||||
|
@ -1965,15 +2066,27 @@ type IRegistrySetRequest = operations['i/registry/set']['requestBody']['content'
|
|||
// @public (undocumented)
|
||||
type IResponse = operations['i']['responses']['200']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type IRevokeTokenRequest = operations['i/revoke-token']['requestBody']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
function isAPIError(reason: any): reason is APIError;
|
||||
|
||||
// @public (undocumented)
|
||||
type ISigninHistoryRequest = operations['i/signin-history']['requestBody']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type ISigninHistoryResponse = operations['i/signin-history']['responses']['200']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type IUnpinRequest = operations['i/unpin']['requestBody']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type IUnpinResponse = operations['i/unpin']['responses']['200']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type IUpdateEmailRequest = operations['i/update-email']['requestBody']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type IUpdateRequest = operations['i/update']['requestBody']['content']['application/json'];
|
||||
|
||||
|
@ -2037,6 +2150,12 @@ type MetaRequest = operations['meta']['requestBody']['content']['application/jso
|
|||
// @public (undocumented)
|
||||
type MetaResponse = operations['meta']['responses']['200']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type MiauthGenTokenRequest = operations['miauth/gen-token']['requestBody']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type MiauthGenTokenResponse = operations['miauth/gen-token']['responses']['200']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type ModerationLog = {
|
||||
id: ID;
|
||||
|
@ -2378,6 +2497,9 @@ type PageEvent = {
|
|||
user: User;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
type PagePushRequest = operations['page-push']['requestBody']['content']['application/json'];
|
||||
|
||||
// @public (undocumented)
|
||||
type PagesCreateRequest = operations['pages/create']['requestBody']['content']['application/json'];
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
"@typescript-eslint/eslint-plugin": "6.11.0",
|
||||
"@typescript-eslint/parser": "6.11.0",
|
||||
"eslint": "8.53.0",
|
||||
"typescript": "5.3.2",
|
||||
"typescript": "5.3.3",
|
||||
"tsx": "4.4.0",
|
||||
"ts-case-convert": "2.0.2",
|
||||
"openapi-types": "12.1.3",
|
||||
|
|
|
@ -160,6 +160,68 @@ async function generateEndpoints(
|
|||
await writeFile(endpointOutputPath, endpointOutputLine.join('\n'));
|
||||
}
|
||||
|
||||
async function generateApiClientJSDoc(
|
||||
openApiDocs: OpenAPIV3.Document,
|
||||
apiClientFileName: string,
|
||||
endpointsFileName: string,
|
||||
warningsOutputPath: string,
|
||||
) {
|
||||
const endpoints: { operationId: string; description: string; }[] = [];
|
||||
|
||||
// cherrypick-jsはPOST固定で送っているので、こちらも決め打ちする。別メソッドに対応することがあればこちらも直す必要あり
|
||||
const paths = openApiDocs.paths;
|
||||
const postPathItems = Object.keys(paths)
|
||||
.map(it => paths[it]?.post)
|
||||
.filter(filterUndefined);
|
||||
|
||||
for (const operation of postPathItems) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const operationId = operation.operationId!;
|
||||
|
||||
if (operation.description) {
|
||||
endpoints.push({
|
||||
operationId: operationId,
|
||||
description: operation.description,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const endpointOutputLine: string[] = [];
|
||||
|
||||
endpointOutputLine.push(generateVersionHeaderComment(openApiDocs));
|
||||
endpointOutputLine.push('');
|
||||
|
||||
endpointOutputLine.push(`import type { SwitchCaseResponseType } from '${toImportPath(apiClientFileName)}';`);
|
||||
endpointOutputLine.push(`import type { Endpoints } from '${toImportPath(endpointsFileName)}';`);
|
||||
endpointOutputLine.push('');
|
||||
|
||||
endpointOutputLine.push(`declare module '${toImportPath(apiClientFileName)}' {`);
|
||||
endpointOutputLine.push(' export interface APIClient {');
|
||||
for (let i = 0; i < endpoints.length; i++) {
|
||||
const endpoint = endpoints[i];
|
||||
|
||||
endpointOutputLine.push(
|
||||
' /**',
|
||||
` * ${endpoint.description.split('\n').join('\n * ')}`,
|
||||
' */',
|
||||
` request<E extends '${endpoint.operationId}', P extends Endpoints[E][\'req\']>(`,
|
||||
' endpoint: E,',
|
||||
' params: P,',
|
||||
' credential?: string | null,',
|
||||
' ): Promise<SwitchCaseResponseType<E, P>>;',
|
||||
);
|
||||
|
||||
if (i < endpoints.length - 1) {
|
||||
endpointOutputLine.push('\n');
|
||||
}
|
||||
}
|
||||
endpointOutputLine.push(' }');
|
||||
endpointOutputLine.push('}');
|
||||
endpointOutputLine.push('');
|
||||
|
||||
await writeFile(warningsOutputPath, endpointOutputLine.join('\n'));
|
||||
}
|
||||
|
||||
function isRequestBodyObject(value: unknown): value is OpenAPIV3.RequestBodyObject {
|
||||
if (!value) {
|
||||
return false;
|
||||
|
@ -280,6 +342,9 @@ async function main() {
|
|||
const entitiesFileName = `${generatePath}/entities.ts`;
|
||||
const endpointFileName = `${generatePath}/endpoint.ts`;
|
||||
await generateEndpoints(openApiDocs, typeFileName, entitiesFileName, endpointFileName);
|
||||
|
||||
const apiClientWarningFileName = `${generatePath}/apiClientJSDoc.ts`;
|
||||
await generateApiClientJSDoc(openApiDocs, '../api.ts', endpointFileName, apiClientWarningFileName);
|
||||
}
|
||||
|
||||
main();
|
||||
|
|
|
@ -21,19 +21,19 @@
|
|||
"url": "git+https://github.com/misskey-dev/misskey.js.git"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@microsoft/api-extractor": "7.38.3",
|
||||
"@microsoft/api-extractor": "7.38.5",
|
||||
"@swc/jest": "0.2.29",
|
||||
"@types/jest": "29.5.10",
|
||||
"@types/node": "20.10.3",
|
||||
"@typescript-eslint/eslint-plugin": "6.13.1",
|
||||
"@typescript-eslint/parser": "6.13.1",
|
||||
"@types/jest": "29.5.11",
|
||||
"@types/node": "20.10.4",
|
||||
"@typescript-eslint/eslint-plugin": "6.13.2",
|
||||
"@typescript-eslint/parser": "6.13.2",
|
||||
"eslint": "8.55.0",
|
||||
"jest": "29.7.0",
|
||||
"jest-fetch-mock": "3.0.3",
|
||||
"jest-websocket-mock": "2.5.0",
|
||||
"mock-socket": "9.3.1",
|
||||
"tsd": "0.29.0",
|
||||
"typescript": "5.3.2",
|
||||
"typescript": "5.3.3",
|
||||
"ncp": "2.0.0"
|
||||
},
|
||||
"files": [
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import './autogen/apiClientJSDoc';
|
||||
|
||||
import { SwitchCaseResponseType } from './api.types';
|
||||
import type { Endpoints } from './api.types';
|
||||
|
||||
|
|
4316
packages/cherrypick-js/src/autogen/apiClientJSDoc.ts
Normal file
4316
packages/cherrypick-js/src/autogen/apiClientJSDoc.ts
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* version: 4.6.0-beta.2
|
||||
* basedMisskeyVersion: 2023.12.0-beta.2
|
||||
* generatedAt: 2023-12-06T10:16:11.711Z
|
||||
* version: 4.6.0-beta.3
|
||||
* basedMisskeyVersion: 2023.12.0-beta.3
|
||||
* generatedAt: 2023-12-13T12:46:52.812Z
|
||||
*/
|
||||
|
||||
import type {
|
||||
|
@ -49,6 +49,7 @@ import type {
|
|||
AdminEmojiCopyResponse,
|
||||
AdminEmojiDeleteBulkRequest,
|
||||
AdminEmojiDeleteRequest,
|
||||
AdminEmojiImportZipRequest,
|
||||
AdminEmojiListRemoteRequest,
|
||||
AdminEmojiListRemoteResponse,
|
||||
AdminEmojiListRequest,
|
||||
|
@ -127,6 +128,7 @@ import type {
|
|||
AppCreateResponse,
|
||||
AppShowRequest,
|
||||
AppShowResponse,
|
||||
AuthAcceptRequest,
|
||||
AuthSessionGenerateRequest,
|
||||
AuthSessionGenerateResponse,
|
||||
AuthSessionShowRequest,
|
||||
|
@ -286,13 +288,31 @@ import type {
|
|||
HashtagsUsersRequest,
|
||||
HashtagsUsersResponse,
|
||||
IResponse,
|
||||
I2faDoneRequest,
|
||||
I2faKeyDoneRequest,
|
||||
I2faPasswordLessRequest,
|
||||
I2faRegisterKeyRequest,
|
||||
I2faRegisterRequest,
|
||||
I2faUpdateKeyRequest,
|
||||
I2faRemoveKeyRequest,
|
||||
I2faUnregisterRequest,
|
||||
IAppsRequest,
|
||||
IAuthorizedAppsRequest,
|
||||
IClaimAchievementRequest,
|
||||
IChangePasswordRequest,
|
||||
IDeleteAccountRequest,
|
||||
IExportFollowingRequest,
|
||||
IFavoritesRequest,
|
||||
IFavoritesResponse,
|
||||
IGalleryLikesRequest,
|
||||
IGalleryLikesResponse,
|
||||
IGalleryPostsRequest,
|
||||
IGalleryPostsResponse,
|
||||
IImportBlockingRequest,
|
||||
IImportFollowingRequest,
|
||||
IImportMutingRequest,
|
||||
IImportUserListsRequest,
|
||||
IImportAntennasRequest,
|
||||
INotificationsRequest,
|
||||
INotificationsResponse,
|
||||
INotificationsGroupedRequest,
|
||||
|
@ -304,6 +324,7 @@ import type {
|
|||
IPinRequest,
|
||||
IPinResponse,
|
||||
IReadAnnouncementRequest,
|
||||
IRegenerateTokenRequest,
|
||||
IRegistryGetAllRequest,
|
||||
IRegistryGetDetailRequest,
|
||||
IRegistryGetRequest,
|
||||
|
@ -311,12 +332,17 @@ import type {
|
|||
IRegistryKeysRequest,
|
||||
IRegistryRemoveRequest,
|
||||
IRegistrySetRequest,
|
||||
IRevokeTokenRequest,
|
||||
ISigninHistoryRequest,
|
||||
ISigninHistoryResponse,
|
||||
IUnpinRequest,
|
||||
IUnpinResponse,
|
||||
IUpdateEmailRequest,
|
||||
IUpdateRequest,
|
||||
IUpdateResponse,
|
||||
IUserGroupInvitesRequest,
|
||||
IUserGroupInvitesResponse,
|
||||
IMoveRequest,
|
||||
IWebhooksCreateRequest,
|
||||
IWebhooksShowRequest,
|
||||
IWebhooksUpdateRequest,
|
||||
|
@ -339,6 +365,8 @@ import type {
|
|||
EmojisResponse,
|
||||
EmojiRequest,
|
||||
EmojiResponse,
|
||||
MiauthGenTokenRequest,
|
||||
MiauthGenTokenResponse,
|
||||
MuteCreateRequest,
|
||||
MuteDeleteRequest,
|
||||
MuteListRequest,
|
||||
|
@ -404,6 +432,7 @@ import type {
|
|||
NotesUserListTimelineRequest,
|
||||
NotesUserListTimelineResponse,
|
||||
NotificationsCreateRequest,
|
||||
PagePushRequest,
|
||||
PagesCreateRequest,
|
||||
PagesCreateResponse,
|
||||
PagesDeleteRequest,
|
||||
|
@ -416,6 +445,8 @@ import type {
|
|||
FlashCreateRequest,
|
||||
FlashDeleteRequest,
|
||||
FlashFeaturedResponse,
|
||||
FlashGenTokenRequest,
|
||||
FlashGenTokenResponse,
|
||||
FlashLikeRequest,
|
||||
FlashShowRequest,
|
||||
FlashShowResponse,
|
||||
|
@ -559,6 +590,7 @@ export type Endpoints = {
|
|||
'admin/emoji/copy': { req: AdminEmojiCopyRequest; res: AdminEmojiCopyResponse };
|
||||
'admin/emoji/delete-bulk': { req: AdminEmojiDeleteBulkRequest; res: EmptyResponse };
|
||||
'admin/emoji/delete': { req: AdminEmojiDeleteRequest; res: EmptyResponse };
|
||||
'admin/emoji/import-zip': { req: AdminEmojiImportZipRequest; res: EmptyResponse };
|
||||
'admin/emoji/list-remote': { req: AdminEmojiListRemoteRequest; res: AdminEmojiListRemoteResponse };
|
||||
'admin/emoji/list': { req: AdminEmojiListRequest; res: AdminEmojiListResponse };
|
||||
'admin/emoji/remove-aliases-bulk': { req: AdminEmojiRemoveAliasesBulkRequest; res: EmptyResponse };
|
||||
|
@ -618,6 +650,7 @@ export type Endpoints = {
|
|||
'ap/show': { req: ApShowRequest; res: ApShowResponse };
|
||||
'app/create': { req: AppCreateRequest; res: AppCreateResponse };
|
||||
'app/show': { req: AppShowRequest; res: AppShowResponse };
|
||||
'auth/accept': { req: AuthAcceptRequest; res: EmptyResponse };
|
||||
'auth/session/generate': { req: AuthSessionGenerateRequest; res: AuthSessionGenerateResponse };
|
||||
'auth/session/show': { req: AuthSessionShowRequest; res: AuthSessionShowResponse };
|
||||
'auth/session/userkey': { req: AuthSessionUserkeyRequest; res: AuthSessionUserkeyResponse };
|
||||
|
@ -681,6 +714,7 @@ export type Endpoints = {
|
|||
'email-address/available': { req: EmailAddressAvailableRequest; res: EmailAddressAvailableResponse };
|
||||
'endpoint': { req: EndpointRequest; res: EmptyResponse };
|
||||
'endpoints': { req: EmptyRequest; res: EndpointsResponse };
|
||||
'export-custom-emojis': { req: EmptyRequest; res: EmptyResponse };
|
||||
'federation/followers': { req: FederationFollowersRequest; res: FederationFollowersResponse };
|
||||
'federation/following': { req: FederationFollowingRequest; res: FederationFollowingResponse };
|
||||
'federation/instances': { req: FederationInstancesRequest; res: FederationInstancesResponse };
|
||||
|
@ -714,10 +748,34 @@ export type Endpoints = {
|
|||
'hashtags/trend': { req: EmptyRequest; res: HashtagsTrendResponse };
|
||||
'hashtags/users': { req: HashtagsUsersRequest; res: HashtagsUsersResponse };
|
||||
'i': { req: EmptyRequest; res: IResponse };
|
||||
'i/2fa/done': { req: I2faDoneRequest; res: EmptyResponse };
|
||||
'i/2fa/key-done': { req: I2faKeyDoneRequest; res: EmptyResponse };
|
||||
'i/2fa/password-less': { req: I2faPasswordLessRequest; res: EmptyResponse };
|
||||
'i/2fa/register-key': { req: I2faRegisterKeyRequest; res: EmptyResponse };
|
||||
'i/2fa/register': { req: I2faRegisterRequest; res: EmptyResponse };
|
||||
'i/2fa/update-key': { req: I2faUpdateKeyRequest; res: EmptyResponse };
|
||||
'i/2fa/remove-key': { req: I2faRemoveKeyRequest; res: EmptyResponse };
|
||||
'i/2fa/unregister': { req: I2faUnregisterRequest; res: EmptyResponse };
|
||||
'i/apps': { req: IAppsRequest; res: EmptyResponse };
|
||||
'i/authorized-apps': { req: IAuthorizedAppsRequest; res: EmptyResponse };
|
||||
'i/claim-achievement': { req: IClaimAchievementRequest; res: EmptyResponse };
|
||||
'i/change-password': { req: IChangePasswordRequest; res: EmptyResponse };
|
||||
'i/delete-account': { req: IDeleteAccountRequest; res: EmptyResponse };
|
||||
'i/export-blocking': { req: EmptyRequest; res: EmptyResponse };
|
||||
'i/export-following': { req: IExportFollowingRequest; res: EmptyResponse };
|
||||
'i/export-mute': { req: EmptyRequest; res: EmptyResponse };
|
||||
'i/export-notes': { req: EmptyRequest; res: EmptyResponse };
|
||||
'i/export-favorites': { req: EmptyRequest; res: EmptyResponse };
|
||||
'i/export-user-lists': { req: EmptyRequest; res: EmptyResponse };
|
||||
'i/export-antennas': { req: EmptyRequest; res: EmptyResponse };
|
||||
'i/favorites': { req: IFavoritesRequest; res: IFavoritesResponse };
|
||||
'i/gallery/likes': { req: IGalleryLikesRequest; res: IGalleryLikesResponse };
|
||||
'i/gallery/posts': { req: IGalleryPostsRequest; res: IGalleryPostsResponse };
|
||||
'i/import-blocking': { req: IImportBlockingRequest; res: EmptyResponse };
|
||||
'i/import-following': { req: IImportFollowingRequest; res: EmptyResponse };
|
||||
'i/import-muting': { req: IImportMutingRequest; res: EmptyResponse };
|
||||
'i/import-user-lists': { req: IImportUserListsRequest; res: EmptyResponse };
|
||||
'i/import-antennas': { req: IImportAntennasRequest; res: EmptyResponse };
|
||||
'i/notifications': { req: INotificationsRequest; res: INotificationsResponse };
|
||||
'i/notifications-grouped': { req: INotificationsGroupedRequest; res: INotificationsGroupedResponse };
|
||||
'i/page-likes': { req: IPageLikesRequest; res: IPageLikesResponse };
|
||||
|
@ -726,16 +784,22 @@ export type Endpoints = {
|
|||
'i/read-all-messaging-messages': { req: EmptyRequest; res: EmptyResponse };
|
||||
'i/read-all-unread-notes': { req: EmptyRequest; res: EmptyResponse };
|
||||
'i/read-announcement': { req: IReadAnnouncementRequest; res: EmptyResponse };
|
||||
'i/regenerate-token': { req: IRegenerateTokenRequest; res: EmptyResponse };
|
||||
'i/registry/get-all': { req: IRegistryGetAllRequest; res: EmptyResponse };
|
||||
'i/registry/get-detail': { req: IRegistryGetDetailRequest; res: EmptyResponse };
|
||||
'i/registry/get': { req: IRegistryGetRequest; res: EmptyResponse };
|
||||
'i/registry/keys-with-type': { req: IRegistryKeysWithTypeRequest; res: EmptyResponse };
|
||||
'i/registry/keys': { req: IRegistryKeysRequest; res: EmptyResponse };
|
||||
'i/registry/remove': { req: IRegistryRemoveRequest; res: EmptyResponse };
|
||||
'i/registry/scopes-with-domain': { req: EmptyRequest; res: EmptyResponse };
|
||||
'i/registry/set': { req: IRegistrySetRequest; res: EmptyResponse };
|
||||
'i/revoke-token': { req: IRevokeTokenRequest; res: EmptyResponse };
|
||||
'i/signin-history': { req: ISigninHistoryRequest; res: ISigninHistoryResponse };
|
||||
'i/unpin': { req: IUnpinRequest; res: IUnpinResponse };
|
||||
'i/update-email': { req: IUpdateEmailRequest; res: EmptyResponse };
|
||||
'i/update': { req: IUpdateRequest; res: IUpdateResponse };
|
||||
'i/user-group-invites': { req: IUserGroupInvitesRequest; res: IUserGroupInvitesResponse };
|
||||
'i/move': { req: IMoveRequest; res: EmptyResponse };
|
||||
'i/webhooks/create': { req: IWebhooksCreateRequest; res: EmptyResponse };
|
||||
'i/webhooks/list': { req: EmptyRequest; res: EmptyResponse };
|
||||
'i/webhooks/show': { req: IWebhooksShowRequest; res: EmptyResponse };
|
||||
|
@ -753,6 +817,7 @@ export type Endpoints = {
|
|||
'meta': { req: MetaRequest; res: MetaResponse };
|
||||
'emojis': { req: EmptyRequest; res: EmojisResponse };
|
||||
'emoji': { req: EmojiRequest; res: EmojiResponse };
|
||||
'miauth/gen-token': { req: MiauthGenTokenRequest; res: MiauthGenTokenResponse };
|
||||
'mute/create': { req: MuteCreateRequest; res: EmptyResponse };
|
||||
'mute/delete': { req: MuteDeleteRequest; res: EmptyResponse };
|
||||
'mute/list': { req: MuteListRequest; res: MuteListResponse };
|
||||
|
@ -795,6 +860,7 @@ export type Endpoints = {
|
|||
'notifications/create': { req: NotificationsCreateRequest; res: EmptyResponse };
|
||||
'notifications/mark-all-as-read': { req: EmptyRequest; res: EmptyResponse };
|
||||
'notifications/test-notification': { req: EmptyRequest; res: EmptyResponse };
|
||||
'page-push': { req: PagePushRequest; res: EmptyResponse };
|
||||
'pages/create': { req: PagesCreateRequest; res: PagesCreateResponse };
|
||||
'pages/delete': { req: PagesDeleteRequest; res: EmptyResponse };
|
||||
'pages/featured': { req: EmptyRequest; res: PagesFeaturedResponse };
|
||||
|
@ -805,6 +871,7 @@ export type Endpoints = {
|
|||
'flash/create': { req: FlashCreateRequest; res: EmptyResponse };
|
||||
'flash/delete': { req: FlashDeleteRequest; res: EmptyResponse };
|
||||
'flash/featured': { req: EmptyRequest; res: FlashFeaturedResponse };
|
||||
'flash/gen-token': { req: FlashGenTokenRequest; res: FlashGenTokenResponse };
|
||||
'flash/like': { req: FlashLikeRequest; res: EmptyResponse };
|
||||
'flash/show': { req: FlashShowRequest; res: FlashShowResponse };
|
||||
'flash/unlike': { req: FlashUnlikeRequest; res: EmptyResponse };
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* version: 4.6.0-beta.2
|
||||
* basedMisskeyVersion: 2023.12.0-beta.2
|
||||
* generatedAt: 2023-12-06T10:16:11.709Z
|
||||
* version: 4.6.0-beta.3
|
||||
* basedMisskeyVersion: 2023.12.0-beta.3
|
||||
* generatedAt: 2023-12-13T12:46:52.811Z
|
||||
*/
|
||||
|
||||
import { operations } from './types.js';
|
||||
|
@ -51,6 +51,7 @@ export type AdminEmojiCopyRequest = operations['admin/emoji/copy']['requestBody'
|
|||
export type AdminEmojiCopyResponse = operations['admin/emoji/copy']['responses']['200']['content']['application/json'];
|
||||
export type AdminEmojiDeleteBulkRequest = operations['admin/emoji/delete-bulk']['requestBody']['content']['application/json'];
|
||||
export type AdminEmojiDeleteRequest = operations['admin/emoji/delete']['requestBody']['content']['application/json'];
|
||||
export type AdminEmojiImportZipRequest = operations['admin/emoji/import-zip']['requestBody']['content']['application/json'];
|
||||
export type AdminEmojiListRemoteRequest = operations['admin/emoji/list-remote']['requestBody']['content']['application/json'];
|
||||
export type AdminEmojiListRemoteResponse = operations['admin/emoji/list-remote']['responses']['200']['content']['application/json'];
|
||||
export type AdminEmojiListRequest = operations['admin/emoji/list']['requestBody']['content']['application/json'];
|
||||
|
@ -129,6 +130,7 @@ export type AppCreateRequest = operations['app/create']['requestBody']['content'
|
|||
export type AppCreateResponse = operations['app/create']['responses']['200']['content']['application/json'];
|
||||
export type AppShowRequest = operations['app/show']['requestBody']['content']['application/json'];
|
||||
export type AppShowResponse = operations['app/show']['responses']['200']['content']['application/json'];
|
||||
export type AuthAcceptRequest = operations['auth/accept']['requestBody']['content']['application/json'];
|
||||
export type AuthSessionGenerateRequest = operations['auth/session/generate']['requestBody']['content']['application/json'];
|
||||
export type AuthSessionGenerateResponse = operations['auth/session/generate']['responses']['200']['content']['application/json'];
|
||||
export type AuthSessionShowRequest = operations['auth/session/show']['requestBody']['content']['application/json'];
|
||||
|
@ -288,13 +290,31 @@ export type HashtagsTrendResponse = operations['hashtags/trend']['responses']['2
|
|||
export type HashtagsUsersRequest = operations['hashtags/users']['requestBody']['content']['application/json'];
|
||||
export type HashtagsUsersResponse = operations['hashtags/users']['responses']['200']['content']['application/json'];
|
||||
export type IResponse = operations['i']['responses']['200']['content']['application/json'];
|
||||
export type I2faDoneRequest = operations['i/2fa/done']['requestBody']['content']['application/json'];
|
||||
export type I2faKeyDoneRequest = operations['i/2fa/key-done']['requestBody']['content']['application/json'];
|
||||
export type I2faPasswordLessRequest = operations['i/2fa/password-less']['requestBody']['content']['application/json'];
|
||||
export type I2faRegisterKeyRequest = operations['i/2fa/register-key']['requestBody']['content']['application/json'];
|
||||
export type I2faRegisterRequest = operations['i/2fa/register']['requestBody']['content']['application/json'];
|
||||
export type I2faUpdateKeyRequest = operations['i/2fa/update-key']['requestBody']['content']['application/json'];
|
||||
export type I2faRemoveKeyRequest = operations['i/2fa/remove-key']['requestBody']['content']['application/json'];
|
||||
export type I2faUnregisterRequest = operations['i/2fa/unregister']['requestBody']['content']['application/json'];
|
||||
export type IAppsRequest = operations['i/apps']['requestBody']['content']['application/json'];
|
||||
export type IAuthorizedAppsRequest = operations['i/authorized-apps']['requestBody']['content']['application/json'];
|
||||
export type IClaimAchievementRequest = operations['i/claim-achievement']['requestBody']['content']['application/json'];
|
||||
export type IChangePasswordRequest = operations['i/change-password']['requestBody']['content']['application/json'];
|
||||
export type IDeleteAccountRequest = operations['i/delete-account']['requestBody']['content']['application/json'];
|
||||
export type IExportFollowingRequest = operations['i/export-following']['requestBody']['content']['application/json'];
|
||||
export type IFavoritesRequest = operations['i/favorites']['requestBody']['content']['application/json'];
|
||||
export type IFavoritesResponse = operations['i/favorites']['responses']['200']['content']['application/json'];
|
||||
export type IGalleryLikesRequest = operations['i/gallery/likes']['requestBody']['content']['application/json'];
|
||||
export type IGalleryLikesResponse = operations['i/gallery/likes']['responses']['200']['content']['application/json'];
|
||||
export type IGalleryPostsRequest = operations['i/gallery/posts']['requestBody']['content']['application/json'];
|
||||
export type IGalleryPostsResponse = operations['i/gallery/posts']['responses']['200']['content']['application/json'];
|
||||
export type IImportBlockingRequest = operations['i/import-blocking']['requestBody']['content']['application/json'];
|
||||
export type IImportFollowingRequest = operations['i/import-following']['requestBody']['content']['application/json'];
|
||||
export type IImportMutingRequest = operations['i/import-muting']['requestBody']['content']['application/json'];
|
||||
export type IImportUserListsRequest = operations['i/import-user-lists']['requestBody']['content']['application/json'];
|
||||
export type IImportAntennasRequest = operations['i/import-antennas']['requestBody']['content']['application/json'];
|
||||
export type INotificationsRequest = operations['i/notifications']['requestBody']['content']['application/json'];
|
||||
export type INotificationsResponse = operations['i/notifications']['responses']['200']['content']['application/json'];
|
||||
export type INotificationsGroupedRequest = operations['i/notifications-grouped']['requestBody']['content']['application/json'];
|
||||
|
@ -306,6 +326,7 @@ export type IPagesResponse = operations['i/pages']['responses']['200']['content'
|
|||
export type IPinRequest = operations['i/pin']['requestBody']['content']['application/json'];
|
||||
export type IPinResponse = operations['i/pin']['responses']['200']['content']['application/json'];
|
||||
export type IReadAnnouncementRequest = operations['i/read-announcement']['requestBody']['content']['application/json'];
|
||||
export type IRegenerateTokenRequest = operations['i/regenerate-token']['requestBody']['content']['application/json'];
|
||||
export type IRegistryGetAllRequest = operations['i/registry/get-all']['requestBody']['content']['application/json'];
|
||||
export type IRegistryGetDetailRequest = operations['i/registry/get-detail']['requestBody']['content']['application/json'];
|
||||
export type IRegistryGetRequest = operations['i/registry/get']['requestBody']['content']['application/json'];
|
||||
|
@ -313,12 +334,17 @@ export type IRegistryKeysWithTypeRequest = operations['i/registry/keys-with-type
|
|||
export type IRegistryKeysRequest = operations['i/registry/keys']['requestBody']['content']['application/json'];
|
||||
export type IRegistryRemoveRequest = operations['i/registry/remove']['requestBody']['content']['application/json'];
|
||||
export type IRegistrySetRequest = operations['i/registry/set']['requestBody']['content']['application/json'];
|
||||
export type IRevokeTokenRequest = operations['i/revoke-token']['requestBody']['content']['application/json'];
|
||||
export type ISigninHistoryRequest = operations['i/signin-history']['requestBody']['content']['application/json'];
|
||||
export type ISigninHistoryResponse = operations['i/signin-history']['responses']['200']['content']['application/json'];
|
||||
export type IUnpinRequest = operations['i/unpin']['requestBody']['content']['application/json'];
|
||||
export type IUnpinResponse = operations['i/unpin']['responses']['200']['content']['application/json'];
|
||||
export type IUpdateEmailRequest = operations['i/update-email']['requestBody']['content']['application/json'];
|
||||
export type IUpdateRequest = operations['i/update']['requestBody']['content']['application/json'];
|
||||
export type IUpdateResponse = operations['i/update']['responses']['200']['content']['application/json'];
|
||||
export type IUserGroupInvitesRequest = operations['i/user-group-invites']['requestBody']['content']['application/json'];
|
||||
export type IUserGroupInvitesResponse = operations['i/user-group-invites']['responses']['200']['content']['application/json'];
|
||||
export type IMoveRequest = operations['i/move']['requestBody']['content']['application/json'];
|
||||
export type IWebhooksCreateRequest = operations['i/webhooks/create']['requestBody']['content']['application/json'];
|
||||
export type IWebhooksShowRequest = operations['i/webhooks/show']['requestBody']['content']['application/json'];
|
||||
export type IWebhooksUpdateRequest = operations['i/webhooks/update']['requestBody']['content']['application/json'];
|
||||
|
@ -341,6 +367,8 @@ export type MetaResponse = operations['meta']['responses']['200']['content']['ap
|
|||
export type EmojisResponse = operations['emojis']['responses']['200']['content']['application/json'];
|
||||
export type EmojiRequest = operations['emoji']['requestBody']['content']['application/json'];
|
||||
export type EmojiResponse = operations['emoji']['responses']['200']['content']['application/json'];
|
||||
export type MiauthGenTokenRequest = operations['miauth/gen-token']['requestBody']['content']['application/json'];
|
||||
export type MiauthGenTokenResponse = operations['miauth/gen-token']['responses']['200']['content']['application/json'];
|
||||
export type MuteCreateRequest = operations['mute/create']['requestBody']['content']['application/json'];
|
||||
export type MuteDeleteRequest = operations['mute/delete']['requestBody']['content']['application/json'];
|
||||
export type MuteListRequest = operations['mute/list']['requestBody']['content']['application/json'];
|
||||
|
@ -406,6 +434,7 @@ export type NotesUnrenoteRequest = operations['notes/unrenote']['requestBody']['
|
|||
export type NotesUserListTimelineRequest = operations['notes/user-list-timeline']['requestBody']['content']['application/json'];
|
||||
export type NotesUserListTimelineResponse = operations['notes/user-list-timeline']['responses']['200']['content']['application/json'];
|
||||
export type NotificationsCreateRequest = operations['notifications/create']['requestBody']['content']['application/json'];
|
||||
export type PagePushRequest = operations['page-push']['requestBody']['content']['application/json'];
|
||||
export type PagesCreateRequest = operations['pages/create']['requestBody']['content']['application/json'];
|
||||
export type PagesCreateResponse = operations['pages/create']['responses']['200']['content']['application/json'];
|
||||
export type PagesDeleteRequest = operations['pages/delete']['requestBody']['content']['application/json'];
|
||||
|
@ -418,6 +447,8 @@ export type PagesUpdateRequest = operations['pages/update']['requestBody']['cont
|
|||
export type FlashCreateRequest = operations['flash/create']['requestBody']['content']['application/json'];
|
||||
export type FlashDeleteRequest = operations['flash/delete']['requestBody']['content']['application/json'];
|
||||
export type FlashFeaturedResponse = operations['flash/featured']['responses']['200']['content']['application/json'];
|
||||
export type FlashGenTokenRequest = operations['flash/gen-token']['requestBody']['content']['application/json'];
|
||||
export type FlashGenTokenResponse = operations['flash/gen-token']['responses']['200']['content']['application/json'];
|
||||
export type FlashLikeRequest = operations['flash/like']['requestBody']['content']['application/json'];
|
||||
export type FlashShowRequest = operations['flash/show']['requestBody']['content']['application/json'];
|
||||
export type FlashShowResponse = operations['flash/show']['responses']['200']['content']['application/json'];
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* version: 4.6.0-beta.2
|
||||
* basedMisskeyVersion: 2023.12.0-beta.2
|
||||
* generatedAt: 2023-12-06T10:16:11.709Z
|
||||
* version: 4.6.0-beta.3
|
||||
* basedMisskeyVersion: 2023.12.0-beta.3
|
||||
* generatedAt: 2023-12-13T12:46:52.810Z
|
||||
*/
|
||||
|
||||
import { components } from './types.js';
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -70,12 +70,6 @@ module.exports = {
|
|||
'require': false,
|
||||
'__dirname': false,
|
||||
|
||||
// Vue
|
||||
'$$': false,
|
||||
'$ref': false,
|
||||
'$shallowRef': false,
|
||||
'$computed': false,
|
||||
|
||||
// CherryPick
|
||||
'_DEV_': false,
|
||||
'_LANGS_': false,
|
||||
|
|
|
@ -180,7 +180,7 @@ import './photoswipe-!~{003}~.js';
|
|||
const _hoisted_1 = createBaseVNode("i", {
|
||||
class: "ti ti-photo"
|
||||
}, null, -1);
|
||||
const _sfc_main = defineComponent({
|
||||
const index_photos = defineComponent({
|
||||
__name: "index.photos",
|
||||
props: {
|
||||
user: {}
|
||||
|
@ -261,7 +261,6 @@ const style0 = {
|
|||
const cssModules = {
|
||||
"$style": style0
|
||||
};
|
||||
const index_photos = _sfc_main;
|
||||
export {index_photos as default};
|
||||
`.slice(1));
|
||||
});
|
||||
|
|
|
@ -13,13 +13,13 @@ function isFalsyIdentifier(identifier: estree.Identifier): boolean {
|
|||
return identifier.name === 'undefined' || identifier.name === 'NaN';
|
||||
}
|
||||
|
||||
function normalizeClassWalker(tree: estree.Node): string | null {
|
||||
function normalizeClassWalker(tree: estree.Node, stack: string | undefined): string | null {
|
||||
if (tree.type === 'Identifier') return isFalsyIdentifier(tree) ? '' : null;
|
||||
if (tree.type === 'Literal') return typeof tree.value === 'string' ? tree.value : '';
|
||||
if (tree.type === 'BinaryExpression') {
|
||||
if (tree.operator !== '+') return null;
|
||||
const left = normalizeClassWalker(tree.left);
|
||||
const right = normalizeClassWalker(tree.right);
|
||||
const left = normalizeClassWalker(tree.left, stack);
|
||||
const right = normalizeClassWalker(tree.right, stack);
|
||||
if (left === null || right === null) return null;
|
||||
return `${left}${right}`;
|
||||
}
|
||||
|
@ -33,15 +33,15 @@ function normalizeClassWalker(tree: estree.Node): string | null {
|
|||
if (tree.type === 'ArrayExpression') {
|
||||
const values = tree.elements.map((treeNode) => {
|
||||
if (treeNode === null) return '';
|
||||
if (treeNode.type === 'SpreadElement') return normalizeClassWalker(treeNode.argument);
|
||||
return normalizeClassWalker(treeNode);
|
||||
if (treeNode.type === 'SpreadElement') return normalizeClassWalker(treeNode.argument, stack);
|
||||
return normalizeClassWalker(treeNode, stack);
|
||||
});
|
||||
if (values.some((x) => x === null)) return null;
|
||||
return values.join(' ');
|
||||
}
|
||||
if (tree.type === 'ObjectExpression') {
|
||||
const values = tree.properties.map((treeNode) => {
|
||||
if (treeNode.type === 'SpreadElement') return normalizeClassWalker(treeNode.argument);
|
||||
if (treeNode.type === 'SpreadElement') return normalizeClassWalker(treeNode.argument, stack);
|
||||
let x = treeNode.value;
|
||||
let inveted = false;
|
||||
while (x.type === 'UnaryExpression' && x.operator === '!') {
|
||||
|
@ -67,18 +67,26 @@ function normalizeClassWalker(tree: estree.Node): string | null {
|
|||
if (values.some((x) => x === null)) return null;
|
||||
return values.join(' ');
|
||||
}
|
||||
console.error(`Unexpected node type: ${tree.type}`);
|
||||
if (
|
||||
tree.type !== 'CallExpression' &&
|
||||
tree.type !== 'ChainExpression' &&
|
||||
tree.type !== 'ConditionalExpression' &&
|
||||
tree.type !== 'LogicalExpression' &&
|
||||
tree.type !== 'MemberExpression') {
|
||||
console.error(stack ? `Unexpected node type: ${tree.type} (in ${stack})` : `Unexpected node type: ${tree.type}`);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function normalizeClass(tree: estree.Node): string | null {
|
||||
const walked = normalizeClassWalker(tree);
|
||||
export function normalizeClass(tree: estree.Node, stack?: string): string | null {
|
||||
const walked = normalizeClassWalker(tree, stack);
|
||||
return walked && walked.replace(/^\s+|\s+(?=\s)|\s+$/g, '');
|
||||
}
|
||||
|
||||
export function unwindCssModuleClassName(ast: estree.Node): void {
|
||||
(walk as typeof estreeWalker.walk)(ast, {
|
||||
enter(node, parent): void {
|
||||
//#region
|
||||
if (parent?.type !== 'Program') return;
|
||||
if (node.type !== 'VariableDeclaration') return;
|
||||
if (node.declarations.length !== 1) return;
|
||||
|
@ -102,6 +110,14 @@ export function unwindCssModuleClassName(ast: estree.Node): void {
|
|||
return true;
|
||||
});
|
||||
if (!~__cssModulesIndex) return;
|
||||
/* This region assumeed that the entered node looks like the following code.
|
||||
*
|
||||
* ```ts
|
||||
* const SomeComponent = _export_sfc(_sfc_main, [["foo", bar], ["__cssModules", cssModules]]);
|
||||
* ```
|
||||
*/
|
||||
//#endregion
|
||||
//#region
|
||||
const cssModuleForestName = ((node.declarations[0].init.arguments[1].elements[__cssModulesIndex] as estree.ArrayExpression).elements[1] as estree.Identifier).name;
|
||||
const cssModuleForestNode = parent.body.find((x) => {
|
||||
if (x.type !== 'VariableDeclaration') return false;
|
||||
|
@ -117,6 +133,16 @@ export function unwindCssModuleClassName(ast: estree.Node): void {
|
|||
if (property.value.type !== 'Identifier') return [];
|
||||
return [[property.key.value as string, property.value.name as string]];
|
||||
}));
|
||||
/* This region collected a VariableDeclaration node in the module that looks like the following code.
|
||||
*
|
||||
* ```ts
|
||||
* const cssModules = {
|
||||
* "$style": style0,
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
//#endregion
|
||||
//#region
|
||||
const sfcMain = parent.body.find((x) => {
|
||||
if (x.type !== 'VariableDeclaration') return false;
|
||||
if (x.declarations.length !== 1) return false;
|
||||
|
@ -146,7 +172,22 @@ export function unwindCssModuleClassName(ast: estree.Node): void {
|
|||
if (ctx.type !== 'Identifier') return;
|
||||
if (ctx.name !== '_ctx') return;
|
||||
if (render.argument.body.type !== 'BlockStatement') return;
|
||||
/* This region assumed that `sfcMain` looks like the following code.
|
||||
*
|
||||
* ```ts
|
||||
* const _sfc_main = defineComponent({
|
||||
* setup(_props) {
|
||||
* ...
|
||||
* return (_ctx, _cache) => {
|
||||
* ...
|
||||
* };
|
||||
* },
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
//#endregion
|
||||
for (const [key, value] of moduleForest) {
|
||||
//#region
|
||||
const cssModuleTreeNode = parent.body.find((x) => {
|
||||
if (x.type !== 'VariableDeclaration') return false;
|
||||
if (x.declarations.length !== 1) return false;
|
||||
|
@ -172,6 +213,19 @@ export function unwindCssModuleClassName(ast: estree.Node): void {
|
|||
if (actualValue.declarations[0].init?.type !== 'Literal') return [];
|
||||
return [[actualKey, actualValue.declarations[0].init.value as string]];
|
||||
}));
|
||||
/* This region collected VariableDeclaration nodes in the module that looks like the following code.
|
||||
*
|
||||
* ```ts
|
||||
* const foo = "bar";
|
||||
* const baz = "qux";
|
||||
* const style0 = {
|
||||
* foo: foo,
|
||||
* baz: baz,
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
//#endregion
|
||||
//#region
|
||||
(walk as typeof estreeWalker.walk)(render.argument.body, {
|
||||
enter(childNode) {
|
||||
if (childNode.type !== 'MemberExpression') return;
|
||||
|
@ -189,6 +243,39 @@ export function unwindCssModuleClassName(ast: estree.Node): void {
|
|||
});
|
||||
},
|
||||
});
|
||||
/* This region inlined the reference identifier of the class name in the render function into the actual literal, as in the following code.
|
||||
*
|
||||
* ```ts
|
||||
* const _sfc_main = defineComponent({
|
||||
* setup(_props) {
|
||||
* ...
|
||||
* return (_ctx, _cache) => {
|
||||
* ...
|
||||
* return openBlock(), createElementBlock("div", {
|
||||
* class: normalizeClass(_ctx.$style.foo),
|
||||
* }, null);
|
||||
* };
|
||||
* },
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* ↓
|
||||
*
|
||||
* ```ts
|
||||
* const _sfc_main = defineComponent({
|
||||
* setup(_props) {
|
||||
* ...
|
||||
* return (_ctx, _cache) => {
|
||||
* ...
|
||||
* return openBlock(), createElementBlock("div", {
|
||||
* class: normalizeClass("bar"),
|
||||
* }, null);
|
||||
* };
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
//#endregion
|
||||
//#region
|
||||
(walk as typeof estreeWalker.walk)(render.argument.body, {
|
||||
enter(childNode) {
|
||||
if (childNode.type !== 'MemberExpression') return;
|
||||
|
@ -205,13 +292,47 @@ export function unwindCssModuleClassName(ast: estree.Node): void {
|
|||
});
|
||||
},
|
||||
});
|
||||
/* This region replaced the reference identifier of missing class names in the render function with `undefined`, as in the following code.
|
||||
*
|
||||
* ```ts
|
||||
* const _sfc_main = defineComponent({
|
||||
* setup(_props) {
|
||||
* ...
|
||||
* return (_ctx, _cache) => {
|
||||
* ...
|
||||
* return openBlock(), createElementBlock("div", {
|
||||
* class: normalizeClass(_ctx.$style.hoge),
|
||||
* }, null);
|
||||
* };
|
||||
* },
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* ↓
|
||||
*
|
||||
* ```ts
|
||||
* const _sfc_main = defineComponent({
|
||||
* setup(_props) {
|
||||
* ...
|
||||
* return (_ctx, _cache) => {
|
||||
* ...
|
||||
* return openBlock(), createElementBlock("div", {
|
||||
* class: normalizeClass(undefined),
|
||||
* }, null);
|
||||
* };
|
||||
* },
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
//#endregion
|
||||
//#region
|
||||
(walk as typeof estreeWalker.walk)(render.argument.body, {
|
||||
enter(childNode) {
|
||||
if (childNode.type !== 'CallExpression') return;
|
||||
if (childNode.callee.type !== 'Identifier') return;
|
||||
if (childNode.callee.name !== 'normalizeClass') return;
|
||||
if (childNode.arguments.length !== 1) return;
|
||||
const normalized = normalizeClass(childNode.arguments[0]);
|
||||
const normalized = normalizeClass(childNode.arguments[0], name);
|
||||
if (normalized === null) return;
|
||||
this.replace({
|
||||
type: 'Literal',
|
||||
|
@ -219,8 +340,60 @@ export function unwindCssModuleClassName(ast: estree.Node): void {
|
|||
});
|
||||
},
|
||||
});
|
||||
/* This region compiled the `normalizeClass` call into a pseudo-AOT compilation, as in the following code.
|
||||
*
|
||||
* ```ts
|
||||
* const _sfc_main = defineComponent({
|
||||
* setup(_props) {
|
||||
* ...
|
||||
* return (_ctx, _cache) => {
|
||||
* ...
|
||||
* return openBlock(), createElementBlock("div", {
|
||||
* class: normalizeClass("bar"),
|
||||
* }, null);
|
||||
* };
|
||||
* },
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* ↓
|
||||
*
|
||||
* ```ts
|
||||
* const _sfc_main = defineComponent({
|
||||
* setup(_props) {
|
||||
* ...
|
||||
* return (_ctx, _cache) => {
|
||||
* ...
|
||||
* return openBlock(), createElementBlock("div", {
|
||||
* class: "bar",
|
||||
* }, null);
|
||||
* };
|
||||
* },
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
//#endregion
|
||||
}
|
||||
//#region
|
||||
if (node.declarations[0].init.arguments[1].elements.length === 1) {
|
||||
(walk as typeof estreeWalker.walk)(ast, {
|
||||
enter(childNode) {
|
||||
if (childNode.type !== 'Identifier') return;
|
||||
if (childNode.name !== ident) return;
|
||||
this.replace({
|
||||
type: 'Identifier',
|
||||
name: node.declarations[0].id.name,
|
||||
});
|
||||
},
|
||||
});
|
||||
this.remove();
|
||||
/* NOTE: The above logic is valid as long as the following two conditions are met.
|
||||
*
|
||||
* - the uniqueness of `ident` is kept throughout the module
|
||||
* - `_export_sfc` is noop when the second argument is an empty array
|
||||
*
|
||||
* Otherwise, the below logic should be used instead.
|
||||
|
||||
this.replace({
|
||||
type: 'VariableDeclaration',
|
||||
declarations: [{
|
||||
|
@ -236,6 +409,7 @@ export function unwindCssModuleClassName(ast: estree.Node): void {
|
|||
}],
|
||||
kind: 'const',
|
||||
});
|
||||
*/
|
||||
} else {
|
||||
this.replace({
|
||||
type: 'VariableDeclaration',
|
||||
|
@ -263,6 +437,35 @@ export function unwindCssModuleClassName(ast: estree.Node): void {
|
|||
kind: 'const',
|
||||
});
|
||||
}
|
||||
/* This region removed the `__cssModules` reference from the second argument of `_export_sfc`, as in the following code.
|
||||
*
|
||||
* ```ts
|
||||
* const SomeComponent = _export_sfc(_sfc_main, [["foo", bar], ["__cssModules", cssModules]]);
|
||||
* ```
|
||||
*
|
||||
* ↓
|
||||
*
|
||||
* ```ts
|
||||
* const SomeComponent = _export_sfc(_sfc_main, [["foo", bar]]);
|
||||
* ```
|
||||
*
|
||||
* When the declaration becomes noop, it is removed as follows.
|
||||
*
|
||||
* ```ts
|
||||
* const _sfc_main = defineComponent({
|
||||
* ...
|
||||
* });
|
||||
* const SomeComponent = _export_sfc(_sfc_main, []);
|
||||
* ```
|
||||
*
|
||||
* ↓
|
||||
*
|
||||
* ```ts
|
||||
* const SomeComponent = defineComponent({
|
||||
* ...
|
||||
* });
|
||||
*/
|
||||
//#endregion
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
"type": "module",
|
||||
"scripts": {
|
||||
"watch": "vite",
|
||||
"dev": "vite --config vite.config.local-dev.ts",
|
||||
"build": "vite build",
|
||||
"storybook-dev": "nodemon --verbose --watch src --ext \"mdx,ts,vue\" --ignore \"*.stories.ts\" --exec \"pnpm build-storybook-pre && pnpm exec storybook dev -p 6006 --ci\"",
|
||||
"build-storybook-pre": "(tsc -p .storybook || echo done.) && node .storybook/generate.js && node .storybook/preload-locale.js && node .storybook/preload-theme.js",
|
||||
|
@ -22,11 +23,10 @@
|
|||
"@rollup/plugin-alias": "5.1.0",
|
||||
"@rollup/plugin-json": "6.0.1",
|
||||
"@rollup/plugin-replace": "5.0.5",
|
||||
"@rollup/pluginutils": "5.0.5",
|
||||
"@rollup/pluginutils": "5.1.0",
|
||||
"@syuilo/aiscript": "0.16.0",
|
||||
"@tabler/icons-webfont": "2.37.0",
|
||||
"@vitejs/plugin-vue": "4.5.1",
|
||||
"@vue-macros/reactivity-transform": "0.4.0",
|
||||
"@vue/compiler-sfc": "3.3.9",
|
||||
"astring": "1.8.6",
|
||||
"autosize": "6.0.1",
|
||||
|
@ -35,14 +35,14 @@
|
|||
"browser-image-resizer": "github:misskey-dev/browser-image-resizer#v2.2.1-misskey.3",
|
||||
"buraha": "0.0.1",
|
||||
"canvas-confetti": "1.6.1",
|
||||
"chart.js": "4.4.0",
|
||||
"chart.js": "4.4.1",
|
||||
"chartjs-adapter-date-fns": "3.0.0",
|
||||
"chartjs-chart-matrix": "2.0.1",
|
||||
"chartjs-plugin-gradient": "0.6.1",
|
||||
"chartjs-plugin-zoom": "2.0.1",
|
||||
"cherrypick-js": "workspace:*",
|
||||
"cherrypick-mfm-js": "github:kokonect-link/mfm.js",
|
||||
"chromatic": "9.1.0",
|
||||
"chromatic": "10.1.0",
|
||||
"compare-versions": "6.1.0",
|
||||
"cropperjs": "2.0.0-beta.4",
|
||||
"date-fns": "2.30.0",
|
||||
|
@ -61,9 +61,9 @@
|
|||
"prismjs": "1.29.0",
|
||||
"punycode": "2.3.1",
|
||||
"querystring": "0.2.1",
|
||||
"rollup": "4.6.1",
|
||||
"rollup": "4.7.0",
|
||||
"sanitize-html": "2.11.0",
|
||||
"shiki": "^0.14.5",
|
||||
"shiki": "0.14.6",
|
||||
"sass": "1.69.5",
|
||||
"strict-event-emitter-types": "2.0.0",
|
||||
"temml": "0.10.13",
|
||||
|
@ -75,41 +75,41 @@
|
|||
"tsc-alias": "1.8.8",
|
||||
"tsconfig-paths": "4.2.0",
|
||||
"twemoji-parser": "14.0.0",
|
||||
"typescript": "5.3.2",
|
||||
"typescript": "5.3.3",
|
||||
"uuid": "9.0.1",
|
||||
"v-code-diff": "1.7.2",
|
||||
"vanilla-tilt": "1.8.1",
|
||||
"vite": "5.0.5",
|
||||
"vue": "3.3.9",
|
||||
"vite": "5.0.7",
|
||||
"vue": "3.3.11",
|
||||
"vue-prism-editor": "2.0.0-alpha.2",
|
||||
"vuedraggable": "next"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@storybook/addon-actions": "7.6.3",
|
||||
"@storybook/addon-essentials": "7.6.3",
|
||||
"@storybook/addon-interactions": "7.6.3",
|
||||
"@storybook/addon-links": "7.6.3",
|
||||
"@storybook/addon-storysource": "7.6.3",
|
||||
"@storybook/addons": "7.6.3",
|
||||
"@storybook/blocks": "7.6.3",
|
||||
"@storybook/core-events": "7.6.3",
|
||||
"@storybook/addon-actions": "7.6.4",
|
||||
"@storybook/addon-essentials": "7.6.4",
|
||||
"@storybook/addon-interactions": "7.6.4",
|
||||
"@storybook/addon-links": "7.6.4",
|
||||
"@storybook/addon-storysource": "7.6.4",
|
||||
"@storybook/addons": "7.6.4",
|
||||
"@storybook/blocks": "7.6.4",
|
||||
"@storybook/core-events": "7.6.4",
|
||||
"@storybook/jest": "0.2.3",
|
||||
"@storybook/manager-api": "7.6.3",
|
||||
"@storybook/preview-api": "7.6.3",
|
||||
"@storybook/react": "7.6.3",
|
||||
"@storybook/react-vite": "7.6.3",
|
||||
"@storybook/manager-api": "7.6.4",
|
||||
"@storybook/preview-api": "7.6.4",
|
||||
"@storybook/react": "7.6.4",
|
||||
"@storybook/react-vite": "7.6.4",
|
||||
"@storybook/testing-library": "0.2.2",
|
||||
"@storybook/theming": "7.6.3",
|
||||
"@storybook/types": "7.6.3",
|
||||
"@storybook/vue3": "7.6.3",
|
||||
"@storybook/vue3-vite": "7.6.3",
|
||||
"@storybook/theming": "7.6.4",
|
||||
"@storybook/types": "7.6.4",
|
||||
"@storybook/vue3": "7.6.4",
|
||||
"@storybook/vue3-vite": "7.6.4",
|
||||
"@testing-library/vue": "8.0.1",
|
||||
"@types/autosize": "^4.0.1",
|
||||
"@types/escape-regexp": "0.0.3",
|
||||
"@types/estree": "1.0.5",
|
||||
"@types/matter-js": "0.19.5",
|
||||
"@types/micromatch": "4.0.6",
|
||||
"@types/node": "20.10.3",
|
||||
"@types/node": "20.10.4",
|
||||
"@types/prismjs": "^1.26.0",
|
||||
"@types/punycode": "2.1.3",
|
||||
"@types/sanitize-html": "2.9.5",
|
||||
|
@ -118,13 +118,13 @@
|
|||
"@types/uuid": "9.0.7",
|
||||
"@types/websocket": "1.0.10",
|
||||
"@types/ws": "8.5.10",
|
||||
"@typescript-eslint/eslint-plugin": "6.13.1",
|
||||
"@typescript-eslint/parser": "6.13.1",
|
||||
"@typescript-eslint/eslint-plugin": "6.13.2",
|
||||
"@typescript-eslint/parser": "6.13.2",
|
||||
"@vitest/coverage-v8": "0.34.6",
|
||||
"@vue/runtime-core": "3.3.9",
|
||||
"@vue/runtime-core": "3.3.11",
|
||||
"acorn": "8.11.2",
|
||||
"cross-env": "7.0.3",
|
||||
"cypress": "13.6.0",
|
||||
"cypress": "13.6.1",
|
||||
"eslint": "8.55.0",
|
||||
"eslint-plugin-import": "2.29.0",
|
||||
"eslint-plugin-storybook": "^0.6.13",
|
||||
|
@ -139,13 +139,13 @@
|
|||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"start-server-and-test": "2.0.3",
|
||||
"storybook": "7.6.3",
|
||||
"storybook": "7.6.4",
|
||||
"storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme",
|
||||
"summaly": "github:misskey-dev/summaly",
|
||||
"vite-plugin-turbosnap": "1.0.3",
|
||||
"vitest": "0.34.6",
|
||||
"vitest-fetch-mock": "0.2.2",
|
||||
"vue-eslint-parser": "9.3.2",
|
||||
"vue-tsc": "1.8.24"
|
||||
"vue-tsc": "1.8.25"
|
||||
}
|
||||
}
|
||||
|
|
11
packages/frontend/src/_dev_boot_.ts
Normal file
11
packages/frontend/src/_dev_boot_.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and other misskey, cherrypick contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
// devモードで起動される際(index.htmlを使うとき)はrouterが暴発してしまってうまく読み込めない。
|
||||
// よって、devモードとして起動されるときはビルド時に組み込む形としておく。
|
||||
// (pnpm start時はpugファイルの中で静的リソースとして読み込むようになっており、この問題は起こっていない)
|
||||
import '@tabler/icons-webfont/tabler-icons.scss';
|
||||
|
||||
import('@/_boot_.js');
|
|
@ -323,7 +323,7 @@ export async function openAccountMenu(opts: {
|
|||
text: i18n.ts.profile,
|
||||
to: `/@${ $i.username }`,
|
||||
avatar: $i,
|
||||
}, null, ...(opts.includeCurrentAccount ? [createItem($i)] : []), ...accountItemPromises, {
|
||||
}, { type: 'divider' }, ...(opts.includeCurrentAccount ? [createItem($i)] : []), ...accountItemPromises, {
|
||||
type: 'parent' as const,
|
||||
icon: 'ti ti-plus',
|
||||
text: i18n.ts.addAccount,
|
||||
|
|
|
@ -201,6 +201,12 @@ export async function common(createVue: () => App<Element>) {
|
|||
if (instance.defaultLightTheme != null) ColdDeviceStorage.set('lightTheme', JSON.parse(instance.defaultLightTheme));
|
||||
if (instance.defaultDarkTheme != null) ColdDeviceStorage.set('darkTheme', JSON.parse(instance.defaultDarkTheme));
|
||||
defaultStore.set('themeInitial', false);
|
||||
} else {
|
||||
if (defaultStore.state.darkMode) {
|
||||
applyTheme(darkTheme.value);
|
||||
} else {
|
||||
applyTheme(lightTheme.value);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import MkSwitch from '@/components/MkSwitch.vue';
|
||||
import MkKeyValue from '@/components/MkKeyValue.vue';
|
||||
|
@ -56,11 +57,11 @@ const emit = defineEmits<{
|
|||
(ev: 'resolved', reportId: string): void;
|
||||
}>();
|
||||
|
||||
let forward = $ref(props.report.forwarded);
|
||||
const forward = ref(props.report.forwarded);
|
||||
|
||||
function resolve() {
|
||||
os.apiWithDialog('admin/resolve-abuse-user-report', {
|
||||
forward: forward,
|
||||
forward: forward.value,
|
||||
reportId: props.report.id,
|
||||
}).then(() => {
|
||||
emit('resolved', props.report.id);
|
||||
|
|
|
@ -40,7 +40,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { computed, watch } from 'vue';
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { PrismEditor } from 'vue-prism-editor';
|
||||
import { highlight, languages } from 'prismjs/components/prism-core';
|
||||
import MkInput from '@/components/MkInput.vue';
|
||||
|
@ -75,7 +75,7 @@ const props = defineProps<{
|
|||
previousExpiresAt?: string;
|
||||
}
|
||||
}>();
|
||||
let expirationDate: Date | null = $ref(null);
|
||||
const expirationDate = ref<Date | null>(null);
|
||||
|
||||
type NonNullType<T> = {
|
||||
[P in keyof T]: NonNullable<T[P]>
|
||||
|
@ -118,9 +118,9 @@ function highlighter(code) {
|
|||
|
||||
function renderExpirationDate(empty = false) {
|
||||
if (value.value.expirationDate && !empty) {
|
||||
expirationDate = new Date(value.value.expirationDate);
|
||||
expirationDate.value = new Date(value.value.expirationDate);
|
||||
} else {
|
||||
expirationDate = null;
|
||||
expirationDate.value = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
|
||||
<script lang="ts" setup>
|
||||
import * as Misskey from 'cherrypick-js';
|
||||
import { onMounted } from 'vue';
|
||||
import { onMounted, ref, computed } from 'vue';
|
||||
import * as os from '@/os.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { ACHIEVEMENT_TYPES, ACHIEVEMENT_BADGES, claimAchievement } from '@/scripts/achievements.js';
|
||||
|
@ -67,15 +67,15 @@ const props = withDefaults(defineProps<{
|
|||
withDescription: true,
|
||||
});
|
||||
|
||||
let achievements = $ref();
|
||||
const lockedAchievements = $computed(() => ACHIEVEMENT_TYPES.filter(x => !(achievements ?? []).some(a => a.name === x)));
|
||||
const achievements = ref();
|
||||
const lockedAchievements = computed(() => ACHIEVEMENT_TYPES.filter(x => !(achievements.value ?? []).some(a => a.name === x)));
|
||||
|
||||
function fetch() {
|
||||
os.api('users/achievements', { userId: props.user.id }).then(res => {
|
||||
achievements = [];
|
||||
achievements.value = [];
|
||||
for (const t of ACHIEVEMENT_TYPES) {
|
||||
const a = res.find(x => x.name === t);
|
||||
if (a) achievements.push(a);
|
||||
if (a) achievements.value.push(a);
|
||||
}
|
||||
//achievements = res.sort((a, b) => b.unlockedAt - a.unlockedAt);
|
||||
});
|
||||
|
|
|
@ -138,45 +138,45 @@ const texts = computed(() => {
|
|||
});
|
||||
|
||||
let enabled = true;
|
||||
let majorGraduationColor = $ref<string>();
|
||||
//let minorGraduationColor = $ref<string>();
|
||||
let sHandColor = $ref<string>();
|
||||
let mHandColor = $ref<string>();
|
||||
let hHandColor = $ref<string>();
|
||||
let nowColor = $ref<string>();
|
||||
let h = $ref<number>(0);
|
||||
let m = $ref<number>(0);
|
||||
let s = $ref<number>(0);
|
||||
let hAngle = $ref<number>(0);
|
||||
let mAngle = $ref<number>(0);
|
||||
let sAngle = $ref<number>(0);
|
||||
let disableSAnimate = $ref(false);
|
||||
const majorGraduationColor = ref<string>();
|
||||
// const minorGraduationColor = ref<string>();
|
||||
const sHandColor = ref<string>();
|
||||
const mHandColor = ref<string>();
|
||||
const hHandColor = ref<string>();
|
||||
const nowColor = ref<string>();
|
||||
const h = ref<number>(0);
|
||||
const m = ref<number>(0);
|
||||
const s = ref<number>(0);
|
||||
const hAngle = ref<number>(0);
|
||||
const mAngle = ref<number>(0);
|
||||
const sAngle = ref<number>(0);
|
||||
const disableSAnimate = ref(false);
|
||||
let sOneRound = false;
|
||||
const sLine = ref<SVGPathElement>();
|
||||
|
||||
function tick() {
|
||||
const now = props.now();
|
||||
now.setMinutes(now.getMinutes() + now.getTimezoneOffset() + props.offset);
|
||||
const previousS = s;
|
||||
const previousM = m;
|
||||
const previousH = h;
|
||||
s = now.getSeconds();
|
||||
m = now.getMinutes();
|
||||
h = now.getHours();
|
||||
if (previousS === s && previousM === m && previousH === h) {
|
||||
const previousS = s.value;
|
||||
const previousM = m.value;
|
||||
const previousH = h.value;
|
||||
s.value = now.getSeconds();
|
||||
m.value = now.getMinutes();
|
||||
h.value = now.getHours();
|
||||
if (previousS === s.value && previousM === m.value && previousH === h.value) {
|
||||
return;
|
||||
}
|
||||
hAngle = Math.PI * (h % (props.twentyfour ? 24 : 12) + (m + s / 60) / 60) / (props.twentyfour ? 12 : 6);
|
||||
mAngle = Math.PI * (m + s / 60) / 30;
|
||||
hAngle.value = Math.PI * (h.value % (props.twentyfour ? 24 : 12) + (m.value + s.value / 60) / 60) / (props.twentyfour ? 12 : 6);
|
||||
mAngle.value = Math.PI * (m.value + s.value / 60) / 30;
|
||||
if (sOneRound && sLine.value) { // 秒針が一周した際のアニメーションをよしなに処理する(これが無いと秒が59->0になったときに期待したアニメーションにならない)
|
||||
sAngle = Math.PI * 60 / 30;
|
||||
sAngle.value = Math.PI * 60 / 30;
|
||||
defaultIdlingRenderScheduler.delete(tick);
|
||||
sLine.value.addEventListener('transitionend', () => {
|
||||
disableSAnimate = true;
|
||||
disableSAnimate.value = true;
|
||||
requestAnimationFrame(() => {
|
||||
sAngle = 0;
|
||||
sAngle.value = 0;
|
||||
requestAnimationFrame(() => {
|
||||
disableSAnimate = false;
|
||||
disableSAnimate.value = false;
|
||||
if (enabled) {
|
||||
defaultIdlingRenderScheduler.add(tick);
|
||||
}
|
||||
|
@ -184,9 +184,9 @@ function tick() {
|
|||
});
|
||||
}, { once: true });
|
||||
} else {
|
||||
sAngle = Math.PI * s / 30;
|
||||
sAngle.value = Math.PI * s.value / 30;
|
||||
}
|
||||
sOneRound = s === 59;
|
||||
sOneRound = s.value === 59;
|
||||
}
|
||||
|
||||
tick();
|
||||
|
@ -195,12 +195,12 @@ function calcColors() {
|
|||
const computedStyle = getComputedStyle(document.documentElement);
|
||||
const dark = tinycolor(computedStyle.getPropertyValue('--bg')).isDark();
|
||||
const accent = tinycolor(computedStyle.getPropertyValue('--accent')).toHexString();
|
||||
majorGraduationColor = dark ? 'rgba(255, 255, 255, 0.3)' : 'rgba(0, 0, 0, 0.3)';
|
||||
majorGraduationColor.value = dark ? 'rgba(255, 255, 255, 0.3)' : 'rgba(0, 0, 0, 0.3)';
|
||||
//minorGraduationColor = dark ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)';
|
||||
sHandColor = dark ? 'rgba(255, 255, 255, 0.5)' : 'rgba(0, 0, 0, 0.3)';
|
||||
mHandColor = tinycolor(computedStyle.getPropertyValue('--fg')).toHexString();
|
||||
hHandColor = accent;
|
||||
nowColor = accent;
|
||||
sHandColor.value = dark ? 'rgba(255, 255, 255, 0.5)' : 'rgba(0, 0, 0, 0.3)';
|
||||
mHandColor.value = tinycolor(computedStyle.getPropertyValue('--fg')).toHexString();
|
||||
hHandColor.value = accent;
|
||||
nowColor.value = accent;
|
||||
}
|
||||
|
||||
calcColors();
|
||||
|
|
|
@ -21,8 +21,9 @@ const props = withDefaults(defineProps<{
|
|||
focus: 1.0,
|
||||
});
|
||||
|
||||
function loadShader(gl, type, source) {
|
||||
function loadShader(gl: WebGLRenderingContext, type: number, source: string) {
|
||||
const shader = gl.createShader(type);
|
||||
if (shader == null) return null;
|
||||
|
||||
gl.shaderSource(shader, source);
|
||||
gl.compileShader(shader);
|
||||
|
@ -38,11 +39,13 @@ function loadShader(gl, type, source) {
|
|||
return shader;
|
||||
}
|
||||
|
||||
function initShaderProgram(gl, vsSource, fsSource) {
|
||||
function initShaderProgram(gl: WebGLRenderingContext, vsSource: string, fsSource: string) {
|
||||
const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource);
|
||||
const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource);
|
||||
|
||||
const shaderProgram = gl.createProgram();
|
||||
if (shaderProgram == null || vertexShader == null || fragmentShader == null) return null;
|
||||
|
||||
gl.attachShader(shaderProgram, vertexShader);
|
||||
gl.attachShader(shaderProgram, fragmentShader);
|
||||
gl.linkProgram(shaderProgram);
|
||||
|
@ -63,8 +66,10 @@ let handle: ReturnType<typeof window['requestAnimationFrame']> | null = null;
|
|||
|
||||
onMounted(() => {
|
||||
const canvas = canvasEl.value!;
|
||||
canvas.width = canvas.offsetWidth;
|
||||
canvas.height = canvas.offsetHeight;
|
||||
let width = canvas.offsetWidth;
|
||||
let height = canvas.offsetHeight;
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
|
||||
const gl = canvas.getContext('webgl', { premultipliedAlpha: true });
|
||||
if (gl == null) return;
|
||||
|
@ -197,6 +202,7 @@ onMounted(() => {
|
|||
gl_FragColor = vec4( color, max(max(color.x, color.y), color.z) );
|
||||
}
|
||||
`);
|
||||
if (shaderProgram == null) return;
|
||||
|
||||
gl.useProgram(shaderProgram);
|
||||
const u_resolution = gl.getUniformLocation(shaderProgram, 'u_resolution');
|
||||
|
@ -226,7 +232,23 @@ onMounted(() => {
|
|||
gl!.uniform1f(u_time, 0);
|
||||
gl!.drawArrays(gl!.TRIANGLE_STRIP, 0, 4);
|
||||
} else {
|
||||
function render(timeStamp) {
|
||||
function render(timeStamp: number) {
|
||||
let sizeChanged = false;
|
||||
if (Math.abs(height - canvas.offsetHeight) > 2) {
|
||||
height = canvas.offsetHeight;
|
||||
canvas.height = height;
|
||||
sizeChanged = true;
|
||||
}
|
||||
if (Math.abs(width - canvas.offsetWidth) > 2) {
|
||||
width = canvas.offsetWidth;
|
||||
canvas.width = width;
|
||||
sizeChanged = true;
|
||||
}
|
||||
if (sizeChanged && gl) {
|
||||
gl.uniform2fv(u_resolution, [width, height]);
|
||||
gl.viewport(0, 0, width, height);
|
||||
}
|
||||
|
||||
gl!.uniform1f(u_time, timeStamp);
|
||||
gl!.drawArrays(gl!.TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, shallowRef } from 'vue';
|
||||
import { onMounted, ref, shallowRef } from 'vue';
|
||||
import * as Misskey from 'cherrypick-js';
|
||||
import * as os from '@/os.js';
|
||||
import MkModal from '@/components/MkModal.vue';
|
||||
|
|
|
@ -43,6 +43,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
fixed
|
||||
:instant="true"
|
||||
:initialText="c.form.text"
|
||||
:initialCw="c.form.cw"
|
||||
/>
|
||||
</div>
|
||||
<MkFolder v-else-if="c.type === 'folder'" :defaultOpen="c.opened">
|
||||
|
@ -60,7 +61,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { Ref } from 'vue';
|
||||
import { Ref, ref } from 'vue';
|
||||
import * as os from '@/os.js';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import MkInput from '@/components/MkInput.vue';
|
||||
|
@ -87,16 +88,17 @@ function g(id) {
|
|||
return props.components.find(x => x.value.id === id).value;
|
||||
}
|
||||
|
||||
let valueForSwitch = $ref(c.default ?? false);
|
||||
const valueForSwitch = ref(c.default ?? false);
|
||||
|
||||
function onSwitchUpdate(v) {
|
||||
valueForSwitch = v;
|
||||
valueForSwitch.value = v;
|
||||
if (c.onChange) c.onChange(v);
|
||||
}
|
||||
|
||||
function openPostForm() {
|
||||
os.post({
|
||||
initialText: c.form.text,
|
||||
initialCw: c.form.cw,
|
||||
instant: true,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { nextTick, onMounted } from 'vue';
|
||||
import { nextTick, onMounted, shallowRef } from 'vue';
|
||||
import { vibrate } from '@/scripts/vibrate.js';
|
||||
import { defaultStore } from '@/store.js';
|
||||
|
||||
|
@ -61,13 +61,13 @@ const emit = defineEmits<{
|
|||
(ev: 'click', payload: MouseEvent): void;
|
||||
}>();
|
||||
|
||||
let el = $shallowRef<HTMLElement | null>(null);
|
||||
let ripples = $shallowRef<HTMLElement | null>(null);
|
||||
const el = shallowRef<HTMLElement | null>(null);
|
||||
const ripples = shallowRef<HTMLElement | null>(null);
|
||||
|
||||
onMounted(() => {
|
||||
if (props.autofocus) {
|
||||
nextTick(() => {
|
||||
el!.focus();
|
||||
el.value!.focus();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -90,11 +90,11 @@ function onMousedown(evt: MouseEvent): void {
|
|||
const rect = target.getBoundingClientRect();
|
||||
|
||||
const ripple = document.createElement('div');
|
||||
ripple.classList.add(ripples!.dataset.childrenClass!);
|
||||
ripple.classList.add(ripples.value!.dataset.childrenClass!);
|
||||
ripple.style.top = (evt.clientY - rect.top - 1).toString() + 'px';
|
||||
ripple.style.left = (evt.clientX - rect.left - 1).toString() + 'px';
|
||||
|
||||
ripples!.appendChild(ripple);
|
||||
ripples.value!.appendChild(ripple);
|
||||
|
||||
const circleCenterX = evt.clientX - rect.left;
|
||||
const circleCenterY = evt.clientY - rect.top;
|
||||
|
@ -111,7 +111,7 @@ function onMousedown(evt: MouseEvent): void {
|
|||
ripple.style.opacity = '0';
|
||||
}, 1000);
|
||||
window.setTimeout(() => {
|
||||
if (ripples) ripples.removeChild(ripple);
|
||||
if (ripples.value) ripples.value.removeChild(ripple);
|
||||
}, 2000);
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -74,7 +74,7 @@ const props = defineProps({
|
|||
},
|
||||
});
|
||||
|
||||
let legendEl = $shallowRef<InstanceType<typeof MkChartLegend>>();
|
||||
const legendEl = shallowRef<InstanceType<typeof MkChartLegend>>();
|
||||
|
||||
const sum = (...arr) => arr.reduce((r, a) => r.map((b, i) => a[i] + b));
|
||||
const negate = arr => arr.map(x => -x);
|
||||
|
@ -268,7 +268,7 @@ const render = () => {
|
|||
gradient,
|
||||
},
|
||||
},
|
||||
plugins: [chartVLine(vLineColor), ...(props.detailed ? [chartLegend(legendEl)] : [])],
|
||||
plugins: [chartVLine(vLineColor), ...(props.detailed ? [chartLegend(legendEl.value)] : [])],
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -13,29 +13,30 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { shallowRef } from 'vue';
|
||||
import { Chart, LegendItem } from 'chart.js';
|
||||
|
||||
const props = defineProps({
|
||||
});
|
||||
|
||||
let chart = $shallowRef<Chart>();
|
||||
let items = $shallowRef<LegendItem[]>([]);
|
||||
const chart = shallowRef<Chart>();
|
||||
const items = shallowRef<LegendItem[]>([]);
|
||||
|
||||
function update(_chart: Chart, _items: LegendItem[]) {
|
||||
chart = _chart,
|
||||
items = _items;
|
||||
chart.value = _chart,
|
||||
items.value = _items;
|
||||
}
|
||||
|
||||
function onClick(item: LegendItem) {
|
||||
if (chart == null) return;
|
||||
const { type } = chart.config;
|
||||
if (chart.value == null) return;
|
||||
const { type } = chart.value.config;
|
||||
if (type === 'pie' || type === 'doughnut') {
|
||||
// Pie and doughnut charts only have a single dataset and visibility is per item
|
||||
chart.toggleDataVisibility(item.index);
|
||||
chart.value.toggleDataVisibility(item.index);
|
||||
} else {
|
||||
chart.setDatasetVisibility(item.datasetIndex, !chart.isDatasetVisible(item.datasetIndex));
|
||||
chart.value.setDatasetVisibility(item.datasetIndex, !chart.value.isDatasetVisible(item.datasetIndex));
|
||||
}
|
||||
chart.update();
|
||||
chart.value.update();
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
|
|
|
@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, onMounted, onUnmounted } from 'vue';
|
||||
import { computed, onMounted, onUnmounted, ref } from 'vue';
|
||||
import MkPlusOneEffect from '@/components/MkPlusOneEffect.vue';
|
||||
import * as os from '@/os.js';
|
||||
import { useInterval } from '@/scripts/use-interval.js';
|
||||
|
@ -29,8 +29,8 @@ import { claimAchievement } from '@/scripts/achievements.js';
|
|||
|
||||
const saveData = game.saveData;
|
||||
const cookies = computed(() => saveData.value?.cookies);
|
||||
let cps = $ref(0);
|
||||
let prevCookies = $ref(0);
|
||||
const cps = ref(0);
|
||||
const prevCookies = ref(0);
|
||||
|
||||
function onClick(ev: MouseEvent) {
|
||||
const x = ev.clientX;
|
||||
|
@ -48,9 +48,9 @@ function onClick(ev: MouseEvent) {
|
|||
}
|
||||
|
||||
useInterval(() => {
|
||||
const diff = saveData.value!.cookies - prevCookies;
|
||||
cps = diff;
|
||||
prevCookies = saveData.value!.cookies;
|
||||
const diff = saveData.value!.cookies - prevCookies.value;
|
||||
cps.value = diff;
|
||||
prevCookies.value = saveData.value!.cookies;
|
||||
}, 1000, {
|
||||
immediate: false,
|
||||
afterMounted: true,
|
||||
|
@ -63,7 +63,7 @@ useInterval(game.save, 1000 * 5, {
|
|||
|
||||
onMounted(async () => {
|
||||
await game.load();
|
||||
prevCookies = saveData.value!.cookies;
|
||||
prevCookies.value = saveData.value!.cookies;
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
|
|
|
@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
-->
|
||||
|
||||
<template>
|
||||
<div :class="[$style.codeEditorRoot, { [$style.disabled]: disabled, [$style.focused]: focused }]">
|
||||
<div :class="[$style.codeEditorRoot, { [$style.focused]: focused }]">
|
||||
<div :class="$style.codeEditorScroller">
|
||||
<textarea
|
||||
ref="inputEl"
|
||||
|
|
|
@ -18,7 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, onBeforeUnmount } from 'vue';
|
||||
import { onMounted, onBeforeUnmount, shallowRef, ref } from 'vue';
|
||||
import MkMenu from './MkMenu.vue';
|
||||
import { MenuItem } from './types/menu.vue';
|
||||
import contains from '@/scripts/contains.js';
|
||||
|
@ -34,9 +34,9 @@ const emit = defineEmits<{
|
|||
(ev: 'closed'): void;
|
||||
}>();
|
||||
|
||||
let rootEl = $shallowRef<HTMLDivElement>();
|
||||
const rootEl = shallowRef<HTMLDivElement>();
|
||||
|
||||
let zIndex = $ref<number>(os.claimZIndex('high'));
|
||||
const zIndex = ref<number>(os.claimZIndex('high'));
|
||||
|
||||
const SCROLLBAR_THICKNESS = 16;
|
||||
|
||||
|
@ -44,8 +44,8 @@ onMounted(() => {
|
|||
let left = props.ev.pageX + 1; // 間違って右ダブルクリックした場合に意図せずアイテムがクリックされるのを防ぐため + 1
|
||||
let top = props.ev.pageY + 1; // 間違って右ダブルクリックした場合に意図せずアイテムがクリックされるのを防ぐため + 1
|
||||
|
||||
const width = rootEl.offsetWidth;
|
||||
const height = rootEl.offsetHeight;
|
||||
const width = rootEl.value.offsetWidth;
|
||||
const height = rootEl.value.offsetHeight;
|
||||
|
||||
if (left + width - window.pageXOffset >= (window.innerWidth - SCROLLBAR_THICKNESS)) {
|
||||
left = (window.innerWidth - SCROLLBAR_THICKNESS) - width + window.pageXOffset;
|
||||
|
@ -63,8 +63,8 @@ onMounted(() => {
|
|||
left = 0;
|
||||
}
|
||||
|
||||
rootEl.style.top = `${top}px`;
|
||||
rootEl.style.left = `${left}px`;
|
||||
rootEl.value.style.top = `${top}px`;
|
||||
rootEl.value.style.left = `${left}px`;
|
||||
|
||||
document.body.addEventListener('mousedown', onMousedown);
|
||||
});
|
||||
|
@ -74,7 +74,7 @@ onBeforeUnmount(() => {
|
|||
});
|
||||
|
||||
function onMousedown(evt: Event) {
|
||||
if (!contains(rootEl, evt.target) && (rootEl !== evt.target)) emit('closed');
|
||||
if (!contains(rootEl.value, evt.target) && (rootEl.value !== evt.target)) emit('closed');
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted } from 'vue';
|
||||
import { onMounted, shallowRef, ref } from 'vue';
|
||||
import * as Misskey from 'cherrypick-js';
|
||||
import Cropper from 'cropperjs';
|
||||
import tinycolor from 'tinycolor2';
|
||||
|
@ -56,10 +56,10 @@ const props = defineProps<{
|
|||
}>();
|
||||
|
||||
const imgUrl = getProxiedImageUrl(props.file.url, undefined, true);
|
||||
let dialogEl = $shallowRef<InstanceType<typeof MkModalWindow>>();
|
||||
let imgEl = $shallowRef<HTMLImageElement>();
|
||||
const dialogEl = shallowRef<InstanceType<typeof MkModalWindow>>();
|
||||
const imgEl = shallowRef<HTMLImageElement>();
|
||||
let cropper: Cropper | null = null;
|
||||
let loading = $ref(true);
|
||||
const loading = ref(true);
|
||||
|
||||
const ok = async () => {
|
||||
const promise = new Promise<Misskey.entities.DriveFile>(async (res) => {
|
||||
|
@ -94,16 +94,16 @@ const ok = async () => {
|
|||
const f = await promise;
|
||||
|
||||
emit('ok', f);
|
||||
dialogEl!.close();
|
||||
dialogEl.value!.close();
|
||||
};
|
||||
|
||||
const cancel = () => {
|
||||
emit('cancel');
|
||||
dialogEl!.close();
|
||||
dialogEl.value!.close();
|
||||
};
|
||||
|
||||
const onImageLoad = () => {
|
||||
loading = false;
|
||||
loading.value = false;
|
||||
|
||||
if (cropper) {
|
||||
cropper.getCropperImage()!.$center('contain');
|
||||
|
@ -112,7 +112,7 @@ const onImageLoad = () => {
|
|||
};
|
||||
|
||||
onMounted(() => {
|
||||
cropper = new Cropper(imgEl!, {
|
||||
cropper = new Cropper(imgEl.value!, {
|
||||
});
|
||||
|
||||
const computedStyle = getComputedStyle(document.documentElement);
|
||||
|
|
|
@ -31,8 +31,8 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<MkInput v-if="input" v-model="inputValue" autofocus :type="input.type || 'text'" :placeholder="input.placeholder || undefined" :autocomplete="input.autocomplete" @keydown="onInputKeydown">
|
||||
<template v-if="input.type === 'password'" #prefix><i class="ti ti-lock"></i></template>
|
||||
<template #caption>
|
||||
<span v-if="okButtonDisabled && disabledReason === 'charactersExceeded'" v-text="i18n.t('_dialog.charactersExceeded', { current: (inputValue as string).length, max: input.maxLength ?? 'NaN' })"/>
|
||||
<span v-else-if="okButtonDisabled && disabledReason === 'charactersBelow'" v-text="i18n.t('_dialog.charactersBelow', { current: (inputValue as string).length, min: input.minLength ?? 'NaN' })"/>
|
||||
<span v-if="okButtonDisabledReason === 'charactersExceeded'" v-text="i18n.t('_dialog.charactersExceeded', { current: (inputValue as string).length, max: input.maxLength ?? 'NaN' })"/>
|
||||
<span v-else-if="okButtonDisabledReason === 'charactersBelow'" v-text="i18n.t('_dialog.charactersBelow', { current: (inputValue as string).length, min: input.minLength ?? 'NaN' })"/>
|
||||
</template>
|
||||
</MkInput>
|
||||
<MkSelect v-if="select" v-model="selectedValue" autofocus>
|
||||
|
@ -46,7 +46,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</template>
|
||||
</MkSelect>
|
||||
<div v-if="(showOkButton || showCancelButton) && !actions" :class="$style.buttons">
|
||||
<MkButton v-if="showOkButton" data-cy-modal-dialog-ok inline primary rounded :autofocus="!input && !select" :disabled="okButtonDisabled" @click="ok">{{ okText ?? ((showCancelButton || input || select) ? i18n.ts.ok : i18n.ts.gotIt) }}</MkButton>
|
||||
<MkButton v-if="showOkButton" data-cy-modal-dialog-ok inline primary rounded :autofocus="!input && !select" :disabled="okButtonDisabledReason" @click="ok">{{ okText ?? ((showCancelButton || input || select) ? i18n.ts.ok : i18n.ts.gotIt) }}</MkButton>
|
||||
<MkButton v-if="showCancelButton || input || select" data-cy-modal-dialog-cancel inline rounded @click="cancel">{{ cancelText ?? i18n.ts.cancel }}</MkButton>
|
||||
</div>
|
||||
<div v-if="actions" :class="[$style.buttons, { [$style.changeButtonAlign]: actions.length > 2 }]">
|
||||
|
@ -57,7 +57,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onBeforeUnmount, onMounted, ref, shallowRef } from 'vue';
|
||||
import { onBeforeUnmount, onMounted, ref, shallowRef, computed } from 'vue';
|
||||
import MkModal from '@/components/MkModal.vue';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import MkInput from '@/components/MkInput.vue';
|
||||
|
@ -124,24 +124,21 @@ const modal = shallowRef<InstanceType<typeof MkModal>>();
|
|||
const inputValue = ref<string | number | null>(props.input?.default ?? null);
|
||||
const selectedValue = ref(props.select?.default ?? null);
|
||||
|
||||
let disabledReason = $ref<null | 'charactersExceeded' | 'charactersBelow'>(null);
|
||||
const okButtonDisabled = $computed<boolean>(() => {
|
||||
const okButtonDisabledReason = computed<null | 'charactersExceeded' | 'charactersBelow'>(() => {
|
||||
if (props.input) {
|
||||
if (props.input.minLength) {
|
||||
if ((inputValue.value || inputValue.value === '') && (inputValue.value as string).length < props.input.minLength) {
|
||||
disabledReason = 'charactersBelow';
|
||||
return true;
|
||||
return 'charactersBelow';
|
||||
}
|
||||
}
|
||||
if (props.input.maxLength) {
|
||||
if (inputValue.value && (inputValue.value as string).length > props.input.maxLength) {
|
||||
disabledReason = 'charactersExceeded';
|
||||
return true;
|
||||
return 'charactersExceeded';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return null;
|
||||
});
|
||||
|
||||
function done(canceled: boolean, result?) {
|
||||
|
|
|
@ -39,6 +39,7 @@ import { i18n } from '@/i18n.js';
|
|||
import { defaultStore } from '@/store.js';
|
||||
import { claimAchievement } from '@/scripts/achievements.js';
|
||||
import copyToClipboard from '@/scripts/copy-to-clipboard.js';
|
||||
import { MenuItem } from '@/types/menu.js';
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
folder: Misskey.entities.DriveFolder;
|
||||
|
@ -250,7 +251,7 @@ function setAsUploadFolder() {
|
|||
}
|
||||
|
||||
function onContextmenu(ev: MouseEvent) {
|
||||
let menu;
|
||||
let menu: MenuItem[];
|
||||
menu = [{
|
||||
text: i18n.ts.openInWindow,
|
||||
icon: 'ti ti-app-window',
|
||||
|
@ -260,18 +261,18 @@ function onContextmenu(ev: MouseEvent) {
|
|||
}, {
|
||||
}, 'closed');
|
||||
},
|
||||
}, null, {
|
||||
}, { type: 'divider' }, {
|
||||
text: i18n.ts.rename,
|
||||
icon: 'ti ti-forms',
|
||||
action: rename,
|
||||
}, null, {
|
||||
}, { type: 'divider' }, {
|
||||
text: i18n.ts.delete,
|
||||
icon: 'ti ti-trash',
|
||||
danger: true,
|
||||
action: deleteFolder,
|
||||
}];
|
||||
if (defaultStore.state.devMode) {
|
||||
menu = menu.concat([null, {
|
||||
menu = menu.concat([{ type: 'divider' }, {
|
||||
icon: 'ti ti-id',
|
||||
text: i18n.ts.copyFolderId,
|
||||
action: () => {
|
||||
|
|
|
@ -616,7 +616,7 @@ function getMenu() {
|
|||
type: 'switch',
|
||||
text: i18n.ts.keepOriginalUploading,
|
||||
ref: keepOriginal,
|
||||
}, null, {
|
||||
}, { type: 'divider' }, {
|
||||
text: i18n.ts.addFile,
|
||||
type: 'label',
|
||||
}, {
|
||||
|
@ -627,7 +627,7 @@ function getMenu() {
|
|||
text: i18n.ts.fromUrl,
|
||||
icon: 'ti ti-link',
|
||||
action: () => { urlUpload(); },
|
||||
}, null, {
|
||||
}, { type: 'divider' }, {
|
||||
text: folder.value ? folder.value.name : i18n.ts.drive,
|
||||
type: 'label',
|
||||
}, folder.value ? {
|
||||
|
|
|
@ -31,6 +31,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { shallowRef } from 'vue';
|
||||
import MkModal from '@/components/MkModal.vue';
|
||||
import MkEmojiPicker from '@/components/MkEmojiPicker.vue';
|
||||
import { defaultStore } from '@/store.js';
|
||||
|
@ -54,23 +55,23 @@ const emit = defineEmits<{
|
|||
(ev: 'closed'): void;
|
||||
}>();
|
||||
|
||||
const modal = $shallowRef<InstanceType<typeof MkModal>>();
|
||||
const picker = $shallowRef<InstanceType<typeof MkEmojiPicker>>();
|
||||
const modal = shallowRef<InstanceType<typeof MkModal>>();
|
||||
const picker = shallowRef<InstanceType<typeof MkEmojiPicker>>();
|
||||
|
||||
function chosen(emoji: any) {
|
||||
emit('done', emoji);
|
||||
if (props.choseAndClose) {
|
||||
modal?.close();
|
||||
modal.value?.close();
|
||||
}
|
||||
}
|
||||
|
||||
function opening() {
|
||||
picker?.reset();
|
||||
picker?.focus();
|
||||
picker.value?.reset();
|
||||
picker.value?.focus();
|
||||
|
||||
// 何故かちょっと待たないとフォーカスされない
|
||||
setTimeout(() => {
|
||||
picker?.focus();
|
||||
picker.value?.focus();
|
||||
}, 10);
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import { shallowRef, ref } from 'vue';
|
||||
import * as Misskey from 'cherrypick-js';
|
||||
import MkModalWindow from '@/components/MkModalWindow.vue';
|
||||
import MkTextarea from '@/components/MkTextarea.vue';
|
||||
|
@ -42,12 +42,12 @@ const emit = defineEmits<{
|
|||
(ev: 'closed'): void;
|
||||
}>();
|
||||
|
||||
const dialog = $shallowRef<InstanceType<typeof MkModalWindow>>();
|
||||
const dialog = shallowRef<InstanceType<typeof MkModalWindow>>();
|
||||
|
||||
let caption = $ref(props.default);
|
||||
const caption = ref(props.default);
|
||||
|
||||
async function ok() {
|
||||
emit('done', caption);
|
||||
dialog.close();
|
||||
emit('done', caption.value);
|
||||
dialog.value.close();
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -43,7 +43,7 @@ window.addEventListener('resize', () => {
|
|||
isMobile.value = deviceKind === 'smartphone' || window.innerWidth <= MOBILE_THRESHOLD;
|
||||
});
|
||||
|
||||
let showEl = $ref(false);
|
||||
const showEl = ref(false);
|
||||
|
||||
const miLocalStoragePrefix = 'ui:folder:' as const;
|
||||
|
||||
|
@ -103,7 +103,7 @@ onMounted(() => {
|
|||
bg.value = _bg.toRgbString();
|
||||
|
||||
globalEvents.on('showEl', (showEl_receive) => {
|
||||
showEl = showEl_receive;
|
||||
showEl.value = showEl_receive;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -51,7 +51,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { nextTick, onMounted } from 'vue';
|
||||
import { nextTick, onMounted, shallowRef, ref } from 'vue';
|
||||
import { defaultStore } from '@/store.js';
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
|
@ -75,10 +75,10 @@ const getBgColor = (el: HTMLElement) => {
|
|||
}
|
||||
};
|
||||
|
||||
let rootEl = $shallowRef<HTMLElement>();
|
||||
let bgSame = $ref(false);
|
||||
let opened = $ref(props.defaultOpen);
|
||||
let openedAtLeastOnce = $ref(props.defaultOpen);
|
||||
const rootEl = shallowRef<HTMLElement>();
|
||||
const bgSame = ref(false);
|
||||
const opened = ref(props.defaultOpen);
|
||||
const openedAtLeastOnce = ref(props.defaultOpen);
|
||||
|
||||
function enter(el) {
|
||||
const elementHeight = el.getBoundingClientRect().height;
|
||||
|
@ -103,20 +103,20 @@ function afterLeave(el) {
|
|||
}
|
||||
|
||||
function toggle() {
|
||||
if (!opened) {
|
||||
openedAtLeastOnce = true;
|
||||
if (!opened.value) {
|
||||
openedAtLeastOnce.value = true;
|
||||
}
|
||||
|
||||
nextTick(() => {
|
||||
opened = !opened;
|
||||
opened.value = !opened.value;
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
const computedStyle = getComputedStyle(document.documentElement);
|
||||
const parentBg = getBgColor(rootEl.parentElement);
|
||||
const parentBg = getBgColor(rootEl.value.parentElement);
|
||||
const myBg = computedStyle.getPropertyValue('--panel');
|
||||
bgSame = parentBg === myBg;
|
||||
bgSame.value = parentBg === myBg;
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
|
|
|
@ -37,7 +37,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onBeforeUnmount, onMounted } from 'vue';
|
||||
import { onBeforeUnmount, onMounted, ref } from 'vue';
|
||||
import * as Misskey from 'cherrypick-js';
|
||||
import * as os from '@/os.js';
|
||||
import { useStream } from '@/stream.js';
|
||||
|
@ -49,7 +49,7 @@ import { globalEvents } from '@/events.js';
|
|||
import { vibrate } from '@/scripts/vibrate.js';
|
||||
import { defaultStore } from '@/store.js';
|
||||
|
||||
let showFollowButton = $ref(false);
|
||||
const showFollowButton = ref(false);
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
user: Misskey.entities.UserDetailed,
|
||||
|
@ -70,9 +70,9 @@ const emit = defineEmits<{
|
|||
(_: 'update:user', value: Misskey.entities.UserDetailed): void
|
||||
}>();
|
||||
|
||||
let isFollowing = $ref(props.user.isFollowing);
|
||||
let hasPendingFollowRequestFromYou = $ref(props.user.hasPendingFollowRequestFromYou);
|
||||
let wait = $ref(false);
|
||||
const isFollowing = ref(props.user.isFollowing);
|
||||
const hasPendingFollowRequestFromYou = ref(props.user.hasPendingFollowRequestFromYou);
|
||||
const wait = ref(false);
|
||||
const connection = useStream().useChannel('main');
|
||||
|
||||
if (props.user.isFollowing == null) {
|
||||
|
@ -84,16 +84,16 @@ if (props.user.isFollowing == null) {
|
|||
|
||||
function onFollowChange(user: Misskey.entities.UserDetailed) {
|
||||
if (user.id === props.user.id) {
|
||||
isFollowing = user.isFollowing;
|
||||
hasPendingFollowRequestFromYou = user.hasPendingFollowRequestFromYou;
|
||||
isFollowing.value = user.isFollowing;
|
||||
hasPendingFollowRequestFromYou.value = user.hasPendingFollowRequestFromYou;
|
||||
}
|
||||
}
|
||||
|
||||
async function onClick() {
|
||||
wait = true;
|
||||
wait.value = true;
|
||||
|
||||
try {
|
||||
if (isFollowing) {
|
||||
if (isFollowing.value) {
|
||||
const { canceled } = await os.confirm({
|
||||
type: 'warning',
|
||||
text: i18n.t('unfollowConfirm', { name: userName(props.user) }),
|
||||
|
@ -105,11 +105,11 @@ async function onClick() {
|
|||
userId: props.user.id,
|
||||
});
|
||||
} else {
|
||||
if (hasPendingFollowRequestFromYou) {
|
||||
if (hasPendingFollowRequestFromYou.value) {
|
||||
await os.api('following/requests/cancel', {
|
||||
userId: props.user.id,
|
||||
});
|
||||
hasPendingFollowRequestFromYou = false;
|
||||
hasPendingFollowRequestFromYou.value = false;
|
||||
} else {
|
||||
await os.api('following/create', {
|
||||
userId: props.user.id,
|
||||
|
@ -120,7 +120,7 @@ async function onClick() {
|
|||
withReplies: defaultStore.state.defaultWithReplies,
|
||||
});
|
||||
vibrate(defaultStore.state.vibrateSystem ? [30, 40, 100] : []);
|
||||
hasPendingFollowRequestFromYou = true;
|
||||
hasPendingFollowRequestFromYou.value = true;
|
||||
|
||||
claimAchievement('following1');
|
||||
|
||||
|
@ -141,7 +141,7 @@ async function onClick() {
|
|||
} catch (err) {
|
||||
console.error(err);
|
||||
} finally {
|
||||
wait = false;
|
||||
wait.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -149,7 +149,7 @@ onMounted(() => {
|
|||
connection.on('follow', onFollowChange);
|
||||
connection.on('unfollow', onFollowChange);
|
||||
|
||||
showFollowButton = $i != null && $i.id !== props.user.id;
|
||||
showFollowButton.value = $i != null && $i.id !== props.user.id;
|
||||
globalEvents.emit('showFollowButton', showFollowButton);
|
||||
});
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import { ref } from 'vue';
|
||||
import MkModalWindow from '@/components/MkModalWindow.vue';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import MkInput from '@/components/MkInput.vue';
|
||||
|
@ -53,19 +53,19 @@ const emit = defineEmits<{
|
|||
(ev: 'closed'): void;
|
||||
}>();
|
||||
|
||||
let dialog: InstanceType<typeof MkModalWindow> = $ref();
|
||||
const dialog = ref<InstanceType<typeof MkModalWindow>>();
|
||||
|
||||
let username = $ref('');
|
||||
let email = $ref('');
|
||||
let processing = $ref(false);
|
||||
const username = ref('');
|
||||
const email = ref('');
|
||||
const processing = ref(false);
|
||||
|
||||
async function onSubmit() {
|
||||
processing = true;
|
||||
processing.value = true;
|
||||
await os.apiWithDialog('request-reset-password', {
|
||||
username,
|
||||
email,
|
||||
username: username.value,
|
||||
email: email.value,
|
||||
});
|
||||
emit('done');
|
||||
dialog.close();
|
||||
dialog.value.close();
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -23,7 +23,7 @@ const query = ref(props.q);
|
|||
const search = () => {
|
||||
const sp = new URLSearchParams();
|
||||
sp.append('q', query.value);
|
||||
window.open(`https://www.google.com/search?${sp.toString()}`, '_blank');
|
||||
window.open(`https://www.google.com/search?${sp.toString()}`, '_blank', 'noopener');
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, nextTick, watch } from 'vue';
|
||||
import { onMounted, nextTick, watch, shallowRef, ref } from 'vue';
|
||||
import { Chart } from 'chart.js';
|
||||
import * as os from '@/os.js';
|
||||
import { defaultStore } from '@/store.js';
|
||||
|
@ -27,11 +27,11 @@ const props = defineProps<{
|
|||
src: string;
|
||||
}>();
|
||||
|
||||
const rootEl = $shallowRef<HTMLDivElement>(null);
|
||||
const chartEl = $shallowRef<HTMLCanvasElement>(null);
|
||||
const rootEl = shallowRef<HTMLDivElement>(null);
|
||||
const chartEl = shallowRef<HTMLCanvasElement>(null);
|
||||
const now = new Date();
|
||||
let chartInstance: Chart = null;
|
||||
let fetching = $ref(true);
|
||||
const fetching = ref(true);
|
||||
|
||||
const { handler: externalTooltipHandler } = useChartTooltip({
|
||||
position: 'middle',
|
||||
|
@ -42,8 +42,8 @@ async function renderChart() {
|
|||
chartInstance.destroy();
|
||||
}
|
||||
|
||||
const wide = rootEl.offsetWidth > 700;
|
||||
const narrow = rootEl.offsetWidth < 400;
|
||||
const wide = rootEl.value.offsetWidth > 700;
|
||||
const narrow = rootEl.value.offsetWidth < 400;
|
||||
|
||||
const weeks = wide ? 50 : narrow ? 10 : 25;
|
||||
const chartLimit = 7 * weeks;
|
||||
|
@ -88,7 +88,7 @@ async function renderChart() {
|
|||
values = raw.deliverFailed;
|
||||
}
|
||||
|
||||
fetching = false;
|
||||
fetching.value = false;
|
||||
|
||||
await nextTick();
|
||||
|
||||
|
@ -101,7 +101,7 @@ async function renderChart() {
|
|||
|
||||
const marginEachCell = 4;
|
||||
|
||||
chartInstance = new Chart(chartEl, {
|
||||
chartInstance = new Chart(chartEl.value, {
|
||||
type: 'matrix',
|
||||
data: {
|
||||
datasets: [{
|
||||
|
@ -210,7 +210,7 @@ async function renderChart() {
|
|||
}
|
||||
|
||||
watch(() => props.src, () => {
|
||||
fetching = true;
|
||||
fetching.value = true;
|
||||
renderChart();
|
||||
});
|
||||
|
||||
|
|
|
@ -21,7 +21,6 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { $ref } from 'vue/macros';
|
||||
import DrawBlurhash from '@/workers/draw-blurhash?worker';
|
||||
import TestWebGL2 from '@/workers/test-webgl2?worker';
|
||||
import { WorkerMultiDispatch } from '@/scripts/worker-multi-dispatch.js';
|
||||
|
@ -58,7 +57,7 @@ const canvasPromise = new Promise<WorkerMultiDispatch | HTMLCanvasElement>(resol
|
|||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, nextTick, onMounted, onUnmounted, shallowRef, watch } from 'vue';
|
||||
import { computed, nextTick, onMounted, onUnmounted, shallowRef, watch, ref } from 'vue';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import { render } from 'buraha';
|
||||
import { defaultStore } from '@/store.js';
|
||||
|
@ -100,41 +99,41 @@ const viewId = uuid();
|
|||
const canvas = shallowRef<HTMLCanvasElement>();
|
||||
const root = shallowRef<HTMLDivElement>();
|
||||
const img = shallowRef<HTMLImageElement>();
|
||||
let loaded = $ref(false);
|
||||
let canvasWidth = $ref(64);
|
||||
let canvasHeight = $ref(64);
|
||||
let imgWidth = $ref(props.width);
|
||||
let imgHeight = $ref(props.height);
|
||||
let bitmapTmp = $ref<CanvasImageSource | undefined>();
|
||||
const hide = computed(() => !loaded || props.forceBlurhash);
|
||||
const loaded = ref(false);
|
||||
const canvasWidth = ref(64);
|
||||
const canvasHeight = ref(64);
|
||||
const imgWidth = ref(props.width);
|
||||
const imgHeight = ref(props.height);
|
||||
const bitmapTmp = ref<CanvasImageSource | undefined>();
|
||||
const hide = computed(() => !loaded.value || props.forceBlurhash);
|
||||
|
||||
function waitForDecode() {
|
||||
if (props.src != null && props.src !== '') {
|
||||
nextTick()
|
||||
.then(() => img.value?.decode())
|
||||
.then(() => {
|
||||
loaded = true;
|
||||
loaded.value = true;
|
||||
}, error => {
|
||||
console.log('Error occurred during decoding image', img.value, error);
|
||||
});
|
||||
} else {
|
||||
loaded = false;
|
||||
loaded.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
watch([() => props.width, () => props.height, root], () => {
|
||||
const ratio = props.width / props.height;
|
||||
if (ratio > 1) {
|
||||
canvasWidth = Math.round(64 * ratio);
|
||||
canvasHeight = 64;
|
||||
canvasWidth.value = Math.round(64 * ratio);
|
||||
canvasHeight.value = 64;
|
||||
} else {
|
||||
canvasWidth = 64;
|
||||
canvasHeight = Math.round(64 / ratio);
|
||||
canvasWidth.value = 64;
|
||||
canvasHeight.value = Math.round(64 / ratio);
|
||||
}
|
||||
|
||||
const clientWidth = root.value?.clientWidth ?? 300;
|
||||
imgWidth = clientWidth;
|
||||
imgHeight = Math.round(clientWidth / ratio);
|
||||
imgWidth.value = clientWidth;
|
||||
imgHeight.value = Math.round(clientWidth / ratio);
|
||||
}, {
|
||||
immediate: true,
|
||||
});
|
||||
|
@ -142,15 +141,15 @@ watch([() => props.width, () => props.height, root], () => {
|
|||
function drawImage(bitmap: CanvasImageSource) {
|
||||
// canvasがない(mountedされていない)場合はTmpに保存しておく
|
||||
if (!canvas.value) {
|
||||
bitmapTmp = bitmap;
|
||||
bitmapTmp.value = bitmap;
|
||||
return;
|
||||
}
|
||||
|
||||
// canvasがあれば描画する
|
||||
bitmapTmp = undefined;
|
||||
bitmapTmp.value = undefined;
|
||||
const ctx = canvas.value.getContext('2d');
|
||||
if (!ctx) return;
|
||||
ctx.drawImage(bitmap, 0, 0, canvasWidth, canvasHeight);
|
||||
ctx.drawImage(bitmap, 0, 0, canvasWidth.value, canvasHeight.value);
|
||||
}
|
||||
|
||||
function drawAvg() {
|
||||
|
@ -162,7 +161,7 @@ function drawAvg() {
|
|||
// avgColorでお茶をにごす
|
||||
ctx.beginPath();
|
||||
ctx.fillStyle = extractAvgColorFromBlurhash(props.hash) ?? '#888';
|
||||
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
|
||||
ctx.fillRect(0, 0, canvasWidth.value, canvasHeight.value);
|
||||
}
|
||||
|
||||
async function draw() {
|
||||
|
@ -214,8 +213,8 @@ watch(() => props.hash, () => {
|
|||
|
||||
onMounted(() => {
|
||||
// drawImageがmountedより先に呼ばれている場合はここで描画する
|
||||
if (bitmapTmp) {
|
||||
drawImage(bitmapTmp);
|
||||
if (bitmapTmp.value) {
|
||||
drawImage(bitmapTmp.value);
|
||||
}
|
||||
waitForDecode();
|
||||
});
|
||||
|
|
|
@ -15,6 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import * as Misskey from 'cherrypick-js';
|
||||
import MkMiniChart from '@/components/MkMiniChart.vue';
|
||||
import * as os from '@/os.js';
|
||||
|
@ -24,12 +25,12 @@ const props = defineProps<{
|
|||
instance: Misskey.entities.FederationInstance;
|
||||
}>();
|
||||
|
||||
let chartValues = $ref<number[] | null>(null);
|
||||
const chartValues = ref<number[] | null>(null);
|
||||
|
||||
os.apiGet('charts/instance', { host: props.instance.host, limit: 16 + 1, span: 'day' }).then(res => {
|
||||
// 今日のぶんの値はまだ途中の値であり、それも含めると大抵の場合前日よりも下降しているようなグラフになってしまうため今日は弾く
|
||||
res['requests.received'].splice(0, 1);
|
||||
chartValues = res['requests.received'];
|
||||
chartValues.value = res['requests.received'];
|
||||
});
|
||||
|
||||
function getInstanceIcon(instance): string {
|
||||
|
|
|
@ -84,7 +84,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted } from 'vue';
|
||||
import { onMounted, ref, shallowRef } from 'vue';
|
||||
import { Chart } from 'chart.js';
|
||||
import MkSelect from '@/components/MkSelect.vue';
|
||||
import MkChart from '@/components/MkChart.vue';
|
||||
|
@ -100,11 +100,11 @@ import { initChart } from '@/scripts/init-chart.js';
|
|||
initChart();
|
||||
|
||||
const chartLimit = 500;
|
||||
let chartSpan = $ref<'hour' | 'day'>('hour');
|
||||
let chartSrc = $ref('active-users');
|
||||
let heatmapSrc = $ref('active-users');
|
||||
let subDoughnutEl = $shallowRef<HTMLCanvasElement>();
|
||||
let pubDoughnutEl = $shallowRef<HTMLCanvasElement>();
|
||||
const chartSpan = ref<'hour' | 'day'>('hour');
|
||||
const chartSrc = ref('active-users');
|
||||
const heatmapSrc = ref('active-users');
|
||||
const subDoughnutEl = shallowRef<HTMLCanvasElement>();
|
||||
const pubDoughnutEl = shallowRef<HTMLCanvasElement>();
|
||||
|
||||
const { handler: externalTooltipHandler1 } = useChartTooltip({
|
||||
position: 'middle',
|
||||
|
@ -163,7 +163,7 @@ function createDoughnut(chartEl, tooltip, data) {
|
|||
|
||||
onMounted(() => {
|
||||
os.apiGet('federation/stats', { limit: 30 }).then(fedStats => {
|
||||
createDoughnut(subDoughnutEl, externalTooltipHandler1, fedStats.topSubInstances.map(x => ({
|
||||
createDoughnut(subDoughnutEl.value, externalTooltipHandler1, fedStats.topSubInstances.map(x => ({
|
||||
name: x.host,
|
||||
color: x.themeColor,
|
||||
value: x.followersCount,
|
||||
|
@ -172,7 +172,7 @@ onMounted(() => {
|
|||
},
|
||||
})).concat([{ name: '(other)', color: '#80808080', value: fedStats.otherFollowersCount }]));
|
||||
|
||||
createDoughnut(pubDoughnutEl, externalTooltipHandler2, fedStats.topPubInstances.map(x => ({
|
||||
createDoughnut(pubDoughnutEl.value, externalTooltipHandler2, fedStats.topPubInstances.map(x => ({
|
||||
name: x.host,
|
||||
color: x.themeColor,
|
||||
value: x.followingCount,
|
||||
|
|
|
@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import { computed } from 'vue';
|
||||
import { instanceName } from '@/config.js';
|
||||
import { instance as Instance } from '@/instance.js';
|
||||
import { getProxiedImageUrlNullable } from '@/scripts/media-proxy.js';
|
||||
|
@ -30,7 +30,7 @@ const instance = props.instance ?? {
|
|||
themeColor: (document.querySelector('meta[name="theme-color-orig"]') as HTMLMetaElement).content,
|
||||
};
|
||||
|
||||
const faviconUrl = $computed(() => props.instance ? getProxiedImageUrlNullable(props.instance.faviconUrl, 'preview') : getProxiedImageUrlNullable(Instance.iconUrl, 'preview') ?? getProxiedImageUrlNullable(Instance.faviconUrl, 'preview') ?? '/favicon.ico');
|
||||
const faviconUrl = computed(() => props.instance ? getProxiedImageUrlNullable(props.instance.faviconUrl, 'preview') : getProxiedImageUrlNullable(Instance.iconUrl, 'preview') ?? getProxiedImageUrlNullable(Instance.faviconUrl, 'preview') ?? '/favicon.ico');
|
||||
|
||||
const themeColor = instance.themeColor ?? '#777777';
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import { shallowRef } from 'vue';
|
||||
import MkModal from '@/components/MkModal.vue';
|
||||
import { navbarItemDef } from '@/navbar.js';
|
||||
import { defaultStore } from '@/store.js';
|
||||
|
@ -48,7 +48,7 @@ const preferedModalType = (deviceKind === 'desktop' && props.src != null) ? 'pop
|
|||
deviceKind === 'smartphone' ? 'drawer' :
|
||||
'dialog';
|
||||
|
||||
const modal = $shallowRef<InstanceType<typeof MkModal>>();
|
||||
const modal = shallowRef<InstanceType<typeof MkModal>>();
|
||||
|
||||
const menu = defaultStore.state.menu;
|
||||
|
||||
|
@ -63,7 +63,7 @@ const items = Object.keys(navbarItemDef).filter(k => !menu.includes(k)).map(k =>
|
|||
}));
|
||||
|
||||
function close() {
|
||||
modal.close();
|
||||
modal.value.close();
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
|
||||
<template>
|
||||
<component
|
||||
:is="self ? 'MkA' : 'a'" ref="el" style="word-break: break-all;" class="_link" :[attr]="self ? url.substring(local.length) : url" :rel="rel" :target="target"
|
||||
:is="self ? 'MkA' : 'a'" ref="el" style="word-break: break-all;" class="_link" :[attr]="self ? url.substring(local.length) : url" :rel="rel ?? 'nofollow noopener'" :target="target"
|
||||
:title="url"
|
||||
@click.stop
|
||||
>
|
||||
|
@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { defineAsyncComponent } from 'vue';
|
||||
import { defineAsyncComponent, ref } from 'vue';
|
||||
import { url as local } from '@/config.js';
|
||||
import { useTooltip } from '@/scripts/use-tooltip.js';
|
||||
import * as os from '@/os.js';
|
||||
|
@ -30,13 +30,13 @@ const self = props.url.startsWith(local);
|
|||
const attr = self ? 'to' : 'href';
|
||||
const target = self ? null : '_blank';
|
||||
|
||||
const el = $ref();
|
||||
const el = ref();
|
||||
|
||||
useTooltip($$(el), (showing) => {
|
||||
useTooltip(el, (showing) => {
|
||||
os.popup(defineAsyncComponent(() => import('@/components/MkUrlPreviewPopup.vue')), {
|
||||
showing,
|
||||
url: props.url,
|
||||
source: el,
|
||||
source: el.value,
|
||||
}, {}, 'closed');
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -32,7 +32,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, shallowRef, watch } from 'vue';
|
||||
import { shallowRef, watch, ref } from 'vue';
|
||||
import * as Misskey from 'cherrypick-js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
|
||||
|
@ -42,7 +42,7 @@ const props = withDefaults(defineProps<{
|
|||
});
|
||||
|
||||
const audioEl = shallowRef<HTMLAudioElement>();
|
||||
let hide = $ref(true);
|
||||
const hide = ref(true);
|
||||
|
||||
watch(audioEl, () => {
|
||||
if (audioEl.value) {
|
||||
|
|
|
@ -55,7 +55,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, onUnmounted, watch } from 'vue';
|
||||
import { onMounted, onUnmounted, watch, ref, computed } from 'vue';
|
||||
import * as Misskey from 'cherrypick-js';
|
||||
import { getStaticImageUrl } from '@/scripts/media-proxy.js';
|
||||
import bytes from '@/filters/bytes.js';
|
||||
|
@ -78,20 +78,20 @@ const props = withDefaults(defineProps<{
|
|||
controls: true,
|
||||
});
|
||||
|
||||
let hide = $ref(true);
|
||||
let darkMode: boolean = $ref(defaultStore.state.darkMode);
|
||||
const hide = ref(true);
|
||||
const darkMode = ref<boolean>(defaultStore.state.darkMode);
|
||||
|
||||
let playAnimation = $ref(true);
|
||||
if (defaultStore.state.showingAnimatedImages === 'interaction') playAnimation = false;
|
||||
let playAnimationTimer = setTimeout(() => playAnimation = false, 5000);
|
||||
const url = $computed(() => (props.raw || defaultStore.state.loadRawImages)
|
||||
const playAnimation = ref(true);
|
||||
if (defaultStore.state.showingAnimatedImages === 'interaction') playAnimation.value = false;
|
||||
let playAnimationTimer = setTimeout(() => playAnimation.value = false, 5000);
|
||||
const url = computed(() => (props.raw || defaultStore.state.loadRawImages)
|
||||
? props.image.url
|
||||
: (defaultStore.state.disableShowingAnimatedImages || defaultStore.state.dataSaver.media) || (['interaction', 'inactive'].includes(<string>defaultStore.state.showingAnimatedImages) && !playAnimation)
|
||||
: (defaultStore.state.disableShowingAnimatedImages || defaultStore.state.dataSaver.media) || (['interaction', 'inactive'].includes(<string>defaultStore.state.showingAnimatedImages) && !playAnimation.value)
|
||||
? getStaticImageUrl(props.image.url)
|
||||
: props.image.thumbnailUrl,
|
||||
);
|
||||
|
||||
let clickToShowMessage = $computed(() => defaultStore.state.nsfwOpenBehavior === 'click'
|
||||
const clickToShowMessage = computed(() => defaultStore.state.nsfwOpenBehavior === 'click'
|
||||
? i18n.ts.clickToShow
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
: defaultStore.state.nsfwOpenBehavior === 'doubleClick'
|
||||
|
@ -103,12 +103,12 @@ function onClick(ev: MouseEvent) {
|
|||
if (!props.controls) {
|
||||
return;
|
||||
}
|
||||
if (!hide) return;
|
||||
if (!hide.value) return;
|
||||
if (defaultStore.state.nsfwOpenBehavior === 'doubleClick') {
|
||||
os.popup(MkRippleEffect, { x: ev.clientX, y: ev.clientY }, {}, 'end');
|
||||
}
|
||||
if (defaultStore.state.nsfwOpenBehavior === 'click') {
|
||||
hide = false;
|
||||
hide.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -116,20 +116,20 @@ function onDblclick() {
|
|||
if (!props.controls) {
|
||||
return;
|
||||
}
|
||||
if (hide && defaultStore.state.nsfwOpenBehavior === 'doubleClick') {
|
||||
hide = false;
|
||||
if (hide.value && defaultStore.state.nsfwOpenBehavior === 'doubleClick') {
|
||||
hide.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function resetTimer() {
|
||||
playAnimation = true;
|
||||
playAnimation.value = true;
|
||||
clearTimeout(playAnimationTimer);
|
||||
playAnimationTimer = setTimeout(() => playAnimation = false, 5000);
|
||||
playAnimationTimer = setTimeout(() => playAnimation.value = false, 5000);
|
||||
}
|
||||
|
||||
// Plugin:register_note_view_interruptor を使って書き換えられる可能性があるためwatchする
|
||||
watch(() => props.image, () => {
|
||||
hide = (defaultStore.state.nsfw === 'force' || defaultStore.state.dataSaver.media) ? true : (props.image.isSensitive && defaultStore.state.nsfw !== 'ignore');
|
||||
hide.value = (defaultStore.state.nsfw === 'force' || defaultStore.state.dataSaver.media) ? true : (props.image.isSensitive && defaultStore.state.nsfw !== 'ignore');
|
||||
}, {
|
||||
deep: true,
|
||||
immediate: true,
|
||||
|
@ -140,7 +140,7 @@ function showMenu(ev: MouseEvent) {
|
|||
text: i18n.ts.hide,
|
||||
icon: 'ti ti-eye-off',
|
||||
action: () => {
|
||||
hide = true;
|
||||
hide.value = true;
|
||||
},
|
||||
}, ...(iAmModerator ? [{
|
||||
text: i18n.ts.markAsSensitive,
|
||||
|
|
|
@ -63,7 +63,7 @@ async function getClientWidthWithCache(targetEl: HTMLElement, containerEl: HTMLE
|
|||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, onUnmounted, shallowRef } from 'vue';
|
||||
import { computed, onMounted, onUnmounted, shallowRef } from 'vue';
|
||||
import * as Misskey from 'cherrypick-js';
|
||||
import PhotoSwipeLightbox from 'photoswipe/lightbox';
|
||||
import PhotoSwipe from 'photoswipe';
|
||||
|
@ -86,7 +86,7 @@ const container = shallowRef<HTMLElement | null | undefined>(undefined);
|
|||
const gallery = shallowRef<HTMLDivElement>();
|
||||
const pswpZIndex = os.claimZIndex('middle');
|
||||
document.documentElement.style.setProperty('--mk-pswp-root-z-index', pswpZIndex.toString());
|
||||
const count = $computed(() => props.mediaList.filter(media => previewable(media)).length);
|
||||
const count = computed(() => props.mediaList.filter(media => previewable(media)).length);
|
||||
let lightbox: PhotoSwipeLightbox | null;
|
||||
|
||||
const popstateHandler = (): void => {
|
||||
|
|
|
@ -32,7 +32,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, shallowRef, watch } from 'vue';
|
||||
import { computed, ref, shallowRef, watch } from 'vue';
|
||||
import * as Misskey from 'cherrypick-js';
|
||||
import bytes from '@/filters/bytes.js';
|
||||
import { defaultStore } from '@/store.js';
|
||||
|
@ -47,7 +47,7 @@ const props = defineProps<{
|
|||
|
||||
const hide = ref((defaultStore.state.nsfw === 'force' || defaultStore.state.dataSaver.media) ? true : (props.video.isSensitive && defaultStore.state.nsfw !== 'ignore'));
|
||||
|
||||
let clickToShowMessage = $computed(() => defaultStore.state.nsfwOpenBehavior === 'click'
|
||||
const clickToShowMessage = computed(() => defaultStore.state.nsfwOpenBehavior === 'click'
|
||||
? i18n.ts.clickToShow
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
: defaultStore.state.nsfwOpenBehavior === 'doubleClick'
|
||||
|
|
|
@ -23,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
|
||||
<script lang="ts" setup>
|
||||
import { toUnicode } from 'punycode';
|
||||
import { computed, onMounted, onUnmounted } from 'vue';
|
||||
import { computed, onMounted, onUnmounted, ref } from 'vue';
|
||||
import tinycolor from 'tinycolor2';
|
||||
import { host as localHost } from '@/config.js';
|
||||
import { $i } from '@/account.js';
|
||||
|
@ -48,18 +48,18 @@ const bg = tinycolor(getComputedStyle(document.documentElement).getPropertyValue
|
|||
bg.setAlpha(0.1);
|
||||
const bgCss = bg.toRgbString();
|
||||
|
||||
let playAnimation = $ref(true);
|
||||
if (defaultStore.state.showingAnimatedImages === 'interaction') playAnimation = false;
|
||||
let playAnimationTimer = setTimeout(() => playAnimation = false, 5000);
|
||||
const avatarUrl = $computed(() => (defaultStore.state.disableShowingAnimatedImages || defaultStore.state.dataSaver.avatar) || (['interaction', 'inactive'].includes(<string>defaultStore.state.showingAnimatedImages) && !playAnimation)
|
||||
const playAnimation = ref(true);
|
||||
if (defaultStore.state.showingAnimatedImages === 'interaction') playAnimation.value = false;
|
||||
let playAnimationTimer = setTimeout(() => playAnimation.value = false, 5000);
|
||||
const avatarUrl = computed(() => (defaultStore.state.disableShowingAnimatedImages || defaultStore.state.dataSaver.avatar) || (['interaction', 'inactive'].includes(<string>defaultStore.state.showingAnimatedImages) && !playAnimation.value)
|
||||
? getStaticImageUrl(`/avatar/@${props.username}@${props.host}`)
|
||||
: `/avatar/@${props.username}@${props.host}`,
|
||||
);
|
||||
|
||||
function resetTimer() {
|
||||
playAnimation = true;
|
||||
playAnimation.value = true;
|
||||
clearTimeout(playAnimationTimer);
|
||||
playAnimationTimer = setTimeout(() => playAnimation = false, 5000);
|
||||
playAnimationTimer = setTimeout(() => playAnimation.value = false, 5000);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
|
|
|
@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
@contextmenu.self="e => e.preventDefault()"
|
||||
>
|
||||
<template v-for="(item, i) in items2">
|
||||
<div v-if="item === null" role="separator" :class="$style.divider"></div>
|
||||
<div v-if="item.type === 'divider'" role="separator" :class="$style.divider"></div>
|
||||
<span v-else-if="item.type === 'label'" role="menuitem" :class="[$style.label, $style.item]">
|
||||
<span>{{ item.text }}</span>
|
||||
</span>
|
||||
|
@ -62,7 +62,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Ref, defineAsyncComponent, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue';
|
||||
import { Ref, computed, defineAsyncComponent, nextTick, onBeforeUnmount, onMounted, ref, shallowRef, watch } from 'vue';
|
||||
import { focusPrev, focusNext } from '@/scripts/focus.js';
|
||||
import MkSwitchButton from '@/components/MkSwitch.button.vue';
|
||||
import { MenuItem, InnerMenuItem, MenuPending, MenuAction, MenuSwitch, MenuParent } from '@/types/menu.js';
|
||||
|
@ -91,19 +91,19 @@ const emit = defineEmits<{
|
|||
(ev: 'hide'): void;
|
||||
}>();
|
||||
|
||||
let itemsEl = $shallowRef<HTMLDivElement>();
|
||||
const itemsEl = shallowRef<HTMLDivElement>();
|
||||
|
||||
let items2: InnerMenuItem[] = $ref([]);
|
||||
const items2 = ref<InnerMenuItem[]>([]);
|
||||
|
||||
let child = $shallowRef<InstanceType<typeof XChild>>();
|
||||
const child = shallowRef<InstanceType<typeof XChild>>();
|
||||
|
||||
let keymap = $computed(() => ({
|
||||
const keymap = computed(() => ({
|
||||
'up|k|shift+tab': focusUp,
|
||||
'down|j|tab': focusDown,
|
||||
'esc': close,
|
||||
}));
|
||||
|
||||
let childShowingItem = $ref<MenuItem | null>();
|
||||
const childShowingItem = ref<MenuItem | null>();
|
||||
|
||||
let preferClick = isTouchUsing || props.asDrawer;
|
||||
|
||||
|
@ -116,22 +116,22 @@ watch(() => props.items, () => {
|
|||
if (item && 'then' in item) { // if item is Promise
|
||||
items[i] = { type: 'pending' };
|
||||
item.then(actualItem => {
|
||||
items2[i] = actualItem;
|
||||
items2.value[i] = actualItem;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
items2 = items as InnerMenuItem[];
|
||||
items2.value = items as InnerMenuItem[];
|
||||
}, {
|
||||
immediate: true,
|
||||
});
|
||||
|
||||
const childMenu = ref<MenuItem[] | null>();
|
||||
let childTarget = $shallowRef<HTMLElement | null>();
|
||||
const childTarget = shallowRef<HTMLElement | null>();
|
||||
|
||||
function closeChild() {
|
||||
childMenu.value = null;
|
||||
childShowingItem = null;
|
||||
childShowingItem.value = null;
|
||||
}
|
||||
|
||||
function childActioned() {
|
||||
|
@ -140,8 +140,8 @@ function childActioned() {
|
|||
}
|
||||
|
||||
const onGlobalMousedown = (event: MouseEvent) => {
|
||||
if (childTarget && (event.target === childTarget || childTarget.contains(event.target))) return;
|
||||
if (child && child.checkHit(event)) return;
|
||||
if (childTarget.value && (event.target === childTarget.value || childTarget.value.contains(event.target))) return;
|
||||
if (child.value && child.value.checkHit(event)) return;
|
||||
closeChild();
|
||||
};
|
||||
|
||||
|
@ -178,10 +178,10 @@ async function showChildren(item: MenuParent, ev: MouseEvent) {
|
|||
});
|
||||
emit('hide');
|
||||
} else {
|
||||
childTarget = ev.currentTarget ?? ev.target;
|
||||
childTarget.value = ev.currentTarget ?? ev.target;
|
||||
// これでもリアクティビティは保たれる
|
||||
childMenu.value = children;
|
||||
childShowingItem = item;
|
||||
childShowingItem.value = item;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -210,7 +210,7 @@ function switchItem(item: MenuSwitch & { ref: any }) {
|
|||
onMounted(() => {
|
||||
if (props.viaKeyboard) {
|
||||
nextTick(() => {
|
||||
if (itemsEl) focusNext(itemsEl.children[0], true, false);
|
||||
if (itemsEl.value) focusNext(itemsEl.value.children[0], true, false);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { watch } from 'vue';
|
||||
import { watch, ref } from 'vue';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import tinycolor from 'tinycolor2';
|
||||
import { useInterval } from '@/scripts/use-interval.js';
|
||||
|
@ -43,11 +43,11 @@ const props = defineProps<{
|
|||
const viewBoxX = 50;
|
||||
const viewBoxY = 50;
|
||||
const gradientId = uuid();
|
||||
let polylinePoints = $ref('');
|
||||
let polygonPoints = $ref('');
|
||||
let headX = $ref<number | null>(null);
|
||||
let headY = $ref<number | null>(null);
|
||||
let clock = $ref<number | null>(null);
|
||||
const polylinePoints = ref('');
|
||||
const polygonPoints = ref('');
|
||||
const headX = ref<number | null>(null);
|
||||
const headY = ref<number | null>(null);
|
||||
const clock = ref<number | null>(null);
|
||||
const accent = tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--accent'));
|
||||
const color = accent.toRgbString();
|
||||
|
||||
|
@ -60,12 +60,12 @@ function draw(): void {
|
|||
(1 - (n / peak)) * viewBoxY,
|
||||
]);
|
||||
|
||||
polylinePoints = _polylinePoints.map(xy => `${xy[0]},${xy[1]}`).join(' ');
|
||||
polylinePoints.value = _polylinePoints.map(xy => `${xy[0]},${xy[1]}`).join(' ');
|
||||
|
||||
polygonPoints = `0,${ viewBoxY } ${ polylinePoints } ${ viewBoxX },${ viewBoxY }`;
|
||||
polygonPoints.value = `0,${ viewBoxY } ${ polylinePoints.value } ${ viewBoxX },${ viewBoxY }`;
|
||||
|
||||
headX = _polylinePoints.at(-1)![0];
|
||||
headY = _polylinePoints.at(-1)![1];
|
||||
headX.value = _polylinePoints.at(-1)![0];
|
||||
headY.value = _polylinePoints.at(-1)![1];
|
||||
}
|
||||
|
||||
watch(() => props.src, draw, { immediate: true });
|
||||
|
|
|
@ -42,7 +42,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { nextTick, normalizeClass, onMounted, onUnmounted, provide, watch } from 'vue';
|
||||
import { nextTick, normalizeClass, onMounted, onUnmounted, provide, watch, ref, shallowRef, computed } from 'vue';
|
||||
import * as os from '@/os.js';
|
||||
import { isTouchUsing } from '@/scripts/touch.js';
|
||||
import { defaultStore } from '@/store.js';
|
||||
|
@ -89,14 +89,14 @@ const emit = defineEmits<{
|
|||
|
||||
provide('modal', true);
|
||||
|
||||
let maxHeight = $ref<number>();
|
||||
let fixed = $ref(false);
|
||||
let transformOrigin = $ref('center');
|
||||
let showing = $ref(true);
|
||||
let content = $shallowRef<HTMLElement>();
|
||||
const maxHeight = ref<number>();
|
||||
const fixed = ref(false);
|
||||
const transformOrigin = ref('center');
|
||||
const showing = ref(true);
|
||||
const content = shallowRef<HTMLElement>();
|
||||
const zIndex = os.claimZIndex(props.zPriority);
|
||||
let useSendAnime = $ref(false);
|
||||
const type = $computed<ModalTypes>(() => {
|
||||
const useSendAnime = ref(false);
|
||||
const type = computed<ModalTypes>(() => {
|
||||
if (props.preferType === 'auto') {
|
||||
if (!defaultStore.state.disableDrawer && isTouchUsing && deviceKind === 'smartphone') {
|
||||
return 'drawer';
|
||||
|
@ -107,26 +107,26 @@ const type = $computed<ModalTypes>(() => {
|
|||
return props.preferType!;
|
||||
}
|
||||
});
|
||||
const isEnableBgTransparent = $computed(() => props.transparentBg && (type === 'popup'));
|
||||
let transitionName = $computed((() =>
|
||||
const isEnableBgTransparent = computed(() => props.transparentBg && (type.value === 'popup'));
|
||||
const transitionName = computed((() =>
|
||||
defaultStore.state.animation
|
||||
? useSendAnime
|
||||
? useSendAnime.value
|
||||
? 'send'
|
||||
: type === 'drawer'
|
||||
: type.value === 'drawer'
|
||||
? 'modal-drawer'
|
||||
: type === 'popup'
|
||||
: type.value === 'popup'
|
||||
? 'modal-popup'
|
||||
: 'modal'
|
||||
: ''
|
||||
));
|
||||
let transitionDuration = $computed((() =>
|
||||
transitionName === 'send'
|
||||
const transitionDuration = computed((() =>
|
||||
transitionName.value === 'send'
|
||||
? 400
|
||||
: transitionName === 'modal-popup'
|
||||
: transitionName.value === 'modal-popup'
|
||||
? 100
|
||||
: transitionName === 'modal'
|
||||
: transitionName.value === 'modal'
|
||||
? 200
|
||||
: transitionName === 'modal-drawer'
|
||||
: transitionName.value === 'modal-drawer'
|
||||
? 200
|
||||
: 0
|
||||
));
|
||||
|
@ -135,12 +135,12 @@ let contentClicking = false;
|
|||
|
||||
function close(opts: { useSendAnimation?: boolean } = {}) {
|
||||
if (opts.useSendAnimation) {
|
||||
useSendAnime = true;
|
||||
useSendAnime.value = true;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line vue/no-mutating-props
|
||||
if (props.src) props.src.style.pointerEvents = 'auto';
|
||||
showing = false;
|
||||
showing.value = false;
|
||||
emit('close');
|
||||
}
|
||||
|
||||
|
@ -149,8 +149,8 @@ function onBgClick() {
|
|||
emit('click');
|
||||
}
|
||||
|
||||
if (type === 'drawer') {
|
||||
maxHeight = window.innerHeight / 1.5;
|
||||
if (type.value === 'drawer') {
|
||||
maxHeight.value = window.innerHeight / 1.5;
|
||||
}
|
||||
|
||||
const keymap = {
|
||||
|
@ -162,21 +162,21 @@ const SCROLLBAR_THICKNESS = 16;
|
|||
|
||||
const align = () => {
|
||||
if (props.src == null) return;
|
||||
if (type === 'drawer') return;
|
||||
if (type === 'dialog') return;
|
||||
if (type.value === 'drawer') return;
|
||||
if (type.value === 'dialog') return;
|
||||
|
||||
if (content == null) return;
|
||||
if (content.value == null) return;
|
||||
|
||||
const srcRect = props.src.getBoundingClientRect();
|
||||
|
||||
const width = content!.offsetWidth;
|
||||
const height = content!.offsetHeight;
|
||||
const width = content.value!.offsetWidth;
|
||||
const height = content.value!.offsetHeight;
|
||||
|
||||
let left;
|
||||
let top;
|
||||
|
||||
const x = srcRect.left + (fixed ? 0 : window.pageXOffset);
|
||||
const y = srcRect.top + (fixed ? 0 : window.pageYOffset);
|
||||
const x = srcRect.left + (fixed.value ? 0 : window.pageXOffset);
|
||||
const y = srcRect.top + (fixed.value ? 0 : window.pageYOffset);
|
||||
|
||||
if (props.anchor.x === 'center') {
|
||||
left = x + (props.src.offsetWidth / 2) - (width / 2);
|
||||
|
@ -194,7 +194,7 @@ const align = () => {
|
|||
top = y + props.src.offsetHeight;
|
||||
}
|
||||
|
||||
if (fixed) {
|
||||
if (fixed.value) {
|
||||
// 画面から横にはみ出る場合
|
||||
if (left + width > (window.innerWidth - SCROLLBAR_THICKNESS)) {
|
||||
left = (window.innerWidth - SCROLLBAR_THICKNESS) - width;
|
||||
|
@ -207,16 +207,16 @@ const align = () => {
|
|||
if (top + height > ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN)) {
|
||||
if (props.noOverlap && props.anchor.x === 'center') {
|
||||
if (underSpace >= (upperSpace / 3)) {
|
||||
maxHeight = underSpace;
|
||||
maxHeight.value = underSpace;
|
||||
} else {
|
||||
maxHeight = upperSpace;
|
||||
maxHeight.value = upperSpace;
|
||||
top = (upperSpace + MARGIN) - height;
|
||||
}
|
||||
} else {
|
||||
top = ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN) - height;
|
||||
}
|
||||
} else {
|
||||
maxHeight = underSpace;
|
||||
maxHeight.value = underSpace;
|
||||
}
|
||||
} else {
|
||||
// 画面から横にはみ出る場合
|
||||
|
@ -231,16 +231,16 @@ const align = () => {
|
|||
if (top + height - window.pageYOffset > ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN)) {
|
||||
if (props.noOverlap && props.anchor.x === 'center') {
|
||||
if (underSpace >= (upperSpace / 3)) {
|
||||
maxHeight = underSpace;
|
||||
maxHeight.value = underSpace;
|
||||
} else {
|
||||
maxHeight = upperSpace;
|
||||
maxHeight.value = upperSpace;
|
||||
top = window.pageYOffset + ((upperSpace + MARGIN) - height);
|
||||
}
|
||||
} else {
|
||||
top = ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN) - height + window.pageYOffset - 1;
|
||||
}
|
||||
} else {
|
||||
maxHeight = underSpace;
|
||||
maxHeight.value = underSpace;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -255,29 +255,29 @@ const align = () => {
|
|||
let transformOriginX = 'center';
|
||||
let transformOriginY = 'center';
|
||||
|
||||
if (top >= srcRect.top + props.src.offsetHeight + (fixed ? 0 : window.pageYOffset)) {
|
||||
if (top >= srcRect.top + props.src.offsetHeight + (fixed.value ? 0 : window.pageYOffset)) {
|
||||
transformOriginY = 'top';
|
||||
} else if ((top + height) <= srcRect.top + (fixed ? 0 : window.pageYOffset)) {
|
||||
} else if ((top + height) <= srcRect.top + (fixed.value ? 0 : window.pageYOffset)) {
|
||||
transformOriginY = 'bottom';
|
||||
}
|
||||
|
||||
if (left >= srcRect.left + props.src.offsetWidth + (fixed ? 0 : window.pageXOffset)) {
|
||||
if (left >= srcRect.left + props.src.offsetWidth + (fixed.value ? 0 : window.pageXOffset)) {
|
||||
transformOriginX = 'left';
|
||||
} else if ((left + width) <= srcRect.left + (fixed ? 0 : window.pageXOffset)) {
|
||||
} else if ((left + width) <= srcRect.left + (fixed.value ? 0 : window.pageXOffset)) {
|
||||
transformOriginX = 'right';
|
||||
}
|
||||
|
||||
transformOrigin = `${transformOriginX} ${transformOriginY}`;
|
||||
transformOrigin.value = `${transformOriginX} ${transformOriginY}`;
|
||||
|
||||
content.style.left = left + 'px';
|
||||
content.style.top = top + 'px';
|
||||
content.value.style.left = left + 'px';
|
||||
content.value.style.top = top + 'px';
|
||||
};
|
||||
|
||||
const onOpened = () => {
|
||||
emit('opened');
|
||||
|
||||
// モーダルコンテンツにマウスボタンが押され、コンテンツ外でマウスボタンが離されたときにモーダルバックグラウンドクリックと判定させないためにマウスイベントを監視しフラグ管理する
|
||||
const el = content!.children[0];
|
||||
const el = content.value!.children[0];
|
||||
el.addEventListener('mousedown', ev => {
|
||||
contentClicking = true;
|
||||
window.addEventListener('mouseup', ev => {
|
||||
|
@ -299,7 +299,7 @@ onMounted(() => {
|
|||
// eslint-disable-next-line vue/no-mutating-props
|
||||
props.src.style.pointerEvents = 'none';
|
||||
}
|
||||
fixed = (type === 'drawer') || (getFixedContainer(props.src) != null);
|
||||
fixed.value = (type.value === 'drawer') || (getFixedContainer(props.src) != null);
|
||||
|
||||
await nextTick();
|
||||
|
||||
|
@ -307,7 +307,7 @@ onMounted(() => {
|
|||
}, { immediate: true });
|
||||
|
||||
nextTick(() => {
|
||||
alignObserver.observe(content!);
|
||||
alignObserver.observe(content.value!);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, onUnmounted } from 'vue';
|
||||
import { onMounted, onUnmounted, shallowRef, ref } from 'vue';
|
||||
import MkModal from './MkModal.vue';
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
|
@ -44,14 +44,14 @@ const emit = defineEmits<{
|
|||
(event: 'ok'): void;
|
||||
}>();
|
||||
|
||||
let modal = $shallowRef<InstanceType<typeof MkModal>>();
|
||||
let rootEl = $shallowRef<HTMLElement>();
|
||||
let headerEl = $shallowRef<HTMLElement>();
|
||||
let bodyWidth = $ref(0);
|
||||
let bodyHeight = $ref(0);
|
||||
const modal = shallowRef<InstanceType<typeof MkModal>>();
|
||||
const rootEl = shallowRef<HTMLElement>();
|
||||
const headerEl = shallowRef<HTMLElement>();
|
||||
const bodyWidth = ref(0);
|
||||
const bodyHeight = ref(0);
|
||||
|
||||
const close = () => {
|
||||
modal.close();
|
||||
modal.value.close();
|
||||
};
|
||||
|
||||
const onBgClick = () => {
|
||||
|
@ -67,14 +67,14 @@ const onKeydown = (evt) => {
|
|||
};
|
||||
|
||||
const ro = new ResizeObserver((entries, observer) => {
|
||||
bodyWidth = rootEl.offsetWidth;
|
||||
bodyHeight = rootEl.offsetHeight - headerEl.offsetHeight;
|
||||
bodyWidth.value = rootEl.value.offsetWidth;
|
||||
bodyHeight.value = rootEl.value.offsetHeight - headerEl.value.offsetHeight;
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
bodyWidth = rootEl.offsetWidth;
|
||||
bodyHeight = rootEl.offsetHeight - headerEl.offsetHeight;
|
||||
ro.observe(rootEl);
|
||||
bodyWidth.value = rootEl.value.offsetWidth;
|
||||
bodyHeight.value = rootEl.value.offsetHeight - headerEl.value.offsetHeight;
|
||||
ro.observe(rootEl.value);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue