diff --git a/.config/ci.yml b/.config/ci.yml index 8730ccab3a..44092d3662 100644 --- a/.config/ci.yml +++ b/.config/ci.yml @@ -167,18 +167,8 @@ id: 'aidx' # IP address family used for outgoing request (ipv4, ipv6 or dual) #outgoingAddressFamily: ipv4 -# Amount of characters that can be used when writing notes. Longer notes will be rejected. (minimum: 1) -#maxNoteLength: 3000 -# Amount of characters that will be saved for remote notes. Longer notes will be truncated to this length. (minimum: 1) -#maxRemoteNoteLength: 100000 -# Amount of characters that can be used when writing content warnings. Longer warnings will be rejected. (minimum: 1) -#maxCwLength: 500 -# Amount of characters that will be saved for remote content warnings. Longer warnings will be truncated to this length. (minimum: 1) -#maxRemoteCwLength: 5000 -# Amount of characters that can be used when writing media descriptions (alt text). Longer descriptions will be rejected. (minimum: 1) -#maxAltTextLength: 20000 -# Amount of characters that will be saved for remote media descriptions (alt text). Longer descriptions will be truncated to this length. (minimum: 1) -#maxRemoteAltTextLength: 100000 +# Amount of characters that can be used when writing notes (maximum: 8192, minimum: 1) +maxNoteLength: 3000 # Proxy for HTTP/HTTPS #proxy: http://127.0.0.1:3128 @@ -229,8 +219,3 @@ checkActivityPubGetSignature: false # Upload or download file size limits (bytes) #maxFileSize: 262144000 - -# CHMod-style permission bits to apply to uploaded files. -# Permission bits are specified as a base-8 string representing User/Group/Other permissions. -# This setting is only useful for custom deployments, such as using a reverse proxy to serve media. -#filePermissionBits: '644' diff --git a/.config/cypress-devcontainer.yml b/.config/cypress-devcontainer.yml deleted file mode 100644 index 342b0f43da..0000000000 --- a/.config/cypress-devcontainer.yml +++ /dev/null @@ -1,229 +0,0 @@ -#━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -# Misskey configuration -#━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - -# ┌─────┐ -#───┘ URL └───────────────────────────────────────────────────── - -# Final accessible URL seen by a user. -url: 'http://misskey.local' - -# ONCE YOU HAVE STARTED THE INSTANCE, DO NOT CHANGE THE -# URL SETTINGS AFTER THAT! - -# ┌───────────────────────┐ -#───┘ Port and TLS settings └─────────────────────────────────── - -# -# Misskey requires a reverse proxy to support HTTPS connections. -# -# +----- https://example.tld/ ------------+ -# +------+ |+-------------+ +----------------+| -# | User | ---> || Proxy (443) | ---> | Misskey (3000) || -# +------+ |+-------------+ +----------------+| -# +---------------------------------------+ -# -# You need to set up a reverse proxy. (e.g. nginx) -# An encrypted connection with HTTPS is highly recommended -# because tokens may be transferred in GET requests. - -# The port that your Misskey server should listen on. -port: 61812 - -# ┌──────────────────────────┐ -#───┘ PostgreSQL configuration └──────────────────────────────── - -db: - host: db - port: 5432 - - # Database name - db: misskey - - # Auth - user: postgres - pass: postgres - - # Whether disable Caching queries - #disableCache: true - - # Extra Connection options - #extra: - # ssl: true - -dbReplications: false - -# You can configure any number of replicas here -#dbSlaves: -# - -# host: -# port: -# db: -# user: -# pass: -# - -# host: -# port: -# db: -# user: -# pass: - -# ┌─────────────────────┐ -#───┘ Redis configuration └───────────────────────────────────── - -redis: - host: redis - port: 6379 - #family: 0 # 0=Both, 4=IPv4, 6=IPv6 - #pass: example-pass - #prefix: example-prefix - #db: 1 - -#redisForPubsub: -# host: redis -# port: 6379 -# #family: 0 # 0=Both, 4=IPv4, 6=IPv6 -# #pass: example-pass -# #prefix: example-prefix -# #db: 1 - -#redisForJobQueue: -# host: redis -# port: 6379 -# #family: 0 # 0=Both, 4=IPv4, 6=IPv6 -# #pass: example-pass -# #prefix: example-prefix -# #db: 1 - -#redisForTimelines: -# host: redis -# port: 6379 -# #family: 0 # 0=Both, 4=IPv4, 6=IPv6 -# #pass: example-pass -# #prefix: example-prefix -# #db: 1 - -#redisForReactions: -# host: redis -# port: 6379 -# #family: 0 # 0=Both, 4=IPv4, 6=IPv6 -# #pass: example-pass -# #prefix: example-prefix -# #db: 1 - -# ┌───────────────────────────┐ -#───┘ MeiliSearch configuration └───────────────────────────── - -#meilisearch: -# host: meilisearch -# port: 7700 -# apiKey: '' -# ssl: true -# index: '' - -# ┌───────────────┐ -#───┘ ID generation └─────────────────────────────────────────── - -# You can select the ID generation method. -# You don't usually need to change this setting, but you can -# change it according to your preferences. - -# Available methods: -# aid ... Short, Millisecond accuracy -# aidx ... Millisecond accuracy -# meid ... Similar to ObjectID, Millisecond accuracy -# ulid ... Millisecond accuracy -# objectid ... This is left for backward compatibility - -# ONCE YOU HAVE STARTED THE INSTANCE, DO NOT CHANGE THE -# ID SETTINGS AFTER THAT! - -id: 'aidx' - -# ┌────────────────┐ -#───┘ Error tracking └────────────────────────────────────────── - -# Sentry is available for error tracking. -# See the Sentry documentation for more details on options. - -#sentryForBackend: -# enableNodeProfiling: true -# options: -# dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0' - -#sentryForFrontend: -# options: -# dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0' - -# ┌─────────────────────┐ -#───┘ Other configuration └───────────────────────────────────── - -# Whether disable HSTS -#disableHsts: true - -# Number of worker processes -#clusterLimit: 1 - -# Job concurrency per worker -# deliverJobConcurrency: 128 -# inboxJobConcurrency: 16 - -# Job rate limiter -# deliverJobPerSec: 128 -# inboxJobPerSec: 32 - -# Job attempts -# deliverJobMaxAttempts: 12 -# inboxJobMaxAttempts: 8 - -# IP address family used for outgoing request (ipv4, ipv6 or dual) -#outgoingAddressFamily: ipv4 - -# Amount of characters that can be used when writing notes. Longer notes will be rejected. (minimum: 1) -#maxNoteLength: 3000 -# Amount of characters that will be saved for remote notes. Longer notes will be truncated to this length. (minimum: 1) -#maxRemoteNoteLength: 100000 -# Amount of characters that can be used when writing content warnings. Longer warnings will be rejected. (minimum: 1) -#maxCwLength: 500 -# Amount of characters that will be saved for remote content warnings. Longer warnings will be truncated to this length. (minimum: 1) -#maxRemoteCwLength: 5000 -# Amount of characters that can be used when writing media descriptions (alt text). Longer descriptions will be rejected. (minimum: 1) -#maxAltTextLength: 20000 -# Amount of characters that will be saved for remote media descriptions (alt text). Longer descriptions will be truncated to this length. (minimum: 1) -#maxRemoteAltTextLength: 100000 - -# Proxy for HTTP/HTTPS -#proxy: http://127.0.0.1:3128 - -proxyBypassHosts: - - api.deepl.com - - api-free.deepl.com - - www.recaptcha.net - - hcaptcha.com - - challenges.cloudflare.com - -# Proxy for SMTP/SMTPS -#proxySmtp: http://127.0.0.1:3128 # use HTTP/1.1 CONNECT -#proxySmtp: socks4://127.0.0.1:1080 # use SOCKS4 -#proxySmtp: socks5://127.0.0.1:1080 # use SOCKS5 - -# Media Proxy -#mediaProxy: https://example.com/proxy - -# Proxy remote files (default: true) -proxyRemoteFiles: true - -# Sign to ActivityPub GET request (default: true) -signToActivityPubGet: true - -allowedPrivateNetworks: [ - '127.0.0.1/32' -] - -# Upload or download file size limits (bytes) -#maxFileSize: 262144000 - -# CHMod-style permission bits to apply to uploaded files. -# Permission bits are specified as a base-8 string representing User/Group/Other permissions. -# This setting is only useful for custom deployments, such as using a reverse proxy to serve media. -#filePermissionBits: '644' diff --git a/.config/docker_example.env b/.config/docker_example.env index c61248da2e..4fe8e76b78 100644 --- a/.config/docker_example.env +++ b/.config/docker_example.env @@ -1,11 +1,5 @@ -# misskey settings -# MISSKEY_URL=https://example.tld/ - # db settings POSTGRES_PASSWORD=example-misskey-pass -# DATABASE_PASSWORD=${POSTGRES_PASSWORD} POSTGRES_USER=example-misskey-user -# DATABASE_USER=${POSTGRES_USER} POSTGRES_DB=misskey -# DATABASE_DB=${POSTGRES_DB} DATABASE_URL="postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}" diff --git a/.config/docker_example.yml b/.config/docker_example.yml index ce2daf3aec..f4645d672d 100644 --- a/.config/docker_example.yml +++ b/.config/docker_example.yml @@ -63,7 +63,6 @@ #───┘ URL └───────────────────────────────────────────────────── # Final accessible URL seen by a user. -# You can set url from an environment variable instead. url: https://example.tld/ # ONCE YOU HAVE STARTED THE INSTANCE, DO NOT CHANGE THE @@ -96,11 +95,9 @@ db: port: 5432 # Database name - # You can set db from an environment variable instead. db: misskey # Auth - # You can set user and pass from environment variables instead. user: example-misskey-user pass: example-misskey-pass @@ -163,14 +160,6 @@ redis: # #prefix: example-prefix # #db: 1 -#redisForReactions: -# host: redis -# port: 6379 -# #family: 0 # 0=Both, 4=IPv4, 6=IPv6 -# #pass: example-pass -# #prefix: example-prefix -# #db: 1 - # ┌───────────────────────────┐ #───┘ MeiliSearch configuration └───────────────────────────── @@ -250,18 +239,8 @@ id: 'aidx' # IP address family used for outgoing request (ipv4, ipv6 or dual) #outgoingAddressFamily: ipv4 -# Amount of characters that can be used when writing notes. Longer notes will be rejected. (minimum: 1) -#maxNoteLength: 3000 -# Amount of characters that will be saved for remote notes. Longer notes will be truncated to this length. (minimum: 1) -#maxRemoteNoteLength: 100000 -# Amount of characters that can be used when writing content warnings. Longer warnings will be rejected. (minimum: 1) -#maxCwLength: 500 -# Amount of characters that will be saved for remote content warnings. Longer warnings will be truncated to this length. (minimum: 1) -#maxRemoteCwLength: 5000 -# Amount of characters that can be used when writing media descriptions (alt text). Longer descriptions will be rejected. (minimum: 1) -#maxAltTextLength: 20000 -# Amount of characters that will be saved for remote media descriptions (alt text). Longer descriptions will be truncated to this length. (minimum: 1) -#maxRemoteAltTextLength: 100000 +# Amount of characters that can be used when writing notes (maximum: 8192, minimum: 1) +maxNoteLength: 3000 # Proxy for HTTP/HTTPS #proxy: http://127.0.0.1:3128 @@ -312,8 +291,3 @@ checkActivityPubGetSignature: false # Upload or download file size limits (bytes) #maxFileSize: 262144000 - -# CHMod-style permission bits to apply to uploaded files. -# Permission bits are specified as a base-8 string representing User/Group/Other permissions. -# This setting is only useful for custom deployments, such as using a reverse proxy to serve media. -#filePermissionBits: '644' diff --git a/.config/example.yml b/.config/example.yml index 9debb3bf70..21e85b7b89 100644 --- a/.config/example.yml +++ b/.config/example.yml @@ -99,10 +99,10 @@ db: port: 5432 # Database name - db: sharkey + db: misskey # Auth - user: sharkey + user: example-misskey-user pass: example-misskey-pass # Whether disable Caching queries @@ -172,16 +172,6 @@ redis: # # You can specify more ioredis options... # #username: example-username -#redisForReactions: -# host: localhost -# port: 6379 -# #family: 0 # 0=Both, 4=IPv4, 6=IPv6 -# #pass: example-pass -# #prefix: example-prefix -# #db: 1 -# # You can specify more ioredis options... -# #username: example-username - # ┌───────────────────────────┐ #───┘ MeiliSearch configuration └───────────────────────────── @@ -261,18 +251,8 @@ id: 'aidx' # IP address family used for outgoing request (ipv4, ipv6 or dual) #outgoingAddressFamily: ipv4 -# Amount of characters that can be used when writing notes. Longer notes will be rejected. (minimum: 1) -#maxNoteLength: 3000 -# Amount of characters that will be saved for remote notes. Longer notes will be truncated to this length. (minimum: 1) -#maxRemoteNoteLength: 100000 -# Amount of characters that can be used when writing content warnings. Longer warnings will be rejected. (minimum: 1) -#maxCwLength: 500 -# Amount of characters that will be saved for remote content warnings. Longer warnings will be truncated to this length. (minimum: 1) -#maxRemoteCwLength: 5000 -# Amount of characters that can be used when writing media descriptions (alt text). Longer descriptions will be rejected. (minimum: 1) -#maxAltTextLength: 20000 -# Amount of characters that will be saved for remote media descriptions (alt text). Longer descriptions will be truncated to this length. (minimum: 1) -#maxRemoteAltTextLength: 100000 +# Amount of characters that can be used when writing notes (maximum: 8192, minimum: 1) +maxNoteLength: 3000 # Proxy for HTTP/HTTPS #proxy: http://127.0.0.1:3128 @@ -334,8 +314,3 @@ checkActivityPubGetSignature: false # PID File of master process #pidFile: /tmp/misskey.pid - -# CHMod-style permission bits to apply to uploaded files. -# Permission bits are specified as a base-8 string representing User/Group/Other permissions. -# This setting is only useful for custom deployments, such as using a reverse proxy to serve media. -#filePermissionBits: '644' diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index fbf959d449..31b6212cb5 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,16 +1,16 @@ { "name": "Misskey", - "dockerComposeFile": "compose.yml", + "dockerComposeFile": "docker-compose.yml", "service": "app", "workspaceFolder": "/workspace", "features": { "ghcr.io/devcontainers/features/node:1": { - "version": "20.16.0" + "version": "20.12.2" }, "ghcr.io/devcontainers-contrib/features/corepack:1": {} }, "forwardPorts": [3000], - "postCreateCommand": "/bin/bash .devcontainer/init.sh", + "postCreateCommand": "sudo chmod 755 .devcontainer/init.sh && .devcontainer/init.sh", "customizations": { "vscode": { "extensions": [ diff --git a/.devcontainer/devcontainer.yml b/.devcontainer/devcontainer.yml index 3eb4fc2879..beefcfd0a2 100644 --- a/.devcontainer/devcontainer.yml +++ b/.devcontainer/devcontainer.yml @@ -103,14 +103,6 @@ redis: # #prefix: example-prefix # #db: 1 -#redisForReactions: -# host: redis -# port: 6379 -# #family: 0 # 0=Both, 4=IPv4, 6=IPv6 -# #pass: example-pass -# #prefix: example-prefix -# #db: 1 - # ┌───────────────────────────┐ #───┘ MeiliSearch configuration └───────────────────────────── diff --git a/.devcontainer/compose.yml b/.devcontainer/docker-compose.yml similarity index 93% rename from .devcontainer/compose.yml rename to .devcontainer/docker-compose.yml index d02d2a8f4a..2809cd2ca4 100644 --- a/.devcontainer/compose.yml +++ b/.devcontainer/docker-compose.yml @@ -1,3 +1,5 @@ +version: '3.8' + services: app: build: @@ -6,7 +8,6 @@ services: volumes: - ../:/workspace:cached - - node_modules:/workspace/node_modules command: sleep infinity @@ -45,7 +46,6 @@ services: volumes: postgres-data: redis-data: - node_modules: networks: internal_network: diff --git a/.devcontainer/init.sh b/.devcontainer/init.sh index e02a533c15..729e1a9d2d 100755 --- a/.devcontainer/init.sh +++ b/.devcontainer/init.sh @@ -2,10 +2,7 @@ set -xe -sudo chown node node_modules -sudo apt-get update -sudo apt-get -y install libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libnss3 libxss1 libasound2 libxtst6 xauth xvfb -git config --global --add safe.directory /workspace +sudo chown -R node /workspace git submodule update --init corepack install corepack enable @@ -14,4 +11,3 @@ pnpm install --frozen-lockfile cp .devcontainer/devcontainer.yml .config/default.yml pnpm build pnpm migrate -pnpm exec cypress install diff --git a/.dockerignore b/.dockerignore index f204349160..1de0c7982b 100644 --- a/.dockerignore +++ b/.dockerignore @@ -7,11 +7,12 @@ Dockerfile build/ built/ db/ -.devcontainer/compose.yml +docker-compose.yml node_modules/ packages/*/node_modules redis/ files/ +misskey-assets/ fluent-emojis/ .pnp.* @@ -27,4 +28,4 @@ fluent-emojis/ .idea/ packages/*/.vscode/ -packages/backend/test/compose.yml +packages/backend/test/docker-compose.yml diff --git a/.gitignore b/.gitignore index 7cc7354a4a..2b6a5c1ebf 100644 --- a/.gitignore +++ b/.gitignore @@ -23,9 +23,6 @@ packages/sw/.yarn/cache # pnpm .pnpm-store -# eslint -**/.eslintcache - # Cypress cypress/screenshots cypress/videos @@ -38,17 +35,13 @@ coverage !/.config/example.yml !/.config/docker_example.yml !/.config/docker_example.env -!/.config/cypress-devcontainer.yml docker-compose.yml -compose.yml -.devcontainer/compose.yml -!/.devcontainer/compose.yml +!/.devcontainer/docker-compose.yml # misskey /build built built-test -js-built /data /.cache-loader /db @@ -66,11 +59,9 @@ ormconfig.json temp /packages/frontend/src/**/*.stories.ts tsdoc-metadata.json -misskey-assets -# Vite temporary files -vite.config.js.timestamp-* -vite.config.ts.timestamp-* +# Sharkey +/packages/megalodon/lib # blender backups *.blend1 @@ -81,6 +72,3 @@ vite.config.ts.timestamp-* # VSCode addon .favorites.json - -# Sharkey -/packages/megalodon/lib diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 4db8bda32e..2e773eddf9 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -20,9 +20,9 @@ testCommit: - pnpm install --frozen-lockfile - pnpm run build - pnpm run migrate - - pnpm run --filter='!megalodon' test - - pnpm run --filter=backend --filter=misskey-js lint - - pnpm run --filter=frontend --filter=frontend-embed eslint + - pnpm run --filter='!megalodon' --workspace-concurrency=1 test + - pnpm run --filter=backend lint + - pnpm run --filter=frontend eslint cache: key: test policy: pull-push diff --git a/.gitlab/issue_templates/bug.md b/.gitlab/issue_templates/bug.md index a909067269..6914647570 100644 --- a/.gitlab/issue_templates/bug.md +++ b/.gitlab/issue_templates/bug.md @@ -3,33 +3,27 @@ 🔒 Found a security vulnerability? [Please disclose it responsibly.](https://activitypub.software/TransFem-org/Sharkey/-/blob/develop/SECURITY.md) 🤝 By submitting this feature request, you agree to follow our [Contribution Guidelines.](https://activitypub.software/TransFem-org/Sharkey/-/blob/develop/CONTRIBUTING.md) --> -# **What happened?** - +**What happened?** _(Please give us a brief description of what happened.)_ -# **What did you expect to happen?** - +**What did you expect to happen?** _(Please give us a brief description of what you expected to happen.)_ -# **Version** - +**Version** _(What version of Sharkey is your instance running? You can find this by clicking your instance's logo at the top left and then clicking instance information.)_ -# **Instance** - +**Instance** _(What instance of Sharkey are you using?)_ -# **What type of issue is this?** - +**What type of issue is this?** _(If this happens on your device and has to do with the user interface, it's client-side. If this happens on either with the API or the backend, or you got a server-side error in the client, it's server-side.)_ -# **What browser are you using? (Client-side issues only)** +**What browser are you using? (Client-side issues only)** -# **What operating system are you using? (Client-side issues only)** +**What operating system are you using? (Client-side issues only)** -# **How do you deploy Sharkey on your server? (Server-side issues only)** +**How do you deploy Sharkey on your server? (Server-side issues only)** -# **What operating system are you using? (Server-side issues only)** +**What operating system are you using? (Server-side issues only)** -# **Relevant log output** - +**Relevant log output** _(Please copy and paste any relevant log output. You can find your log by inspecting the page, and going to the "console" tab. This will be automatically formatted into code, so no need for backticks.)_ -# **Contribution Guidelines** +**Contribution Guidelines** By submitting this issue, you agree to follow our [Contribution Guidelines](https://activitypub.software/TransFem-org/Sharkey/-/blob/develop/CONTRIBUTING.md) - [ ] I agree to follow this project's Contribution Guidelines - [ ] I have searched the issue tracker for similar issues, and this is not a duplicate. diff --git a/.gitlab/issue_templates/feature.md b/.gitlab/issue_templates/feature.md index a77f9335fe..d4235eb5a3 100644 --- a/.gitlab/issue_templates/feature.md +++ b/.gitlab/issue_templates/feature.md @@ -3,19 +3,15 @@ 🔒 Found a security vulnerability? [Please disclose it responsibly.](https://activitypub.software/TransFem-org/Sharkey/-/blob/develop/SECURITY.md) 🤝 By submitting this feature request, you agree to follow our [Contribution Guidelines.](https://activitypub.software/TransFem-org/Sharkey/-/blob/develop/CONTRIBUTING.md) --> -# **What feature would you like implemented?** - +**What feature would you like implemented?** _(Please give us a brief description of what you'd like.)_ -# **Why should we add this feature?** - +**Why should we add this feature?** _(Please give us a brief description of why your feature is important.)_ -# **Version** - +**Version** _(What version of Sharkey is your instance running? You can find this by clicking your instance's logo at the top left and then clicking instance information.)_ -# **Instance** - +**Instance** _(What instance of Sharkey are you using?)_ -# **Contribution Guidelines** +**Contribution Guidelines** By submitting this issue, you agree to follow our [Contribution Guidelines](https://activitypub.software/TransFem-org/Sharkey/-/blob/develop/CONTRIBUTING.md) - [ ] I agree to follow this project's Contribution Guidelines - [ ] I have searched the issue tracker for similar requests, and this is not a duplicate. diff --git a/.gitlab/merge_request_templates/default.md b/.gitlab/merge_request_templates/default.md index e6977def70..18bffa5419 100644 --- a/.gitlab/merge_request_templates/default.md +++ b/.gitlab/merge_request_templates/default.md @@ -1,12 +1,11 @@ -# **What does this MR do?** - +**What does this PR do?** _(Please give us a brief description of what this PR does.)_ -# **Contribution Guidelines** +**Contribution Guidelines** By submitting this merge request, you agree to follow our [Contribution Guidelines](https://activitypub.software/TransFem-org/Sharkey/-/blob/develop/CONTRIBUTING.md) - [ ] I agree to follow this project's Contribution Guidelines -- [ ] I have made sure to test this merge request +- [ ] I have made sure to test this pull request diff --git a/.gitmodules b/.gitmodules index 1a68b48180..a3ca76cc96 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ +[submodule "misskey-assets"] + path = misskey-assets + url = https://github.com/misskey-dev/assets.git [submodule "fluent-emojis"] path = fluent-emojis url = https://github.com/misskey-dev/emojis.git diff --git a/.node-version b/.node-version index 8ce7030825..87834047a6 100644 --- a/.node-version +++ b/.node-version @@ -1 +1 @@ -20.16.0 +20.12.2 diff --git a/CHANGELOG.md b/CHANGELOG.md index cf0437e51a..e2528594c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,220 +1,14 @@ -## 2024.9.0 +## Unreleased ### General -- Feat: ノート単体・ユーザーのノート・クリップのノートの埋め込み機能 - - 埋め込みコードやウェブサイトへの実装方法の詳細は https://misskey-hub.net/docs/for-users/features/embed/ をご覧ください -- Feat: パスキーでログインボタンを実装 (#14574) -- Feat: フォローされた際のメッセージを設定できるように -- Feat: 連合をホワイトリスト制にできるように -- Feat: UserWebhookとSystemWebhookのテスト送信機能を追加 (#14445) -- Feat: モデレーターはユーザーにかかわらずファイルが添付されているノートを検索できるように - (Cherry-picked from https://github.com/MisskeyIO/misskey/pull/680) -- Feat: データエクスポートが完了した際に通知を発行するように -- Enhance: ユーザーによるコンテンツインポートの可否をロールポリシーで制御できるように -- Enhance: 依存関係の更新 -- Enhance: l10nの更新 - -### Client -- Enhance: サイズ制限を超過するファイルをアップロードしようとした際にエラーを出すように -- Enhance: アイコンデコレーション管理画面にプレビューを追加 -- Enhance: コントロールパネル内のファイル一覧でセンシティブなファイルを区別しやすく -- Enhance: ScratchpadにUIインスペクターを追加 -- Enhance: Play編集画面の項目の並びを少しリデザイン -- Enhance: 各種メニューをドロワー表示するかどうか設定可能に -- Enhance: AiScriptのMk:C:containerのオプションに`borderStyle`と`borderRadius`を追加 -- Enhance: CWでも絵文字をクリックしてメニューを表示できるように -- Fix: サーバーメトリクスが2つ以上あるとリロード直後の表示がおかしくなる問題を修正 -- Fix: コントロールパネル内のAp requests内のチャートの表示がおかしかった問題を修正 -- Fix: 月の違う同じ日はセパレータが表示されないのを修正 -- Fix: タッチ画面でレンジスライダーを操作するとツールチップが複数表示される問題を修正 - (Cherry-picked from https://github.com/taiyme/misskey/pull/265) -- Fix: 縦横比が極端なカスタム絵文字を表示する際にレイアウトが崩れる箇所があるのを修正 - (Cherry-picked from https://github.com/MisskeyIO/misskey/pull/725) -- Fix: 設定変更時のリロード確認ダイアログが複数個表示されることがある問題を修正 -- Fix: ファイルの詳細ページのファイルの説明で改行が正しく表示されない問題を修正 - (Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/commit/bde6bb0bd2e8b0d027e724d2acdb8ae0585a8110) -- Fix: 一部画面のページネーションが動作しにくくなっていたのを修正 ( #12766 , #11449 ) - -### Server -- Feat: Misskey® Reactions Boost Technology™ (RBT)により、リアクションの作成負荷を低減することが可能に -- Fix: アンテナの書き込み時にキーワードが与えられなかった場合のエラーをApiErrorとして投げるように - - この変更により、公式フロントエンドでは入力の不備が内部エラーとして報告される代わりに一般的なエラーダイアログで報告されます -- Fix: ファイルがサイズの制限を超えてアップロードされた際にエラーを返さなかった問題を修正 -- Fix: 外部ページを解析する際に、ページに紐づけられた関連リソースも読み込まれてしまう問題を修正 - (Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/commit/26e0412fbb91447c37e8fb06ffb0487346063bb8) -- Fix: Continue importing from file if single emoji import fails -- Fix: `Retry-After`ヘッダーが送信されなかった問題を修正 - (Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/commit/8a982c61c01909e7540ff1be9f019df07c3f0624) -- Fix: サーバーサイドのDOM解析完了時にリソースを開放するように - (Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/634) -- Fix: ``を追って照会するのはOKレスポンスが返却された場合のみに - (Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/633) -- Fix: メールにスタイルが適用されていなかった問題を修正 - -## 2024.8.0 - -### General -- Enhance: モデレーターはすべてのユーザーのフォロー・フォロワーの一覧を見られるように -- Enhance: アカウントの削除のモデレーションログを残すように -- Enhance: 不適切なページ、ギャラリー、Playを管理者権限で削除できるように -- Fix: リモートユーザのフォロー・フォロワーの一覧が非公開設定の場合も表示できてしまう問題を修正 - -### Client -- Enhance: 「自分のPlay」ページにおいてPlayが非公開かどうかが一目でわかるように -- Enhance: 不適切なページ、ギャラリー、Playを通報できるように -- Fix: Play編集時に公開範囲が「パブリック」にリセットされる問題を修正 -- Fix: ページ遷移に失敗することがある問題を修正 -- Fix: iOSでユーザー名などがリンクとして誤検知される現象を抑制 -- Fix: mCaptchaを使用していてもbotプロテクションに関する警告が消えないのを修正 -- Fix: ユーザーのモデレーションページにおいてユーザー名にドットが入っているとシステムアカウントとして表示されてしまう問題を修正 -- Fix: 特定の条件下でノートの削除ボタンが出ないのを修正 - -### Server -- Enhance: 照会時にURLがhtmlかつheadタグ内に`rel="alternate"`, `type="application/activity+json"`の`link`タグがある場合に追ってリンク先を照会できるように -- Enhance: 凍結されたアカウントのフォローリクエストを表示しないように -- Fix: WSの`readAllNotifications` メッセージが `body` を持たない場合に動作しない問題 #14374 - - 通知ページや通知カラム(デッキ)を開いている状態において、新たに発生した通知が既読されない問題が修正されます。 - - これにより、プッシュ通知が有効な同条件下の環境において、プッシュ通知が常に発生してしまう問題も修正されます。 -- Fix: Play各種エンドポイントの返り値に`visibility`が含まれていない問題を修正 -- Fix: サーバー情報取得の際にモデレーター限定の情報が取得できないことがあるのを修正 - (Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/582) -- Fix: 公開範囲がダイレクトのノートをユーザーアクティビティのチャート生成に使用しないように - (Cherry-picked from https://github.com/MisskeyIO/misskey/pull/679) -- Fix: ActivityPubのエンティティタイプ判定で不明なタイプを受け取った場合でも処理を継続するように - - キュー処理のつまりが改善される可能性があります -- Fix: リバーシの対局設定の変更が反映されないのを修正 -- Fix: 無制限にストリーミングのチャンネルに接続できる問題を修正 -- Fix: ベースロールのポリシーを変更した際にモデログに記録されないのを修正 - (Cherry-picked from https://github.com/MisskeyIO/misskey/pull/700) -- Fix: Prevent memory leak from memory caches (#14310) -- Fix: More reliable memory cache eviction (#14311) - -## 2024.7.0 - -### Note -- デッキUIの新着ノートをサウンドで通知する機能の追加(v2024.5.0)に伴い、以前から動作しなくなっていたクライアント設定内の「アンテナ受信」「チャンネル通知」サウンドを削除しました。 -- Streaming APIにて入力が不正な場合にはそのメッセージを無視するようになりました。 #14251 - -### General -- Feat: 通報を受けた際、または解決した際に、予め登録した宛先に通知を飛ばせるように(mail or webhook) #13705 -- Feat: ユーザーのアイコン/バナーの変更可否をロールで設定可能に - - 変更不可となっていても、設定済みのものを解除してデフォルト画像に戻すことは出来ます -- Feat: ユーザ作成時にSystemWebhookを送信可能に #14281 -- Feat: メディアサイレンスを実装 #13842 - - メディアサイレンスされたサーバーに所属するアカウントによるファイルはすべてセンシティブとして扱われ、カスタム絵文字が使用できないようになります。 -- Enhance: 管理画面でアーカイブにしたお知らせを表示・編集できるように - Fix: 配信停止したインスタンス一覧が見れなくなる問題を修正 -- Fix: Dockerコンテナの立ち上げ時に`pnpm`のインストールで固まることがある問題 -- Fix: デフォルトテーマに無効なテーマコードを入力するとUIが使用できなくなる問題を修正 -- 翻訳の更新 -- 依存関係の更新 ### Client -- Feat: ユーザーページから「このユーザーのノートを検索」できるように (#14128) -- Feat: 検索ページはクエリを受け付けるようになりました (#14128) -- Enhance: 検索ページのUI改善 (#14128) -- Enhance: 内蔵APIドキュメントのデザイン・パフォーマンスを改善 -- Enhance: 非ログイン時に他サーバーに遷移するアクションを追加 -- Enhance: 非ログイン時のハイライトTLのデザインを改善 -- Enhance: フロントエンドのアクセシビリティ改善 - (Based on https://github.com/taiyme/misskey/pull/226) -- Enhance: サーバー情報ページ・お問い合わせページを改善 - (Cherry-picked from https://github.com/taiyme/misskey/pull/238) -- Enhance: AiScriptを0.19.0にアップデート -- Enhance: Allow negative delay for MFM animation elements (`tada`, `jelly`, `twitch`, `shake`, `spin`, `jump`, `bounce`, `rainbow`) -- Enhance: センシティブなメディアを開く際に確認ダイアログを出せるように -- Enhance: 検索(ノート/ユーザー)で `#` から始まる文字列を入力すると、そのハッシュタグのノート/ユーザー一覧ページが表示できるように -- Enhance: 検索(ノート/ユーザー)において、入力に空白が含まれている場合は照会を行わないように -- Enhance: 検索(ノート/ユーザー)において、照会を行うかどうか、ハッシュタグのノート/ユーザー一覧ページを表示するかどうかの確認ダイアログを出すように -- Enhance: 検索(ノート/ユーザー)で `@` から始まる文字列(`@user@host`など)を入力すると、そのユーザーを照会できるように -- Enhance: ドライブのファイル・フォルダをドラッグしなくても移動できるように - (Cherry-picked from https://github.com/nafu-at/misskey/commit/b89c2af6945c6a9f9f10e83f54d2bcf0f240b0b4, https://github.com/nafu-at/misskey/commit/8a7d710c6acb83f50c83f050bd1423c764d60a99) -- Enhance: デッキのアンテナ・リスト選択画面からそれぞれを新規作成できるように -- Enhance: ブラウザのコンテキストメニューを使用できるように -- Enhance: 連合の「連合中」,「購読中」,「配信中」に対してブロックしているサーバー、配信停止しているサーバーを含めないように -- Fix: `/about#federation` ページなどで各インスタンスのチャートが表示されなくなっていた問題を修正 -- Fix: ユーザーページの追加情報のラベルを投稿者のサーバーの絵文字で表示する (#13968) -- Fix: リバーシの対局を正しく共有できないことがある問題を修正 -- Fix: コントロールパネルでベースロールのポリシーを編集してもUI上では変更が反映されない問題を修正 -- Fix: アンテナの編集画面のボタンに隙間を追加 -- Fix: テーマプレビューが見れない問題を修正 -- Fix: ショートカットキーが連打できる問題を修正 - (Cherry-picked from https://github.com/taiyme/misskey/pull/234) -- Fix: MkSignin.vueのcredentialRequestからReactivityを削除(ProxyがPasskey認証処理に渡ることを避けるため) -- Fix: 「アニメーション画像を再生しない」がオンのときでもサーバーのバナー画像・背景画像がアニメーションしてしまう問題を修正 - (Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/574) -- Fix: Twitchの埋め込みが開けない問題を修正 -- Fix: 子メニューの高さがウィンドウからはみ出ることがある問題を修正 -- Fix: 個人宛てのダイアログ形式のお知らせが即時表示されない問題を修正 -- Fix: 一部の画像がセンシティブ指定されているときに画面に何も表示されないことがあるのを修正 -- Fix: リアクションしたユーザー一覧のユーザー名がはみ出る問題を修正 - (Cherry-picked from https://github.com/MisskeyIO/misskey/pull/672) -- Fix: `/share`ページにおいて絵文字ピッカーを開くことができない問題を修正 -- Fix: deck uiの通知音が重なる問題 (#14029) -- Fix: ダイレクト投稿の"削除して編集"において、宛先が保持されていなかった問題を修正 -- Fix: 投稿フォームへのURL貼り付けによる引用が下書きに保存されていなかった問題を修正 -- Fix: "削除して編集"や下書きにおいて、リアクションの受け入れ設定が保持/保存されていなかった問題を修正 -- Fix: 照会に `#` から始まる文字列を入力してそのハッシュタグのページを表示する際、入力が `#` のみの場合に「指定されたURLに該当するページはありませんでした。」が表示されてしまう問題を修正 -- Fix: 照会に `@` から始まる文字列を入力してユーザーを照会する際、入力が `@` のみの場合に「問題が発生しました」が表示されてしまう問題を修正 -- Fix: 投稿フォームにノートのURLを貼り付けて"引用として添付"した場合、投稿文を空にすることによるRenote化が出来なかった問題を修正 -- Fix: フォロー中のユーザーに関する"TLに他の人への返信を含める"の設定が分かりづらい問題を修正 -- Fix: タイムラインページを開いた時、`TLに他の人への返信を含める`がオフのときに`ファイル付きのみ`をオンにできない問題を修正 -- Fix: deck uiでタイムラインを切り替えた際にTLの設定項目が更新されず、`TLに他の人への返信を含める`のトグルが表示されない問題を修正 -- Fix: ウィジェットのタイムライン選択欄に無効化されたタイムラインが表示される問題を修正 -- Fix: サウンドにドライブの音声を使用している際にドライブの音声が再生できなくなると設定が変更できなくなる問題を修正 +- ### Server -- Feat: レートリミット制限に引っかかったときに`Retry-After`ヘッダーを返すように (#13949) -- Enhance: エンドポイント`clips/update`の必須項目を`clipId`のみに -- Enhance: エンドポイント`admin/roles/update`の必須項目を`roleId`のみに -- Enhance: エンドポイント`pages/update`の必須項目を`pageId`のみに -- Enhance: エンドポイント`gallery/posts/update`の必須項目を`postId`のみに -- Enhance: エンドポイント`i/webhook/update`の必須項目を`webhookId`のみに -- Enhance: エンドポイント`admin/ad/update`の必須項目を`id`のみに -- Enhance: `default.yml`内の`url`, `db.db`, `db.user`, `db.pass`を環境変数から読み込めるように -- Enhance: エンドポイント`api/meta`にプロパティ`noteSearchableScope`が増え、`string`値`local`または`global`を返却します -- Fix: チャート生成時にinstance.suspensionStateに置き換えられたinstance.isSuspendedが参照されてしまう問題を修正 -- Fix: ユーザーのフィードページのMFMをHTMLに展開するように (#14006) -- Fix: アンテナ・クリップ・リスト・ウェブフックがロールポリシーの上限より一つ多く作れてしまうのを修正 (#14036) +- チャート生成時にinstance.suspentionStateに置き換えられたinstance.isSuspendedが参照されてしまう問題を修正 - Fix: notRespondingSinceが実装される前に不通になったインスタンスが自動的に配信停止にならない (#14059) -- Fix: FTT有効時、タイムライン用エンドポイントで`sinceId`にキャッシュ内最古のものより古いものを指定した場合に正しく結果が返ってこない問題を修正 -- Fix: 自分以外のクリップ内のノート個数が見えることがあるのを修正 -- Fix: 空文字列のリアクションはフォールバックされるように -- Fix: リノートにリアクションできないように -- Fix: ユーザー名の前後に空白文字列がある場合は省略するように -- Fix: プロフィール編集時に名前を空白文字列のみにできる問題を修正 -- Fix: ユーザ名のサジェスト時に表示される内容と順番を調整(以下の順番になります) #14149 - 1. フォロー中かつアクティブなユーザ - 2. フォロー中かつ非アクティブなユーザ - 3. フォローしていないアクティブなユーザ - 4. フォローしていない非アクティブなユーザ - - また、自分自身のアカウントもサジェストされるようになりました。 -- Fix: 一般ユーザーから見たユーザーのバッジの一覧に公開されていないものが含まれることがある問題を修正 - (Cherry-picked from https://github.com/MisskeyIO/misskey/pull/652) -- Fix: ユーザーのリアクション一覧でミュート/ブロックが機能していなかった問題を修正 -- Fix: FTT有効時にリモートユーザーのノートがHTLにキャッシュされる問題を修正 -- Fix: 一部の通知がローカル上のリモートユーザーに対して行われていた問題を修正 -- Fix: エラーメッセージの誤字を修正 (#14213) -- Fix: ソーシャルタイムラインにローカルタイムラインに表示される自分へのリプライが表示されない問題を修正 -- Fix: リノートのミュートが適用されるまでに時間がかかることがある問題を修正 - (Cherry-picked from https://github.com/Type4ny-Project/Type4ny/commit/e9601029b52e0ad43d9131b555b614e56c84ebc1) -- Fix: Steaming APIが不正なデータを受けた場合の動作が不安定である問題 #14251 -- Fix: `users/search`において `@` から始まる文字列が与えられた際の処理が正しくなかった問題を修正 - - 名前や自己紹介に `@` から始まる文言が含まれるユーザーも検索できるようになります -- Fix: 一部のMisskey以外のソフトウェアからファイルを受け取れない問題 - (Cherry-picked from https://github.com/Secineralyr/misskey.dream/pull/73/commits/652eaff1e8aa00b890d71d2e1e52c263c1e67c76) - - NOTE: `drive_file`の`url`, `uri`, `src`の上限が512から1024に変更されます - Migrationではカラム定義の変更のみが行われます。 - サーバー管理者は各サーバーの必要に応じ`drive_file` `("uri")`に対するインデックスを張りなおすことでより安定しDBの探索が行われる可能性があります。詳細 は [GitHub](https://github.com/misskey-dev/misskey/pull/14323#issuecomment-2257562228)で確認可能です -- Fix: 自分のフォロワー限定投稿に対するリプライがホームタイムラインで見えないことが有る問題を修正 -- Fix: フォローしていないユーザによるフォロワー限定投稿に対するリプライがソーシャルタイムラインで表示されることがある問題を修正 -- Fix: ActivityPubのエンティティタイプ判定で不明なタイプを受け取った場合でも処理を継続するように - - キュー処理のつまりが改善される可能性があります - -### Misskey.js -- Feat: `/drive/files/create` のリクエストに対応(`multipart/form-data`に対応) -- Feat: `/admin/role/create` のロールポリシーの型を修正 ## 2024.5.0 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f2e48ec61d..053fffcaeb 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -11,12 +11,10 @@ Before creating an issue, please check the following: - Issues should only be used to feature requests, suggestions, and bug tracking. - Please ask questions or troubleshooting in [Discord](https://discord.gg/6VgKmEqHNk). -> [!WARNING] +> **Warning** > Do not close issues that are about to be resolved. It should remain open until a commit that actually resolves it is merged. -### Recommended discussing before implementation -We welcome your proposal. - +## Before implementation When you want to add a feature or fix a bug, *please open an issue*, don't just start writing code. We may suggest different approaches, or show that the "bug" is actually intended behaviour (and offer @@ -27,20 +25,7 @@ Misskey. Each of these examples have actually happened! On the other hand, it's very likely that we'll tell you "go ahead!". We try our best to incorporate improvements from our users! -Also, when you start implementation, assign yourself to the Issue (if you cannot do it yourself, ask Committer to assign you). -By expressing your intention to work on the Issue, you can prevent conflicts in the work. - -To the Committers: you should not assign someone on it before the Final Decision. - -### How issues are triaged - -The Committers may: -* close an issue that is not reproducible on latest stable release, -* merge an issue into another issue, -* split an issue into multiple issues, -* or re-open that has been closed for some reason which is not applicable anymore. - -@syuilo reserves the Final Decision rights including whether the project will implement feature and how to implement, these rights are not always exercised. +Also, when you start implementation, assign yourself to the Issue (if you cannot do it yourself, ask another member to assign you). By expressing your intention to work the Issue, you can prevent conflicts in the work. ## Well-known branches - **`stable`** branch is tracking the latest release and used for production purposes. @@ -50,14 +35,14 @@ The Committers may: ## Creating a PR Thank you for your PR! Before creating a PR, please check the following: - If possible, prefix the title with a keyword that identifies the type of this PR, as shown below. - - `fix` / `refactor` / `feat` / `enhance` / `perf` / `chore` etc - - Also, make sure that the granularity of this PR is appropriate. Please do not include more than one type of change or interest in a single PR. + - `fix` / `refactor` / `feat` / `enhance` / `perf` / `chore` etc + - Also, make sure that the granularity of this PR is appropriate. Please do not include more than one type of change or interest in a single PR. - If there is an Issue which will be resolved by this PR, please include a reference to the Issue in the text. - Please add the summary of the changes to [`CHANGELOG.md`](CHANGELOG.md). However, this is not necessary for changes that do not affect the users, such as refactoring. - Check if there are any documents that need to be created or updated due to this change. - If you have added a feature or fixed a bug, please add a test case if possible. - Please make sure that tests and Lint are passed in advance. - - You can run it with `pnpm test` and `pnpm lint`. [See more info](#testing) + - You can run it with `pnpm test` and `pnpm lint`. [See more info](#testing) - If this PR includes UI changes, please attach a screenshot in the text. Thanks for your cooperation 🤗 @@ -67,8 +52,8 @@ Be willing to comment on the good points and not just the things you want fixed ### Review perspective - Scope - - Are the goals of the PR clear? - - Is the granularity of the PR appropriate? + - Are the goals of the PR clear? + - Is the granularity of the PR appropriate? - Security - Does merging this PR create a vulnerability? - Performance @@ -83,7 +68,7 @@ Be willing to comment on the good points and not just the things you want fixed ## Release ### Release Instructions -1. Commit version changes in the `develop` branch ([package.json](package.json)) +1. Commit version changes in the `develop` branch ([package.json](https://activitypub.software/TransFem-org/Sharkey/-/blob/develop/package.json)) 2. Create a release PR. - Into `stable` from `develop` branch. - The title must be in the format `Release: x.y.z`. @@ -94,7 +79,7 @@ Be willing to comment on the good points and not just the things you want fixed - The target branch must be `stable` - The tag name must be the version -> [!NOTE] +> **Note** > Why this instruction is necessary: > - To perform final QA checks > - To distribute responsibility @@ -121,42 +106,12 @@ When Updating the Font make sure to copy **all generated files** from the `dest` For the CSS simply copy the file content and replace the old content in `style.css` and for the WOFF, TTF and SVG simply replace them. ## Development -### Setup -Before developing, you have to set up environment. Misskey requires Redis, PostgreSQL, and FFmpeg. - -You would want to install Meilisearch to experiment related features. Technically, meilisearch is not strict requirement, but some features and tests require it. - -There are a few ways to proceed. - -#### Use system-wide software -You could install them in system-wide (such as from package manager). - -#### Use `docker compose` -You could obtain middleware container by typing `docker compose -f $PROJECT_ROOT/compose.local-db.yml up -d`. - -#### Use Devcontainer -Devcontainer also has necessary setting. This method can be done by connecting from VSCode. - -Instead of running `pnpm` locally, you can use Dev Container to set up your development environment. -To use Dev Container, open the project directory on VSCode with Dev Containers installed. -**Note:** If you are using Windows, please clone the repository with WSL. Using Git for Windows will result in broken files due to the difference in how newlines are handled. - -It will run the following command automatically inside the container. -``` bash -git submodule update --init -pnpm install --frozen-lockfile -cp .devcontainer/devcontainer.yml .config/default.yml -pnpm build -pnpm migrate -``` - -After finishing the migration, you can proceed. - -### Start developing During development, it is useful to use the + ``` pnpm dev ``` + command. - Server-side source files and automatically builds them if they are modified. Automatically start the server process(es). @@ -180,6 +135,26 @@ MK_DEV_PREFER=backend pnpm dev - To change the port of Vite, specify with `VITE_PORT` environment variable. - HMR may not work in some environments such as Windows. +### Dev Container +Instead of running `pnpm` locally, you can use Dev Container to set up your development environment. +To use Dev Container, open the project directory on VSCode with Dev Containers installed. +**Note:** If you are using Windows, please clone the repository with WSL. Using Git for Windows will result in broken files due to the difference in how newlines are handled. + +It will run the following command automatically inside the container. +``` bash +git submodule update --init +pnpm install --frozen-lockfile +cp .devcontainer/devcontainer.yml .config/default.yml +pnpm build +pnpm migrate +``` + +After finishing the migration, run the `pnpm dev` command to start the development server. + +``` bash +pnpm dev +``` + ## Testing - Test codes are located in [`/packages/backend/test`](packages/backend/test). @@ -190,7 +165,7 @@ cp .github/misskey/test.yml .config/ ``` Prepare DB/Redis for testing. ``` -docker compose -f packages/backend/test/compose.yml up +docker compose -f packages/backend/test/docker-compose.yml up ``` Alternatively, prepare an empty (data can be erased) DB and edit `.config/test.yml`. @@ -229,7 +204,7 @@ niraxは、Misskeyで使用しているオリジナルのフロントエンド ### ルート定義 ルート定義は、以下の形式のオブジェクトの配列です。 -```ts +``` ts { name?: string; path: string; @@ -242,7 +217,7 @@ niraxは、Misskeyで使用しているオリジナルのフロントエンド } ``` -> [!WARNING] +> **Warning** > 現状、ルートは定義された順に評価されます。 > たとえば、`/foo/:id`ルート定義の次に`/foo/bar`ルート定義がされていた場合、後者がマッチすることはありません。 @@ -304,7 +279,7 @@ export const Default = { parameters: { layout: 'centered', }, -} satisfies StoryObj; +} satisfies StoryObj; ``` If you want to opt-out from the automatic generation, create a `MyComponent.stories.impl.ts` file and add the following line to the file. @@ -415,7 +390,7 @@ describe('test', () => { }) .useMocker(... .compile(); - + fooService = app.get(FooService); barService = app.get(BarService) as jest.Mocked; @@ -529,21 +504,20 @@ enumの列挙の内容の削除は、その値をもつレコードを全て削 ### Migration作成方法 packages/backendで: ```sh -pnpm run build -pnpm dlx typeorm migration:generate -d ormconfig.js -o migration/ +pnpm dlx typeorm migration:generate -d ormconfig.js -o ``` - 生成後、ファイルをmigration下に移してください - 作成されたスクリプトは不必要な変更を含むため除去してください ### JSON SchemaのobjectでanyOfを使うとき -JSON Schemaで、objectに対してanyOfを使う場合、anyOfの中でpropertiesを定義しないこと。 -バリデーションが効かないため。(SchemaTypeもそのように作られており、objectのanyOf内のpropertiesは捨てられます) +JSON Schemaで、objectに対してanyOfを使う場合、anyOfの中でpropertiesを定義しないこと。 +バリデーションが効かないため。(SchemaTypeもそのように作られており、objectのanyOf内のpropertiesは捨てられます) https://github.com/misskey-dev/misskey/pull/10082 テキストhogeおよびfugaについて、片方を必須としつつ両方の指定もありうる場合: -```ts +``` export const paramDef = { type: 'object', properties: { @@ -574,26 +548,6 @@ marginはそのコンポーネントを使う側が設定する ### indexというファイル名を使うな ESMではディレクトリインポートは廃止されているのと、ディレクトリインポートせずともファイル名が index だと何故か一部のライブラリ?でディレクトリインポートだと見做されてエラーになる -## CSS Recipe - -### Lighten CSS vars - -``` css -color: hsl(from var(--accent) h s calc(l + 10)); -``` - -### Darken CSS vars - -``` css -color: hsl(from var(--accent) h s calc(l - 10)); -``` - -### Add alpha to CSS vars - -``` css -color: color(from var(--accent) srgb r g b / 0.5); -``` - ## Merging from Misskey into Sharkey Make sure you have both remotes in the same clone (`git remote add misskey @@ -610,46 +564,30 @@ seems to do a decent job) *after that commit*, do all the extra work, on the same branch: -* copy all changes (commit after each step): - * in - `packages/backend/src/core/activitypub/models/ApNoteService.ts`, - from `createNote` to `updateNote` - * from `packages/backend/src/core/NoteCreateService.ts` to - `packages/backend/src/core/NoteEditService.vue` - * from `packages/backend/src/server/api/endpoints/notes/create.ts` - to `packages/backend/src/server/api/endpoints/notes/edit.ts` - * from `packages/frontend/src/components/MkNote*.vue` to - `packages/frontend/src/components/SkNote*.vue` (if sensible) +* copy all changes: + * from `NoteCreateService.create` to `NoteCreateService.import` (and + vice versa if `git` got confused!) + * from `NoteCreateService` to `NoteEditService` + * from `ApNoteService.createNote` to `ApNoteService.updateNote` + * from `endoints/notes/create.ts` to `endoints/notes/edit.ts` + * from `MkNote*` to `SkNote*` (if sensible) * from the global timeline to the bubble timeline (`packages/backend/src/server/api/stream/channels/global-timeline.ts`, `packages/backend/src/server/api/stream/channels/bubble-timeline.ts`, - `packages/frontend/src/timelines.ts`, `packages/frontend/src/components/MkTimeline.vue`, `packages/frontend/src/pages/timeline.vue`, `packages/frontend/src/ui/deck/tl-column.vue`, `packages/frontend/src/widgets/WidgetTimeline.vue`) -* check the changes against our `develop` (`git diff develop`) and - against Misskey (`git diff misskey/develop`) -* re-generate `misskey-js` (`pnpm build-misskey-js-with-types`) and commit -* build the frontend: `rm -rf built/; NODE_ENV=development pnpm - --filter=frontend --filter=frontend-embed build` (the `development` - tells it to keep some of the original filenames in the built files) * make sure there aren't any new `ti-*` classes (Tabler Icons), and - replace them with appropriate `ph-*` ones (Phosphor Icons): - `grep -rP '["'\'']ti[ -](?!fw)' -- built/` should show you what to change. + replace them with appropriate `ph-*` ones (Phosphor Icons). + `git grep '["'\'']ti[ -](?!fw)'` should show you what to change. NOTE: `ti-fw` is a special class that's defined by Misskey, leave it alone - - after every change, re-build the frontend and check again, until - there are no more `ti-*` classes in the built files - - commit! -* double-check the new migration, that they won't conflict with our db - changes: `git diff develop -- packages/backend/migration/` -* `pnpm clean; pnpm build` -* run tests `pnpm --filter='!megalodon' test` (requires a test - database, [see above](#testing)) and fix as much as you can - * right now `megalodon` doesn't pass its tests, so we skip them +* re-generate `misskey-js`: `pnpm build-misskey-js-with-types` +* run tests `pnpm test` and fix as much as you can + * right now `megalodon` doesn't pass its tests, you probably need to + run `pnpm --filter=backend test` (requires a test database, [see + above](#testing)) and `pnpm --filter=frontend test` * run lint `pnpm --filter=backend lint` + `pnpm --filter=frontend eslint` and fix as much as you can diff --git a/Dockerfile b/Dockerfile index abee7fb098..b937c69cdb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ # syntax = docker/dockerfile:1.4 -ARG NODE_VERSION=20.16.0-alpine3.20 +ARG NODE_VERSION=20.12.2-alpine3.19 FROM node:${NODE_VERSION} as build @@ -23,10 +23,9 @@ RUN --mount=type=cache,target=/root/.local/share/pnpm/store,sharing=locked \ RUN pnpm build RUN node scripts/trim-deps.mjs RUN mv packages/frontend/assets sharkey-assets -RUN mv packages/frontend-embed/assets sharkey-embed-assets RUN --mount=type=cache,target=/root/.local/share/pnpm/store,sharing=locked \ pnpm prune -RUN rm -r node_modules packages/frontend packages/frontend-shared packages/frontend-embed packages/sw +RUN rm -r node_modules packages/frontend packages/sw RUN --mount=type=cache,target=/root/.local/share/pnpm/store,sharing=locked \ pnpm i --prod --frozen-lockfile --aggregate-output RUN rm -rf .git @@ -40,18 +39,12 @@ RUN apk add ffmpeg tini jemalloc \ && corepack enable \ && addgroup -g "${GID}" sharkey \ && adduser -D -u "${UID}" -G sharkey -h /sharkey sharkey \ - && mkdir /sharkey/files \ - && chown sharkey:sharkey /sharkey/files \ && find / -type d -path /sys -prune -o -type d -path /proc -prune -o -type f -perm /u+s -exec chmod u-s {} \; \ && find / -type d -path /sys -prune -o -type d -path /proc -prune -o -type f -perm /g+s -exec chmod g-s {} \; USER sharkey WORKDIR /sharkey -# add package.json to add pnpm -COPY --chown=sharkey:sharkey ./package.json ./package.json -RUN corepack install - COPY --chown=sharkey:sharkey --from=build /sharkey/node_modules ./node_modules COPY --chown=sharkey:sharkey --from=build /sharkey/packages/backend/node_modules ./packages/backend/node_modules COPY --chown=sharkey:sharkey --from=build /sharkey/packages/misskey-js/node_modules ./packages/misskey-js/node_modules @@ -67,8 +60,8 @@ COPY --chown=sharkey:sharkey --from=build /sharkey/packages/megalodon/lib ./pack COPY --chown=sharkey:sharkey --from=build /sharkey/fluent-emojis ./fluent-emojis COPY --chown=sharkey:sharkey --from=build /sharkey/tossface-emojis/dist ./tossface-emojis/dist COPY --chown=sharkey:sharkey --from=build /sharkey/sharkey-assets ./packages/frontend/assets -COPY --chown=sharkey:sharkey --from=build /sharkey/sharkey-embed-assets ./packages/frontend-embed/assets +COPY --chown=sharkey:sharkey package.json ./package.json COPY --chown=sharkey:sharkey pnpm-workspace.yaml ./pnpm-workspace.yaml COPY --chown=sharkey:sharkey packages/backend/package.json ./packages/backend/package.json COPY --chown=sharkey:sharkey packages/backend/scripts/check_connect.js ./packages/backend/scripts/check_connect.js diff --git a/README.md b/README.md index f9198c06c0..2407bee3f0 100644 --- a/README.md +++ b/README.md @@ -16,8 +16,8 @@ join the community - - donate + + donate --- diff --git a/UPGRADE_NOTES.md b/UPGRADE_NOTES.md deleted file mode 100644 index c941de6643..0000000000 --- a/UPGRADE_NOTES.md +++ /dev/null @@ -1,74 +0,0 @@ -# Upgrade Notes - -## 2024.10.0 - -### Hellspawns - -Sharkey versions before 2024.10 suffered from a bug in the "Mark instance as NSFW" feature. -When a user from such an instance boosted a note, the boost would be converted to a hellspawn (pure renote with Content Warning). -Hellspawns are buggy and do not properly federate, so it may be desirable to correct any that already exist in the database. -The following script will correct any local or remote hellspawns in the database. - -```postgresql -/* Remove "instance is marked as NSFW" hellspawns */ -UPDATE "note" -SET "cw" = null -WHERE - "renoteId" IS NOT NULL - AND "text" IS NULL - AND "cw" = 'Instance is marked as NSFW' - AND "replyId" IS NULL - AND "hasPoll" = false - AND "fileIds" = '{}'; - -/* Fix legacy / user-created hellspawns */ -UPDATE "note" -SET "text" = '.' -WHERE - "renoteId" IS NOT NULL - AND "text" IS NULL - AND "cw" IS NOT NULL - AND "replyId" IS NULL - AND "hasPoll" = false - AND "fileIds" = '{}'; -``` - -## 2024.9.0 - -### Following Feed - -When upgrading an existing instance to version 2024.9.0, the Following Feed will initially be empty. -The feed will gradually fill as new posts federate, but it may be desirable to back-fill the feed with existing data. -This database script will populate the feed with the latest post of each type for all users, ensuring that data is fully populated after the update. -Run this after migrations but before starting the instance. -Warning: the script may take a long time to execute! - -```postgresql -INSERT INTO latest_note (user_id, note_id, is_public, is_reply, is_quote) -SELECT - "userId" as user_id, - id as note_id, - visibility = 'public' AS is_public, - "replyId" IS NOT NULL AS is_reply, - ( - "renoteId" IS NOT NULL - AND ( - text IS NOT NULL - OR cw IS NOT NULL - OR "replyId" IS NOT NULL - OR "hasPoll" - OR "fileIds" != '{}' - ) - ) AS is_quote -FROM note -WHERE ( -- Exclude pure renotes (boosts) - "renoteId" IS NULL - OR text IS NOT NULL - OR cw IS NOT NULL - OR "replyId" IS NOT NULL - OR "hasPoll" - OR "fileIds" != '{}' - ) -ORDER BY id DESC -- This part is very important: it ensures that we only load the *latest* notes of each type. Do not remove it! -ON CONFLICT DO NOTHING; -- Any conflicts are guaranteed to be older notes that we can ignore. -``` diff --git a/chart/files/default.yml b/chart/files/default.yml index 97201aad66..aab7ed6ce1 100644 --- a/chart/files/default.yml +++ b/chart/files/default.yml @@ -124,14 +124,6 @@ redis: # #prefix: example-prefix # #db: 1 -#redisForReactions: -# host: redis -# port: 6379 -# #family: 0 # 0=Both, 4=IPv4, 6=IPv6 -# #pass: example-pass -# #prefix: example-prefix -# #db: 1 - # ┌───────────────────────────┐ #───┘ MeiliSearch configuration └───────────────────────────── diff --git a/crowdin.yml b/crowdin.yml index 0525ac7b0b..774ddc7a63 100644 --- a/crowdin.yml +++ b/crowdin.yml @@ -1,4 +1,4 @@ files: - - source: /sharkey-locales/en-US.yml - translation: /sharkey-locales/%locale%.yml + - source: /locales/ja-JP.yml + translation: /locales/%locale%.yml update_option: update_as_unapproved diff --git a/compose.local-db.yml b/docker-compose.local-db.yml similarity index 98% rename from compose.local-db.yml rename to docker-compose.local-db.yml index 3835cb23db..16ba4b49e1 100644 --- a/compose.local-db.yml +++ b/docker-compose.local-db.yml @@ -1,3 +1,5 @@ +version: "3" + # このconfigは、 dockerでMisskey本体を起動せず、 redisとpostgresql などだけを起動します services: diff --git a/compose_example.yml b/docker-compose_example.yml similarity index 91% rename from compose_example.yml rename to docker-compose_example.yml index 0db8b04dc6..647f6f0c77 100644 --- a/compose_example.yml +++ b/docker-compose_example.yml @@ -1,3 +1,5 @@ +version: "3" + services: web: # image: registry.activitypub.software/transfem-org/sharkey:latest @@ -17,8 +19,6 @@ services: - "3000:3000" networks: - shonk - # env_file: - # - .config/docker.env volumes: - ./files:/sharkey/files - ./.config:/sharkey/.config:ro @@ -53,7 +53,8 @@ services: # restart: always # image: mcaptcha/mcaptcha:latest # networks: -# shonk: +# internal_network: +# external_network: # aliases: # - localhost # ports: @@ -63,8 +64,6 @@ services: # environment: # PORT: 7493 # MCAPTCHA_redis_URL: "redis://mcaptcha_redis/" -# MCAPTCHA_allow_registration: true -# MCAPTCHA_server_DOMAIN: "example.tld" # depends_on: # db: # condition: service_healthy @@ -74,7 +73,7 @@ services: # mcaptcha_redis: # image: mcaptcha/cache:latest # networks: -# - shonk +# - internal_network # healthcheck: # test: "redis-cli ping" # interval: 5s diff --git a/eslint/locale.js b/eslint/locale.js deleted file mode 100644 index dbb807b714..0000000000 --- a/eslint/locale.js +++ /dev/null @@ -1,251 +0,0 @@ -/* - * SPDX-FileCopyrightText: dakkar and other Sharkey contributors - * SPDX-License-Identifier: AGPL-3.0-only -*/ - -/* This is a ESLint rule to report use of the `i18n.ts` and `i18n.tsx` - * objects that reference translation items that don't actually exist - * in the lexicon (the `locale/` files) - */ - -/* given a MemberExpression node, collects all the member names - * - * e.g. for a bit of code like `foo=one.two.three`, `collectMembers` - * called on the node for `three` would return `['one', 'two', - * 'three']` - */ -function collectMembers(node) { - if (!node) return []; - if (node.type !== 'MemberExpression') return []; - // this is something like `foo[bar]` - if (node.computed) return []; - return [ node.property.name, ...collectMembers(node.parent) ]; -} - -/* given an object and an array of names, recursively descends the - * object via those names - * - * e.g. `walkDown({one:{two:{three:15}}},['one','two','three'])` would - * return 15 - */ -function walkDown(locale, path) { - if (!locale) return null; - if (!path || path.length === 0 || !path[0]) return locale; - return walkDown(locale[path[0]], path.slice(1)); -} - -/* given a MemberExpression node, returns its attached CallExpression - * node if present - * - * e.g. for a bit of code like `foo=one.two.three()`, - * `findCallExpression` called on the node for `three` would return - * the node for function call (which is the parent of the `one` and - * `two` nodes, and holds the nodes for the argument list) - * - * if the code had been `foo=one.two.three`, `findCallExpression` - * would have returned null, because there's no function call attached - * to the MemberExpressions - */ -function findCallExpression(node) { - if (!node.parent) return null; - - // the second half of this guard protects from cases like - // `foo(one.two.three)` where the CallExpression is parent of the - // MemberExpressions, but via `arguments`, not `callee` - if (node.parent.type === 'CallExpression' && node.parent.callee === node) return node.parent; - if (node.parent.type === 'MemberExpression') return findCallExpression(node.parent); - return null; -} - -// same, but for Vue expressions (``) -function findVueExpression(node) { - if (!node.parent) return null; - - if (node.parent.type.match(/^VExpr/) && node.parent.expression === node) return node.parent; - if (node.parent.type === 'MemberExpression') return findVueExpression(node.parent); - return null; -} - -function areArgumentsOneObject(node) { - return node.arguments.length === 1 && - node.arguments[0].type === 'ObjectExpression'; -} - -// only call if `areArgumentsOneObject(node)` is true -function getArgumentObjectProperties(node) { - return new Set(node.arguments[0].properties.map( - p => { - if (p.key && p.key.type === 'Identifier') return p.key.name; - return null; - }, - )); -} - -function getTranslationParameters(translation) { - return new Set(Array.from(translation.matchAll(/\{(\w+)\}/g)).map( m => m[1] )); -} - -function setDifference(a,b) { - const result = []; - for (const element of a.values()) { - if (!b.has(element)) { - result.push(element); - } - } - - return result; -} - -/* the actual rule body - */ -function theRuleBody(context,node) { - // we get the locale/translations via the options; it's the data - // that goes into a specific language's JSON file, see - // `scripts/build-assets.mjs` - const locale = context.options[0]; - - // sometimes we get MemberExpression nodes that have a - // *descendent* with the right identifier: skip them, we'll get - // the right ones as well - if (node.object?.name !== 'i18n') { - return; - } - - // `method` is going to be `'ts'` or `'tsx'`, `path` is going to - // be the various translation steps/names - const [ method, ...path ] = collectMembers(node); - const pathStr = `i18n.${method}.${path.join('.')}`; - - // does that path point to a real translation? - const translation = walkDown(locale, path); - if (!translation) { - context.report({ - node, - message: `translation missing for ${pathStr}`, - }); - return; - } - - // we hit something weird, assume the programmers know what - // they're doing (this is usually some complicated slicing of - // the translation structure) - if (typeof(translation) !== 'string') return; - - const callExpression = findCallExpression(node); - const vueExpression = findVueExpression(node); - - // some more checks on how the translation is called - if (method === 'ts') { - // the ` component gets parametric translations via - // `i18n.ts.*`, but we error out elsewhere - if (translation.match(/\{/) && !vueExpression) { - context.report({ - node, - message: `translation for ${pathStr} is parametric, but called via 'ts'`, - }); - return; - } - - if (callExpression) { - context.report({ - node, - message: `translation for ${pathStr} is not parametric, but is called as a function`, - }); - } - } - - if (method === 'tsx') { - if (!translation.match(/\{/)) { - context.report({ - node, - message: `translation for ${pathStr} is not parametric, but called via 'tsx'`, - }); - return; - } - - if (!callExpression && !vueExpression) { - context.report({ - node, - message: `translation for ${pathStr} is parametric, but not called as a function`, - }); - return; - } - - // we're not currently checking arguments when used via the - // `` component, because it's too complicated (also, it - // would have to be done inside the `if (method === 'ts')`) - if (!callExpression) return; - - if (!areArgumentsOneObject(callExpression)) { - context.report({ - node, - message: `translation for ${pathStr} should be called with a single object as argument`, - }); - return; - } - - const translationParameters = getTranslationParameters(translation); - const parameterCount = translationParameters.size; - const callArguments = getArgumentObjectProperties(callExpression); - const argumentCount = callArguments.size; - - if (parameterCount !== argumentCount) { - context.report({ - node, - message: `translation for ${pathStr} has ${parameterCount} parameters, but is called with ${argumentCount} arguments`, - }); - } - - // node 20 doesn't have `Set.difference`... - const extraArguments = setDifference(callArguments, translationParameters); - const missingArguments = setDifference(translationParameters, callArguments); - - if (extraArguments.length > 0) { - context.report({ - node, - message: `translation for ${pathStr} passes unused arguments ${extraArguments.join(' ')}`, - }); - } - - if (missingArguments.length > 0) { - context.report({ - node, - message: `translation for ${pathStr} does not pass arguments ${missingArguments.join(' ')}`, - }); - } - } -} - -function theRule(context) { - // we get the locale/translations via the options; it's the data - // that goes into a specific language's JSON file, see - // `scripts/build-assets.mjs` - const locale = context.options[0]; - - // for all object member access that have an identifier 'i18n'... - return context.getSourceCode().parserServices.defineTemplateBodyVisitor( - { - // this is for -
+
@@ -63,7 +63,7 @@ SPDX-License-Identifier: AGPL-3.0-only - diff --git a/packages/frontend/src/components/MkDonation.stories.impl.ts b/packages/frontend/src/components/MkDonation.stories.impl.ts deleted file mode 100644 index 27d6b7df6c..0000000000 --- a/packages/frontend/src/components/MkDonation.stories.impl.ts +++ /dev/null @@ -1,54 +0,0 @@ -/* - * SPDX-FileCopyrightText: syuilo and misskey-project - * SPDX-License-Identifier: AGPL-3.0-only - */ - -import { action } from '@storybook/addon-actions'; -import { StoryObj } from '@storybook/vue3'; -import { onBeforeUnmount } from 'vue'; -import MkDonation from './MkDonation.vue'; -import { instance } from '@/instance.js'; -export const Default = { - render(args) { - return { - components: { - MkDonation, - }, - setup() { - return { - args, - }; - }, - computed: { - props() { - return { - ...this.args, - }; - }, - events() { - return { - closed: action('closed'), - }; - }, - }, - template: '', - }; - }, - args: { - // @ts-expect-error name is used for mocking instance - name: 'Misskey Hub', - }, - decorators: [ - (_, { args }) => ({ - setup() { - // @ts-expect-error name is used for mocking instance - instance.name = args.name; - onBeforeUnmount(() => instance.name = null); - }, - template: '', - }), - ], - parameters: { - layout: 'centered', - }, -} satisfies StoryObj; diff --git a/packages/frontend/src/components/MkDonation.vue b/packages/frontend/src/components/MkDonation.vue index 1dfdebf0d4..e96980dbbd 100644 --- a/packages/frontend/src/components/MkDonation.vue +++ b/packages/frontend/src/components/MkDonation.vue @@ -23,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only
- {{ i18n.ts.learnMore }} + {{ i18n.ts.learnMore }}
@@ -48,7 +48,7 @@ SPDX-License-Identifier: AGPL-3.0-only - - diff --git a/packages/frontend/src/components/MkEmojiPicker.section.stories.impl.ts b/packages/frontend/src/components/MkEmojiPicker.section.stories.impl.ts deleted file mode 100644 index 69aef577de..0000000000 --- a/packages/frontend/src/components/MkEmojiPicker.section.stories.impl.ts +++ /dev/null @@ -1,7 +0,0 @@ -/* - * SPDX-FileCopyrightText: syuilo and misskey-project - * SPDX-License-Identifier: AGPL-3.0-only - */ - -import MkEmojiPicker_section from './MkEmojiPicker.section.vue'; -void MkEmojiPicker_section; diff --git a/packages/frontend/src/components/MkEmojiPicker.section.vue b/packages/frontend/src/components/MkEmojiPicker.section.vue index 151843b18c..008613c27e 100644 --- a/packages/frontend/src/components/MkEmojiPicker.section.vue +++ b/packages/frontend/src/components/MkEmojiPicker.section.vue @@ -62,7 +62,7 @@ SPDX-License-Identifier: AGPL-3.0-only @@ -39,10 +39,6 @@ const props = defineProps<{ color: var(--accent); } - &:focus-visible { - outline-offset: -2px; - } - > article { padding: 16px; @@ -91,12 +87,6 @@ const props = defineProps<{ } } - &:global(.gray) { - --c: var(--bg); - background-image: linear-gradient(45deg, var(--c) 16.67%, transparent 16.67%, transparent 50%, var(--c) 50%, var(--c) 66.67%, transparent 66.67%, transparent 100%); - background-size: 16px 16px; - } - @media (max-width: 700px) { } diff --git a/packages/frontend/src/components/MkFolder.vue b/packages/frontend/src/components/MkFolder.vue index 392963fdb9..dbe3db9eac 100644 --- a/packages/frontend/src/components/MkFolder.vue +++ b/packages/frontend/src/components/MkFolder.vue @@ -7,10 +7,10 @@ SPDX-License-Identifier: AGPL-3.0-only
@@ -41,9 +41,6 @@ SPDX-License-Identifier: AGPL-3.0-only -
- -
@@ -139,7 +136,7 @@ onMounted(() => { width: 100%; box-sizing: border-box; padding: 9px 12px 9px 12px; - background: var(--folderHeaderBg); + background: var(--buttonBg); -webkit-backdrop-filter: var(--blur, blur(15px)); backdrop-filter: var(--blur, blur(15px)); border-radius: var(--radius-sm); @@ -147,16 +144,12 @@ onMounted(() => { &:hover { text-decoration: none; - background: var(--folderHeaderHoverBg); - } - - &:focus-within { - outline-offset: 2px; + background: var(--buttonHoverBg); } &.active { color: var(--accent); - background: var(--folderHeaderHoverBg); + background: var(--buttonHoverBg); } &.opened { @@ -197,12 +190,6 @@ onMounted(() => { padding-right: 12px; } -.headerTextMain, -.headerTextSub { - width: fit-content; - max-width: 100%; -} - .headerTextSub { color: var(--fgTransparentWeak); font-size: .85em; @@ -227,18 +214,4 @@ onMounted(() => { background: var(--bg); } } - -.footer { - position: sticky !important; - z-index: 1; - bottom: var(--stickyBottom, 0px); - left: 0; - padding: 12px; - background: var(--acrylicBg); - -webkit-backdrop-filter: var(--blur, blur(15px)); - backdrop-filter: var(--blur, blur(15px)); - background-size: auto auto; - background-image: repeating-linear-gradient(135deg, transparent, transparent 5px, var(--panel) 5px, var(--panel) 10px); - border-radius: 0 0 6px 6px; -} diff --git a/packages/frontend/src/components/MkFollowButton.vue b/packages/frontend/src/components/MkFollowButton.vue index 52497a2994..ba0527e570 100644 --- a/packages/frontend/src/components/MkFollowButton.vue +++ b/packages/frontend/src/components/MkFollowButton.vue @@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only {{ i18n.ts.processing }} @@ -23,8 +23,8 @@ SPDX-License-Identifier: AGPL-3.0-only diff --git a/packages/frontend/src/components/MkMediaAudio.vue b/packages/frontend/src/components/MkMediaAudio.vue index 2d7cde1af2..3b5d50315e 100644 --- a/packages/frontend/src/components/MkMediaAudio.vue +++ b/packages/frontend/src/components/MkMediaAudio.vue @@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only @contextmenu.stop @keydown.stop > - @@ -57,22 +52,13 @@ SPDX-License-Identifier: AGPL-3.0-only
-
{{ hms(elapsedTimeMs) }}
- @@ -97,7 +83,6 @@ import type { MenuItem } from '@/types/menu.js'; import { defaultStore } from '@/store.js'; import { i18n } from '@/i18n.js'; import * as os from '@/os.js'; -import { type Keymap } from '@/scripts/hotkey.js'; import bytes from '@/filters/bytes.js'; import { hms } from '@/filters/hms.js'; import MkMediaRange from '@/components/MkMediaRange.vue'; @@ -108,44 +93,32 @@ const props = defineProps<{ }>(); const keymap = { - 'up': { - allowRepeat: true, - callback: () => { - if (hasFocus() && audioEl.value) { - volume.value = Math.min(volume.value + 0.1, 1); - } - }, + 'up': () => { + if (hasFocus() && audioEl.value) { + volume.value = Math.min(volume.value + 0.1, 1); + } }, - 'down': { - allowRepeat: true, - callback: () => { - if (hasFocus() && audioEl.value) { - volume.value = Math.max(volume.value - 0.1, 0); - } - }, + 'down': () => { + if (hasFocus() && audioEl.value) { + volume.value = Math.max(volume.value - 0.1, 0); + } }, - 'left': { - allowRepeat: true, - callback: () => { - if (hasFocus() && audioEl.value) { - audioEl.value.currentTime = Math.max(audioEl.value.currentTime - 5, 0); - } - }, + 'left': () => { + if (hasFocus() && audioEl.value) { + audioEl.value.currentTime = Math.max(audioEl.value.currentTime - 5, 0); + } }, - 'right': { - allowRepeat: true, - callback: () => { - if (hasFocus() && audioEl.value) { - audioEl.value.currentTime = Math.min(audioEl.value.currentTime + 5, audioEl.value.duration); - } - }, + 'right': () => { + if (hasFocus() && audioEl.value) { + audioEl.value.currentTime = Math.min(audioEl.value.currentTime + 5, audioEl.value.duration); + } }, 'space': () => { if (hasFocus()) { togglePlayPause(); } }, -} as const satisfies Keymap; +}; // PlayerElもしくはその子要素にフォーカスがあるかどうか function hasFocus() { @@ -156,26 +129,16 @@ function hasFocus() { const playerEl = shallowRef(); const audioEl = shallowRef(); -// eslint-disable-next-line vue/no-setup-props-reactivity-loss +// eslint-disable-next-line vue/no-setup-props-destructure const hide = ref((defaultStore.state.nsfw === 'force' || defaultStore.state.dataSaver.media) ? true : (props.audio.isSensitive && defaultStore.state.nsfw !== 'ignore')); -async function show() { - if (props.audio.isSensitive && defaultStore.state.confirmWhenRevealingSensitiveMedia) { - const { canceled } = await os.confirm({ - type: 'question', - text: i18n.ts.sensitiveMediaRevealConfirm, - }); - if (canceled) return; - } - - hide.value = false; -} - // Menu const menuShowing = ref(false); function showMenu(ev: MouseEvent) { - const menu: MenuItem[] = [ + let menu: MenuItem[] = []; + + menu = [ // TODO: 再生キューに追加 { type: 'switch', @@ -223,7 +186,7 @@ function showMenu(ev: MouseEvent) { menu.push({ type: 'divider', }, { - type: 'link', + type: 'link' as const, text: i18n.ts._fileViewer.title, icon: 'ti ti-info-circle', to: `/my/drive/file/${props.audio.id}`, @@ -398,7 +361,7 @@ onDeactivated(() => { border-radius: var(--radius); overflow: clip; - &:focus-visible { + &:focus { outline: none; } } @@ -464,10 +427,6 @@ onDeactivated(() => { color: var(--accent); background-color: var(--accentedBg); } - - &:focus-visible { - outline: none; - } } } diff --git a/packages/frontend/src/components/MkMediaBanner.vue b/packages/frontend/src/components/MkMediaBanner.vue index 77a86ff2fb..78979b3f47 100644 --- a/packages/frontend/src/components/MkMediaBanner.vue +++ b/packages/frontend/src/components/MkMediaBanner.vue @@ -5,12 +5,12 @@ SPDX-License-Identifier: AGPL-3.0-only diff --git a/packages/frontend/src/components/SkFollowingRecentNotes.vue b/packages/frontend/src/components/SkFollowingRecentNotes.vue deleted file mode 100644 index 6daa8feba5..0000000000 --- a/packages/frontend/src/components/SkFollowingRecentNotes.vue +++ /dev/null @@ -1,144 +0,0 @@ - - - - - - - diff --git a/packages/frontend/src/components/SkFormula.vue b/packages/frontend/src/components/SkFormula.vue index 6faf36da55..039cef8da8 100644 --- a/packages/frontend/src/components/SkFormula.vue +++ b/packages/frontend/src/components/SkFormula.vue @@ -4,26 +4,26 @@ SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/components/form/link.vue b/packages/frontend/src/components/form/link.vue index f5546edf1e..164606e1f9 100644 --- a/packages/frontend/src/components/form/link.vue +++ b/packages/frontend/src/components/form/link.vue @@ -60,18 +60,18 @@ const props = defineProps<{ width: 100%; box-sizing: border-box; padding: 10px 14px; - background: var(--folderHeaderBg); + background: var(--buttonBg); border-radius: var(--radius-sm); font-size: 0.9em; &:hover { text-decoration: none; - background: var(--folderHeaderHoverBg); + background: var(--buttonHoverBg); } &.active { color: var(--accent); - background: var(--folderHeaderHoverBg); + background: var(--buttonHoverBg); } } diff --git a/packages/frontend/src/components/global/MkA.stories.impl.ts b/packages/frontend/src/components/global/MkA.stories.impl.ts index 02e5a7f98c..c1d8cf0ca6 100644 --- a/packages/frontend/src/components/global/MkA.stories.impl.ts +++ b/packages/frontend/src/components/global/MkA.stories.impl.ts @@ -35,10 +35,12 @@ export const Default = { // FIXME: 通るけどその後落ちるのでコメントアウト // await expect(a.href).toMatch(/^https?:\/\/.*#test$/); await userEvent.pointer({ keys: '[MouseRight]', target: a }); + await tick(); const menu = canvas.getByRole('menu'); await expect(menu).toBeInTheDocument(); await userEvent.click(a); a.blur(); + await tick(); await expect(menu).not.toBeInTheDocument(); }, args: { diff --git a/packages/frontend/src/components/global/MkA.vue b/packages/frontend/src/components/global/MkA.vue index 23f049ebb4..6cb948d3dd 100644 --- a/packages/frontend/src/components/global/MkA.vue +++ b/packages/frontend/src/components/global/MkA.vue @@ -16,8 +16,8 @@ export type MkABehavior = 'window' | 'browser' | null; diff --git a/packages/frontend/src/components/global/MkEmoji.vue b/packages/frontend/src/components/global/MkEmoji.vue index bd9b1d665a..9cf7dab06d 100644 --- a/packages/frontend/src/components/global/MkEmoji.vue +++ b/packages/frontend/src/components/global/MkEmoji.vue @@ -10,14 +10,13 @@ SPDX-License-Identifier: AGPL-3.0-only diff --git a/packages/frontend/src/components/global/MkMfm.stories.impl.ts b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.stories.impl.ts similarity index 78% rename from packages/frontend/src/components/global/MkMfm.stories.impl.ts rename to packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.stories.impl.ts index 1daf7a29cb..730351f795 100644 --- a/packages/frontend/src/components/global/MkMfm.stories.impl.ts +++ b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.stories.impl.ts @@ -2,15 +2,16 @@ * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ - + +/* eslint-disable @typescript-eslint/explicit-function-return-type */ import { StoryObj } from '@storybook/vue3'; import { expect, within } from '@storybook/test'; -import MkMfm from './MkMfm.js'; +import MkMisskeyFlavoredMarkdown from './MkMisskeyFlavoredMarkdown.js'; export const Default = { render(args) { return { components: { - MkMfm, + MkMisskeyFlavoredMarkdown, }, setup() { return { @@ -24,7 +25,7 @@ export const Default = { }; }, }, - template: '', + template: '', }; }, async play({ canvasElement, args }) { @@ -53,25 +54,25 @@ export const Default = { parameters: { layout: 'centered', }, -} satisfies StoryObj; +} satisfies StoryObj; export const Plain = { ...Default, args: { ...Default.args, plain: true, }, -} satisfies StoryObj; +} satisfies StoryObj; export const Nowrap = { ...Default, args: { ...Default.args, nowrap: true, }, -} satisfies StoryObj; +} satisfies StoryObj; export const IsNotNote = { ...Default, args: { ...Default.args, isNote: false, }, -} satisfies StoryObj; +} satisfies StoryObj; diff --git a/packages/frontend/src/components/global/MkMfm.ts b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts similarity index 97% rename from packages/frontend/src/components/global/MkMfm.ts rename to packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts index 9bf9f4a872..ac563ffaf5 100644 --- a/packages/frontend/src/components/global/MkMfm.ts +++ b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts @@ -18,15 +18,10 @@ import MkCodeInline from '@/components/MkCodeInline.vue'; import MkGoogle from '@/components/MkGoogle.vue'; import MkSparkle from '@/components/MkSparkle.vue'; import MkA, { MkABehavior } from '@/components/global/MkA.vue'; -import { host } from '@@/js/config.js'; +import { host } from '@/config.js'; import { defaultStore } from '@/store.js'; - -function safeParseFloat(str: unknown): number | null { - if (typeof str !== 'string' || str === '') return null; - const num = parseFloat(str); - if (isNaN(num)) return null; - return num; -} +import { nyaize as doNyaize } from '@/scripts/nyaize.js'; +import { safeParseFloat } from '@/scripts/safe-parse.js'; const QUOTE_STYLE = ` display: block; @@ -63,7 +58,8 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext { if (t == null) return null; if (typeof t === 'boolean') return null; - return t.match(/^\-?[0-9.]+s$/) ? t : null; + return t.match(/^[0-9.]+s$/) ? t : null; }; const useAnim = defaultStore.state.advancedMfm && defaultStore.state.animatedMfm ? true : props.isAnim ? true : false; @@ -97,7 +93,7 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext
-
+
@@ -25,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only - - diff --git a/packages/frontend/src/components/index.ts b/packages/frontend/src/components/index.ts index b36625ed1b..44d8d59941 100644 --- a/packages/frontend/src/components/index.ts +++ b/packages/frontend/src/components/index.ts @@ -5,7 +5,7 @@ import { App } from 'vue'; -import Mfm from './global/MkMfm.js'; +import Mfm from './global/MkMisskeyFlavoredMarkdown.js'; import MkA from './global/MkA.vue'; import MkAcct from './global/MkAcct.vue'; import MkAvatar from './global/MkAvatar.vue'; diff --git a/packages/frontend/src/config.ts b/packages/frontend/src/config.ts new file mode 100644 index 0000000000..e3922a0cd5 --- /dev/null +++ b/packages/frontend/src/config.ts @@ -0,0 +1,27 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { miLocalStorage } from '@/local-storage.js'; + +const address = new URL(document.querySelector('meta[property="instance_url"]')?.content || location.href); +const siteName = document.querySelector('meta[property="og:site_name"]')?.content; + +export const host = address.host; +export const hostname = address.hostname; +export const url = address.origin; +export const apiUrl = location.origin + '/api'; +export const wsOrigin = location.origin; +export const lang = miLocalStorage.getItem('lang') ?? 'en-US'; +export const langs = _LANGS_; +const preParseLocale = miLocalStorage.getItem('locale'); +export let locale = preParseLocale ? JSON.parse(preParseLocale) : null; +export const version = _VERSION_; +export const instanceName = siteName === 'Sharkey' || siteName == null ? host : siteName; +export const ui = miLocalStorage.getItem('ui'); +export const debug = miLocalStorage.getItem('debug') === 'true'; + +export function updateLocale(newLocale): void { + locale = newLocale; +} diff --git a/packages/frontend-shared/js/const.ts b/packages/frontend/src/const.ts similarity index 90% rename from packages/frontend-shared/js/const.ts rename to packages/frontend/src/const.ts index 8236cc9a4d..f9ee820065 100644 --- a/packages/frontend-shared/js/const.ts +++ b/packages/frontend/src/const.ts @@ -118,10 +118,8 @@ export const notificationTypes = [ 'followRequestAccepted', 'roleAssigned', 'achievementEarned', - 'exportCompleted', - 'test', 'app', - 'edited', + 'edited' ] as const; export const obsoleteNotificationTypes = ['pollVote', 'groupInvited'] as const; @@ -143,7 +141,6 @@ export const ROLE_POLICIES = [ 'canHideAds', 'driveCapacityMb', 'alwaysMarkNsfw', - 'canUpdateBioMedia', 'pinLimit', 'antennaLimit', 'wordMuteLimit', @@ -154,11 +151,6 @@ export const ROLE_POLICIES = [ 'userEachUserListsLimit', 'rateLimitFactor', 'avatarDecorationLimit', - 'canImportAntennas', - 'canImportBlocking', - 'canImportFollowing', - 'canImportMuting', - 'canImportUserLists', ] as const; // なんか動かない @@ -167,9 +159,9 @@ export const ROLE_POLICIES = [ export const CURRENT_STICKY_TOP = 'CURRENT_STICKY_TOP'; export const CURRENT_STICKY_BOTTOM = 'CURRENT_STICKY_BOTTOM'; -export const DEFAULT_SERVER_ERROR_IMAGE_URL = '/client-assets/status/error.png'; -export const DEFAULT_NOT_FOUND_IMAGE_URL = '/client-assets/status/missingpage.webp'; -export const DEFAULT_INFO_IMAGE_URL = '/client-assets/status/nothinghere.png'; +export const DEFAULT_SERVER_ERROR_IMAGE_URL = 'https://launcher.moe/error.png'; +export const DEFAULT_NOT_FOUND_IMAGE_URL = 'https://launcher.moe/missingpage.webp'; +export const DEFAULT_INFO_IMAGE_URL = 'https://launcher.moe/nothinghere.png'; export const MFM_TAGS = ['tada', 'jelly', 'twitch', 'shake', 'spin', 'jump', 'bounce', 'flip', 'x2', 'x3', 'x4', 'scale', 'position', 'fg', 'bg', 'border', 'font', 'blur', 'rainbow', 'sparkle', 'rotate', 'ruby', 'unixtime', 'crop', 'fade', 'followmouse']; export const MFM_PARAMS: Record = { diff --git a/packages/frontend/src/custom-emojis.ts b/packages/frontend/src/custom-emojis.ts index 0d03282cee..9da3582e1a 100644 --- a/packages/frontend/src/custom-emojis.ts +++ b/packages/frontend/src/custom-emojis.ts @@ -6,6 +6,7 @@ import { shallowRef, computed, markRaw, watch } from 'vue'; import * as Misskey from 'misskey-js'; import { misskeyApi, misskeyApiGet } from '@/scripts/misskey-api.js'; +import { useStream } from '@/stream.js'; import { get, set } from '@/scripts/idb-proxy.js'; const storageCache = await get('emojis'); @@ -28,20 +29,23 @@ watch(customEmojis, emojis => { } }, { immediate: true }); -export function addCustomEmoji(emoji: Misskey.entities.EmojiSimple) { - customEmojis.value = [emoji, ...customEmojis.value]; - set('emojis', customEmojis.value); -} +// TODO: ここら辺副作用なのでいい感じにする +const stream = useStream(); -export function updateCustomEmojis(emojis: Misskey.entities.EmojiSimple[]) { - customEmojis.value = customEmojis.value.map(item => emojis.find(search => search.name === item.name) ?? item); +stream.on('emojiAdded', emojiData => { + customEmojis.value = [emojiData.emoji, ...customEmojis.value]; set('emojis', customEmojis.value); -} +}); -export function removeCustomEmojis(emojis: Misskey.entities.EmojiSimple[]) { - customEmojis.value = customEmojis.value.filter(item => !emojis.some(search => search.name === item.name)); +stream.on('emojiUpdated', emojiData => { + customEmojis.value = customEmojis.value.map(item => emojiData.emojis.find(search => search.name === item.name) as Misskey.entities.EmojiSimple ?? item); set('emojis', customEmojis.value); -} +}); + +stream.on('emojiDeleted', emojiData => { + customEmojis.value = customEmojis.value.filter(item => !emojiData.emojis.some(search => search.name === item.name)); + set('emojis', customEmojis.value); +}); export async function fetchCustomEmojis(force = false) { const now = Date.now(); diff --git a/packages/frontend/src/directives/follow-append.ts b/packages/frontend/src/directives/follow-append.ts index 615dd99fa8..f200f242ed 100644 --- a/packages/frontend/src/directives/follow-append.ts +++ b/packages/frontend/src/directives/follow-append.ts @@ -4,7 +4,7 @@ */ import { Directive } from 'vue'; -import { getScrollContainer, getScrollPosition } from '@@/js/scroll.js'; +import { getScrollContainer, getScrollPosition } from '@/scripts/scroll.js'; export default { mounted(src, binding, vn) { diff --git a/packages/frontend/src/directives/hotkey.ts b/packages/frontend/src/directives/hotkey.ts index 0e5c7ede24..b082b6edf2 100644 --- a/packages/frontend/src/directives/hotkey.ts +++ b/packages/frontend/src/directives/hotkey.ts @@ -4,7 +4,7 @@ */ import { Directive } from 'vue'; -import { makeHotkey } from '@/scripts/hotkey.js'; +import { makeHotkey } from '../scripts/hotkey.js'; export default { mounted(el, binding) { @@ -13,9 +13,9 @@ export default { el._keyHandler = makeHotkey(binding.value); if (el._hotkey_global) { - document.addEventListener('keydown', el._keyHandler, { passive: false }); + document.addEventListener('keydown', el._keyHandler); } else { - el.addEventListener('keydown', el._keyHandler, { passive: false }); + el.addEventListener('keydown', el._keyHandler); } }, diff --git a/packages/frontend/src/directives/ripple.ts b/packages/frontend/src/directives/ripple.ts index a043ff212d..2d724f771e 100644 --- a/packages/frontend/src/directives/ripple.ts +++ b/packages/frontend/src/directives/ripple.ts @@ -17,9 +17,7 @@ export default { const x = rect.left + (el.offsetWidth / 2); const y = rect.top + (el.offsetHeight / 2); - const { dispose } = popup(MkRippleEffect, { x, y }, { - end: () => dispose(), - }); + popup(MkRippleEffect, { x, y }, {}, 'end'); }); }, }; diff --git a/packages/frontend/src/directives/tooltip.ts b/packages/frontend/src/directives/tooltip.ts index 251ce5675f..b1c1b19907 100644 --- a/packages/frontend/src/directives/tooltip.ts +++ b/packages/frontend/src/directives/tooltip.ts @@ -51,15 +51,13 @@ export default { if (self.text == null) return; const showing = ref(true); - const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkTooltip.vue')), { + popup(defineAsyncComponent(() => import('@/components/MkTooltip.vue')), { showing, text: self.text, asMfm: binding.modifiers.mfm, direction: binding.modifiers.left ? 'left' : binding.modifiers.right ? 'right' : binding.modifiers.top ? 'top' : binding.modifiers.bottom ? 'bottom' : 'top', targetElement: el, - }, { - closed: () => dispose(), - }); + }, {}, 'closed'); self._close = () => { showing.value = false; diff --git a/packages/frontend/src/directives/user-preview.ts b/packages/frontend/src/directives/user-preview.ts index 278d842d09..7a008a4486 100644 --- a/packages/frontend/src/directives/user-preview.ts +++ b/packages/frontend/src/directives/user-preview.ts @@ -35,7 +35,7 @@ export class UserPreview { const showing = ref(true); - const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkUserPopup.vue')), { + popup(defineAsyncComponent(() => import('@/components/MkUserPopup.vue')), { showing, q: this.user, source: this.el, @@ -47,8 +47,7 @@ export class UserPreview { window.clearTimeout(this.showTimer); this.hideTimer = window.setTimeout(this.close, 500); }, - closed: () => dispose(), - }); + }, 'closed'); this.promise = { cancel: () => { diff --git a/packages/frontend-shared/js/emojilist.json b/packages/frontend/src/emojilist.json similarity index 100% rename from packages/frontend-shared/js/emojilist.json rename to packages/frontend/src/emojilist.json diff --git a/packages/frontend/src/filters/date.ts b/packages/frontend/src/filters/date.ts index d13d1a5e42..2ffe93e868 100644 --- a/packages/frontend/src/filters/date.ts +++ b/packages/frontend/src/filters/date.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { dateTimeFormat } from '@@/js/intl-const.js'; +import { dateTimeFormat } from '@/scripts/intl-const.js'; export default (d: Date | number | undefined) => dateTimeFormat.format(d); export const dateString = (d: string) => dateTimeFormat.format(new Date(d)); diff --git a/packages/frontend/src/filters/number.ts b/packages/frontend/src/filters/number.ts index 10fb64deb4..2e7cc60ff4 100644 --- a/packages/frontend/src/filters/number.ts +++ b/packages/frontend/src/filters/number.ts @@ -3,6 +3,6 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { numberFormat } from '@@/js/intl-const.js'; +import { numberFormat } from '@/scripts/intl-const.js'; export default n => n == null ? 'N/A' : numberFormat.format(n); diff --git a/packages/frontend/src/filters/user.ts b/packages/frontend/src/filters/user.ts index d9bc316764..b713d41789 100644 --- a/packages/frontend/src/filters/user.ts +++ b/packages/frontend/src/filters/user.ts @@ -4,9 +4,9 @@ */ import * as Misskey from 'misskey-js'; -import { url } from '@@/js/config.js'; +import { url } from '@/config.js'; -export const acct = (user: Misskey.Acct) => { +export const acct = (user: misskey.Acct) => { return Misskey.acct.toString(user); }; @@ -14,6 +14,6 @@ export const userName = (user: Misskey.entities.User) => { return user.name || user.username; }; -export const userPage = (user: Misskey.Acct, path?: string, absolute = false) => { +export const userPage = (user: misskey.Acct, path?, absolute = false) => { return `${absolute ? url : ''}/@${acct(user)}${(path ? `/${path}` : '')}`; }; diff --git a/packages/frontend/src/i18n.ts b/packages/frontend/src/i18n.ts index 6ad503b089..cc9faddb20 100644 --- a/packages/frontend/src/i18n.ts +++ b/packages/frontend/src/i18n.ts @@ -4,12 +4,13 @@ */ import { markRaw } from 'vue'; -import { I18n } from '@@/js/i18n.js'; import type { Locale } from '../../../locales/index.js'; -import { locale } from '@@/js/config.js'; +import { locale } from '@/config.js'; +import { I18n } from '@/scripts/i18n.js'; -export const i18n = markRaw(new I18n(locale, _DEV_)); +export const i18n = markRaw(new I18n(locale)); export function updateI18n(newLocale: Locale) { + // @ts-expect-error -- private field i18n.locale = newLocale; } diff --git a/packages/frontend/src/index.html b/packages/frontend/src/index.html index 55d6b6cffd..733116b75f 100644 --- a/packages/frontend/src/index.html +++ b/packages/frontend/src/index.html @@ -17,12 +17,12 @@ diff --git a/packages/frontend/src/instance.ts b/packages/frontend/src/instance.ts index 71cb42b30c..6847321d6c 100644 --- a/packages/frontend/src/instance.ts +++ b/packages/frontend/src/instance.ts @@ -7,7 +7,7 @@ import { computed, reactive } from 'vue'; import * as Misskey from 'misskey-js'; import { misskeyApi } from '@/scripts/misskey-api.js'; import { miLocalStorage } from '@/local-storage.js'; -import { DEFAULT_INFO_IMAGE_URL, DEFAULT_NOT_FOUND_IMAGE_URL, DEFAULT_SERVER_ERROR_IMAGE_URL } from '@@/js/const.js'; +import { DEFAULT_INFO_IMAGE_URL, DEFAULT_NOT_FOUND_IMAGE_URL, DEFAULT_SERVER_ERROR_IMAGE_URL } from '@/const.js'; // TODO: 他のタブと永続化されたstateを同期 diff --git a/packages/frontend/src/local-storage.ts b/packages/frontend/src/local-storage.ts index 89c0a4b849..2b2f59edb3 100644 --- a/packages/frontend/src/local-storage.ts +++ b/packages/frontend/src/local-storage.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -export type Keys = +type Keys = 'v' | 'lastVersion' | 'instance' | @@ -39,22 +39,12 @@ export type Keys = `aiscript:${string}` | 'lastEmojisFetchedAt' | // DEPRECATED, stored in indexeddb (13.9.0~) 'emojis' | // DEPRECATED, stored in indexeddb (13.9.0~); - `channelLastReadedAt:${string}` | - `idbfallback::${string}` - -// セッション毎に廃棄されるLocalStorage代替(セーフモードなどで使用できそう) -//const safeSessionStorage = new Map(); + `channelLastReadedAt:${string}` export const miLocalStorage = { - getItem: (key: Keys): string | null => { - return window.localStorage.getItem(key); - }, - setItem: (key: Keys, value: string): void => { - window.localStorage.setItem(key, value); - }, - removeItem: (key: Keys): void => { - window.localStorage.removeItem(key); - }, + getItem: (key: Keys): string | null => window.localStorage.getItem(key), + setItem: (key: Keys, value: string): void => window.localStorage.setItem(key, value), + removeItem: (key: Keys): void => window.localStorage.removeItem(key), getItemAsJson: (key: Keys): any | undefined => { const item = miLocalStorage.getItem(key); if (item === null) { @@ -62,7 +52,5 @@ export const miLocalStorage = { } return JSON.parse(item); }, - setItemAsJson: (key: Keys, value: any): void => { - miLocalStorage.setItem(key, JSON.stringify(value)); - }, + setItemAsJson: (key: Keys, value: any): void => window.localStorage.setItem(key, JSON.stringify(value)), }; diff --git a/packages/frontend/src/navbar.ts b/packages/frontend/src/navbar.ts index 6f236dc89e..ccd7034968 100644 --- a/packages/frontend/src/navbar.ts +++ b/packages/frontend/src/navbar.ts @@ -12,7 +12,7 @@ import { openInstanceMenu } from '@/ui/_common_/common.js'; import { lookup } from '@/scripts/lookup.js'; import * as os from '@/os.js'; import { i18n } from '@/i18n.js'; -import { ui } from '@@/js/config.js'; +import { ui } from '@/config.js'; import { unisonReload } from '@/scripts/unison-reload.js'; export const navbarItemDef = reactive({ @@ -41,7 +41,7 @@ export const navbarItemDef = reactive({ followRequests: { title: i18n.ts.followRequests, icon: 'ti ti-user-plus', - show: computed(() => $i != null && ($i.isLocked || $i.hasPendingReceivedFollowRequest || $i.hasPendingSentFollowRequest)), + show: computed(() => $i != null && $i.isLocked), indicated: computed(() => $i != null && $i.hasPendingReceivedFollowRequest), to: '/my/follow-requests', }, @@ -68,12 +68,6 @@ export const navbarItemDef = reactive({ lookup(); }, }, - following: { - title: i18n.ts.following, - icon: 'ph-user-check ph-bold ph-lg', - show: computed(() => $i != null && !$i.movedTo), - to: '/following-feed', - }, lists: { title: i18n.ts.lists, icon: 'ti ti-list', @@ -132,7 +126,7 @@ export const navbarItemDef = reactive({ ui: { title: i18n.ts.switchUi, icon: 'ti ti-devices', - action: (ev: MouseEvent) => { + action: (ev) => { os.popupMenu([{ text: i18n.ts.default, active: ui === 'default' || ui === null, diff --git a/packages/frontend/src/nirax.ts b/packages/frontend/src/nirax.ts index 25f853453a..6a8ea09ed6 100644 --- a/packages/frontend/src/nirax.ts +++ b/packages/frontend/src/nirax.ts @@ -7,14 +7,7 @@ import { Component, onMounted, shallowRef, ShallowRef } from 'vue'; import { EventEmitter } from 'eventemitter3'; - -function safeURIDecode(str: string): string { - try { - return decodeURIComponent(str); - } catch { - return str; - } -} +import { safeURIDecode } from '@/scripts/safe-uri-decode.js'; interface RouteDefBase { path: string; diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts index e73aa77a3c..6adf2e590b 100644 --- a/packages/frontend/src/os.ts +++ b/packages/frontend/src/os.ts @@ -5,13 +5,12 @@ // TODO: なんでもかんでもos.tsに突っ込むのやめたいのでよしなに分割する -import { Component, markRaw, Ref, ref, defineAsyncComponent, nextTick } from 'vue'; +import { Component, markRaw, Ref, ref, defineAsyncComponent } from 'vue'; import { EventEmitter } from 'eventemitter3'; import * as Misskey from 'misskey-js'; import type { ComponentProps as CP } from 'vue-component-type-helpers'; import type { Form, GetFormResultType } from '@/scripts/form.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; -import { defaultStore } from '@/store.js'; import { i18n } from '@/i18n.js'; import MkPostFormDialog from '@/components/MkPostFormDialog.vue'; import MkWaitingDialog from '@/components/MkWaitingDialog.vue'; @@ -22,12 +21,9 @@ import MkPasswordDialog from '@/components/MkPasswordDialog.vue'; import MkEmojiPickerDialog from '@/components/MkEmojiPickerDialog.vue'; import MkPopupMenu from '@/components/MkPopupMenu.vue'; import MkContextMenu from '@/components/MkContextMenu.vue'; -import type { MenuItem } from '@/types/menu.js'; -import { copyToClipboard } from '@/scripts/copy-to-clipboard.js'; -import { pleaseLogin } from '@/scripts/please-login.js'; +import { MenuItem } from '@/types/menu.js'; +import copyToClipboard from '@/scripts/copy-to-clipboard.js'; import { showMovedDialog } from '@/scripts/show-moved-dialog.js'; -import { getHTMLElementOrNull } from '@/scripts/get-dom-node-or-null.js'; -import { focusParent } from '@/scripts/focus.js'; export const openingWindowsCount = ref(0); @@ -127,13 +123,11 @@ export function promiseDialog>( }); // NOTE: dynamic importすると挙動がおかしくなる(showingの変更が伝播しない) - const { dispose } = popup(MkWaitingDialog, { + popup(MkWaitingDialog, { success: success, showing: showing, text: text, - }, { - closed: () => dispose(), - }); + }, {}, 'closed'); return promise; } @@ -179,24 +173,28 @@ type EmitsExtractor = { [K in keyof T as K extends `onVnode${string}` ? never : K extends `on${infer E}` ? Uncapitalize : K extends string ? never : K]: T[K]; }; -export function popup( +export async function popup( component: T, props: ComponentProps, events: ComponentEmit = {} as ComponentEmit, -): { dispose: () => void } { + disposeEvent?: keyof ComponentEmit, +): Promise<{ dispose: () => void }> { markRaw(component); const id = ++popupIdCount; const dispose = () => { // このsetTimeoutが無いと挙動がおかしくなる(autocompleteが閉じなくなる)。Vueのバグ? window.setTimeout(() => { - popups.value = popups.value.filter(p => p.id !== id); + popups.value = popups.value.filter(popup => popup.id !== id); }, 0); }; const state = { component, props, - events, + events: disposeEvent ? { + ...events, + [disposeEvent]: dispose, + } : events, id, }; @@ -208,20 +206,16 @@ export function popup( } export function pageWindow(path: string) { - const { dispose } = popup(MkPageWindow, { + popup(MkPageWindow, { initialPath: path, - }, { - closed: () => dispose(), - }); + }, {}, 'closed'); } export function toast(message: string, renderMfm = false) { - const { dispose } = popup(MkToast, { + popup(MkToast, { message, renderMfm, - }, { - closed: () => dispose(), - }); + }, {}, 'closed'); } export function alert(props: { @@ -230,12 +224,11 @@ export function alert(props: { text?: string; }): Promise { return new Promise(resolve => { - const { dispose } = popup(MkDialog, props, { + popup(MkDialog, props, { done: () => { resolve(); }, - closed: () => dispose(), - }); + }, 'closed'); }); } @@ -245,18 +238,16 @@ export function confirm(props: { text?: string; okText?: string; cancelText?: string; - plain?: boolean; }): Promise<{ canceled: boolean }> { return new Promise(resolve => { - const { dispose } = popup(MkDialog, { + popup(MkDialog, { ...props, showCancelButton: true, }, { done: result => { resolve(result ? result : { canceled: true }); }, - closed: () => dispose(), - }); + }, 'closed'); }); } @@ -278,7 +269,7 @@ export function actions { return new Promise(resolve => { - const { dispose } = popup(MkDialog, { + popup(MkDialog, { ...props, actions: props.actions.map(a => ({ text: a.text, @@ -292,8 +283,7 @@ export function actions { resolve(result ? result : { canceled: true }); }, - closed: () => dispose(), - }); + }, 'closed'); }); } @@ -341,7 +331,7 @@ export function inputText(props: { canceled: false; result: string | null; }> { return new Promise(resolve => { - const { dispose } = popup(MkDialog, { + popup(MkDialog, { title: props.title, text: props.text, input: { @@ -356,8 +346,7 @@ export function inputText(props: { done: result => { resolve(result ? result : { canceled: true }); }, - closed: () => dispose(), - }); + }, 'closed'); }); } @@ -396,7 +385,7 @@ export function inputNumber(props: { canceled: false; result: number | null; }> { return new Promise(resolve => { - const { dispose } = popup(MkDialog, { + popup(MkDialog, { title: props.title, text: props.text, input: { @@ -409,8 +398,7 @@ export function inputNumber(props: { done: result => { resolve(result ? result : { canceled: true }); }, - closed: () => dispose(), - }); + }, 'closed'); }); } @@ -425,7 +413,7 @@ export function inputDate(props: { canceled: false; result: Date; }> { return new Promise(resolve => { - const { dispose } = popup(MkDialog, { + popup(MkDialog, { title: props.title, text: props.text, input: { @@ -437,8 +425,7 @@ export function inputDate(props: { done: result => { resolve(result ? { result: new Date(result.result), canceled: false } : { result: undefined, canceled: true }); }, - closed: () => dispose(), - }); + }, 'closed'); }); } @@ -448,29 +435,23 @@ export function authenticateDialog(): Promise<{ canceled: false; result: { password: string; token: string | null; }; }> { return new Promise(resolve => { - const { dispose } = popup(MkPasswordDialog, {}, { + popup(MkPasswordDialog, {}, { done: result => { resolve(result ? { canceled: false, result } : { canceled: true, result: undefined }); }, - closed: () => dispose(), - }); + }, 'closed'); }); } -type SelectItem = { - value: C; - text: string; -}; - // default が指定されていたら result は null になり得ないことを保証する overload function export function select(props: { title?: string; text?: string; default: string; - items: (SelectItem | { - sectionTitle: string; - items: SelectItem[]; - } | undefined)[]; + items: { + value: C; + text: string; + }[]; }): Promise<{ canceled: true; result: undefined; } | { @@ -480,10 +461,10 @@ export function select(props: { title?: string; text?: string; default?: string | null; - items: (SelectItem | { - sectionTitle: string; - items: SelectItem[]; - } | undefined)[]; + items: { + value: C; + text: string; + }[]; }): Promise<{ canceled: true; result: undefined; } | { @@ -493,29 +474,28 @@ export function select(props: { title?: string; text?: string; default?: string | null; - items: (SelectItem | { - sectionTitle: string; - items: SelectItem[]; - } | undefined)[]; + items: { + value: C; + text: string; + }[]; }): Promise<{ canceled: true; result: undefined; } | { canceled: false; result: C | null; }> { return new Promise(resolve => { - const { dispose } = popup(MkDialog, { + popup(MkDialog, { title: props.title, text: props.text, select: { - items: props.items.filter(x => x !== undefined), + items: props.items, default: props.default ?? null, }, }, { done: result => { resolve(result ? result : { canceled: true }); }, - closed: () => dispose(), - }); + }, 'closed'); }); } @@ -525,57 +505,53 @@ export function success(): Promise { window.setTimeout(() => { showing.value = false; }, 1000); - const { dispose } = popup(MkWaitingDialog, { + popup(MkWaitingDialog, { success: true, showing: showing, }, { done: () => resolve(), - closed: () => dispose(), - }); + }, 'closed'); }); } export function waiting(): Promise { return new Promise(resolve => { const showing = ref(true); - const { dispose } = popup(MkWaitingDialog, { + popup(MkWaitingDialog, { success: false, showing: showing, }, { done: () => resolve(), - closed: () => dispose(), - }); + }, 'closed'); }); } export function form(title: string, f: F): Promise<{ canceled: true, result?: undefined } | { canceled?: false, result: GetFormResultType }> { return new Promise(resolve => { - const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkFormDialog.vue')), { title, form: f }, { + popup(defineAsyncComponent(() => import('@/components/MkFormDialog.vue')), { title, form: f }, { done: result => { resolve(result); }, - closed: () => dispose(), - }); + }, 'closed'); }); } export async function selectUser(opts: { includeSelf?: boolean; localOnly?: boolean; } = {}): Promise { return new Promise(resolve => { - const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkUserSelectDialog.vue')), { + popup(defineAsyncComponent(() => import('@/components/MkUserSelectDialog.vue')), { includeSelf: opts.includeSelf, localOnly: opts.localOnly, }, { ok: user => { resolve(user); }, - closed: () => dispose(), - }); + }, 'closed'); }); } export async function selectDriveFile(multiple: boolean): Promise { return new Promise(resolve => { - const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkDriveSelectDialog.vue')), { + popup(defineAsyncComponent(() => import('@/components/MkDriveSelectDialog.vue')), { type: 'file', multiple, }, { @@ -584,14 +560,13 @@ export async function selectDriveFile(multiple: boolean): Promise dispose(), - }); + }, 'closed'); }); } export async function selectDriveFolder(multiple: boolean): Promise { return new Promise(resolve => { - const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkDriveSelectDialog.vue')), { + popup(defineAsyncComponent(() => import('@/components/MkDriveSelectDialog.vue')), { type: 'folder', multiple, }, { @@ -600,22 +575,20 @@ export async function selectDriveFolder(multiple: boolean): Promise dispose(), - }); + }, 'closed'); }); } export async function pickEmoji(src: HTMLElement, opts: ComponentProps): Promise { return new Promise(resolve => { - const { dispose } = popup(MkEmojiPickerDialog, { + popup(MkEmojiPickerDialog, { src, ...opts, }, { done: emoji => { resolve(emoji); }, - closed: () => dispose(), - }); + }, 'closed'); }); } @@ -624,7 +597,7 @@ export async function cropImage(image: Misskey.entities.DriveFile, options: { uploadFolder?: string | null; }): Promise { return new Promise(resolve => { - const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkCropperDialog.vue')), { + popup(defineAsyncComponent(() => import('@/components/MkCropperDialog.vue')), { file: image, aspectRatio: options.aspectRatio, uploadFolder: options.uploadFolder, @@ -632,88 +605,73 @@ export async function cropImage(image: Misskey.entities.DriveFile, options: { ok: x => { resolve(x); }, - closed: () => dispose(), - }); + }, 'closed'); }); } export function popupMenu(items: MenuItem[], src?: HTMLElement | EventTarget | null, options?: { align?: string; width?: number; + viaKeyboard?: boolean; onClosing?: () => void; }): Promise { - let returnFocusTo = getHTMLElementOrNull(src) ?? getHTMLElementOrNull(document.activeElement); - return new Promise(resolve => nextTick(() => { - const { dispose } = popup(MkPopupMenu, { + return new Promise(resolve => { + let dispose; + popup(MkPopupMenu, { items, src, width: options?.width, align: options?.align, - returnFocusTo, + viaKeyboard: options?.viaKeyboard, }, { closed: () => { resolve(); dispose(); - returnFocusTo = null; }, closing: () => { - options?.onClosing?.(); + if (options?.onClosing) options.onClosing(); }, + }).then(res => { + dispose = res.dispose; }); - })); + }); } export function contextMenu(items: MenuItem[], ev: MouseEvent): Promise { - if ( - defaultStore.state.contextMenu === 'native' || - (defaultStore.state.contextMenu === 'appWithShift' && !ev.shiftKey) - ) { - return Promise.resolve(); - } - - let returnFocusTo = getHTMLElementOrNull(ev.currentTarget ?? ev.target) ?? getHTMLElementOrNull(document.activeElement); ev.preventDefault(); - return new Promise(resolve => nextTick(() => { - const { dispose } = popup(MkContextMenu, { + return new Promise(resolve => { + let dispose; + popup(MkContextMenu, { items, ev, }, { closed: () => { resolve(); dispose(); - - // MkModalを通していないのでここでフォーカスを戻す処理を行う - if (returnFocusTo != null) { - focusParent(returnFocusTo, true, false); - returnFocusTo = null; - } }, + }).then(res => { + dispose = res.dispose; }); - })); + }); } -export function post(props: Record = {}): Promise { - pleaseLogin(undefined, (props.initialText || props.initialNote ? { - type: 'share', - params: { - text: props.initialText ?? props.initialNote.text, - visibility: props.initialVisibility ?? props.initialNote?.visibility, - localOnly: (props.initialLocalOnly || props.initialNote?.localOnly) ? '1' : '0', - }, - } : undefined)); - +export function post(props: Record = {}): Promise { showMovedDialog(); + return new Promise(resolve => { // NOTE: MkPostFormDialogをdynamic importするとiOSでテキストエリアに自動フォーカスできない // NOTE: ただ、dynamic importしない場合、MkPostFormDialogインスタンスが使いまわされ、 // Vueが渡されたコンポーネントに内部的に__propsというプロパティを生やす影響で、 // 複数のpost formを開いたときに場合によってはエラーになる // もちろん複数のpost formを開けること自体Misskeyサイドのバグなのだが - const { dispose } = popup(MkPostFormDialog, props, { - closed: (cancelled) => { - resolve(cancelled); + let dispose; + popup(MkPostFormDialog, props, { + closed: () => { + resolve(); dispose(); }, + }).then(res => { + dispose = res.dispose; }); }); } diff --git a/packages/frontend/src/pages/_error_.vue b/packages/frontend/src/pages/_error_.vue index 83f596b870..dbc113a766 100644 --- a/packages/frontend/src/pages/_error_.vue +++ b/packages/frontend/src/pages/_error_.vue @@ -29,7 +29,7 @@ import { ref, computed } from 'vue'; import * as Misskey from 'misskey-js'; import MkButton from '@/components/MkButton.vue'; import MkLink from '@/components/MkLink.vue'; -import { version } from '@@/js/config.js'; +import { version } from '@/config.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; import { unisonReload } from '@/scripts/unison-reload.js'; import { i18n } from '@/i18n.js'; diff --git a/packages/frontend/src/pages/about-sharkey.vue b/packages/frontend/src/pages/about-sharkey.vue index 7ebae62a7b..38ef475743 100644 --- a/packages/frontend/src/pages/about-sharkey.vue +++ b/packages/frontend/src/pages/about-sharkey.vue @@ -53,10 +53,10 @@ SPDX-License-Identifier: AGPL-3.0-only {{ i18n.ts._aboutMisskey.source }} ({{ i18n.ts._aboutMisskey.original_sharkey }}) - + {{ i18n.ts._aboutMisskey.donate_sharkey }} - +
@@ -77,10 +77,18 @@ SPDX-License-Identifier: AGPL-3.0-only @@ -119,9 +119,9 @@ SPDX-License-Identifier: AGPL-3.0-only @lucent - - - @privateger + + + @karilaa @@ -170,9 +170,9 @@ SPDX-License-Identifier: AGPL-3.0-only :key="sponsor" style="margin-bottom: 0.5rem;" > - - - {{ sponsor.name }} + + + {{ sponsor.details.name }}
@@ -185,7 +185,7 @@ SPDX-License-Identifier: AGPL-3.0-only - - diff --git a/packages/frontend/src/pages/about.vue b/packages/frontend/src/pages/about.vue index f35cbe8d5a..11823cc799 100644 --- a/packages/frontend/src/pages/about.vue +++ b/packages/frontend/src/pages/about.vue @@ -8,7 +8,113 @@ SPDX-License-Identifier: AGPL-3.0-only - +
+
+
+ +
+ {{ instance.name ?? host }} +
+
+
+ + + + + + + +
+ + + + +
+
+ + + {{ i18n.ts.aboutMisskey }} + + + + {{ i18n.ts.sourceCode }} + + + {{ i18n.ts.sourceCodeIsNotYetProvided }} + +
+
+ + +
+ + + + + + + + + + + + + {{ i18n.ts.impressum }} + +
+ + + +
    +
  1. +
+
+ + + {{ i18n.ts.termsOfService }} + + + + {{ i18n.ts.privacyPolicy }} + + + + {{ i18n.ts.feedback }} + +
+
+
+ + + + + + + + + + + + + + + + + + + +
+ host-meta + host-meta.json + nodeinfo + robots.txt + manifest.json +
+
+
@@ -24,16 +130,27 @@ SPDX-License-Identifier: AGPL-3.0-only + + diff --git a/packages/frontend/src/pages/admin-file.vue b/packages/frontend/src/pages/admin-file.vue index 60f6be51d4..d8311186ab 100644 --- a/packages/frontend/src/pages/admin-file.vue +++ b/packages/frontend/src/pages/admin-file.vue @@ -44,9 +44,6 @@ SPDX-License-Identifier: AGPL-3.0-only {{ i18n.ts.delete }}
-
- -
{{ i18n.ts.requireAdminForView }} @@ -70,7 +67,7 @@ SPDX-License-Identifier: AGPL-3.0-only - - diff --git a/packages/frontend/src/pages/admin/abuse-report/notification-recipient.item.vue b/packages/frontend/src/pages/admin/abuse-report/notification-recipient.item.vue deleted file mode 100644 index 0b86808faf..0000000000 --- a/packages/frontend/src/pages/admin/abuse-report/notification-recipient.item.vue +++ /dev/null @@ -1,114 +0,0 @@ - - - - - - - diff --git a/packages/frontend/src/pages/admin/abuse-report/notification-recipient.vue b/packages/frontend/src/pages/admin/abuse-report/notification-recipient.vue deleted file mode 100644 index f5249261be..0000000000 --- a/packages/frontend/src/pages/admin/abuse-report/notification-recipient.vue +++ /dev/null @@ -1,177 +0,0 @@ - - - - - - - diff --git a/packages/frontend/src/pages/admin/abuses.vue b/packages/frontend/src/pages/admin/abuses.vue index c8a9ca7112..39267b1345 100644 --- a/packages/frontend/src/pages/admin/abuses.vue +++ b/packages/frontend/src/pages/admin/abuses.vue @@ -7,33 +7,30 @@ SPDX-License-Identifier: AGPL-3.0-only -
-
- {{ i18n.ts.notificationSetting }} -
- -
- - - - - - - - - - - - - - - - - - -
- - - - - + + + +
+
@@ -61,7 +60,6 @@ import MkPagination from '@/components/MkPagination.vue'; import XAbuseReport from '@/components/MkAbuseReport.vue'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; -import MkButton from '@/components/MkButton.vue'; const reports = shallowRef>(); @@ -82,7 +80,7 @@ const pagination = { }; function resolved(reportId) { - reports.value?.removeItem(reportId); + reports.value.removeItem(reportId); } const headerActions = computed(() => []); @@ -94,26 +92,3 @@ definePageMetadata(() => ({ icon: 'ti ti-exclamation-circle', })); - - diff --git a/packages/frontend/src/pages/admin/ads.vue b/packages/frontend/src/pages/admin/ads.vue index 6c8901b10b..bd442ccc69 100644 --- a/packages/frontend/src/pages/admin/ads.vue +++ b/packages/frontend/src/pages/admin/ads.vue @@ -65,18 +65,18 @@ SPDX-License-Identifier: AGPL-3.0-only -
- +
+ {{ i18n.ts.save }} - + {{ i18n.ts.remove }}
- + {{ i18n.ts.more }} diff --git a/packages/frontend/src/pages/admin/announcements.vue b/packages/frontend/src/pages/admin/announcements.vue index fd37311b21..e7fb62ec1d 100644 --- a/packages/frontend/src/pages/admin/announcements.vue +++ b/packages/frontend/src/pages/admin/announcements.vue @@ -11,85 +11,70 @@ SPDX-License-Identifier: AGPL-3.0-only {{ i18n.ts._announcement.shouldNotBeUsedToPresentPermanentInfo }} {{ i18n.ts._announcement.tooManyActiveAnnouncementDescription }} - - - - - + + + + - - - + + + + {{ i18n.ts.more }} + diff --git a/packages/frontend/src/pages/admin/branding.vue b/packages/frontend/src/pages/admin/branding.vue index d3d52002fe..2e14aef0b9 100644 --- a/packages/frontend/src/pages/admin/branding.vue +++ b/packages/frontend/src/pages/admin/branding.vue @@ -37,15 +37,6 @@ SPDX-License-Identifier: AGPL-3.0-only - - - - - - @@ -134,10 +125,9 @@ import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import MkButton from '@/components/MkButton.vue'; import MkColorInput from '@/components/MkColorInput.vue'; -import { host } from '@@/js/config.js'; +import { host } from '@/config.js'; const iconUrl = ref(null); -const sidebarLogoUrl = ref(null); const app192IconUrl = ref(null); const app512IconUrl = ref(null); const bannerUrl = ref(null); @@ -156,7 +146,6 @@ const manifestJsonOverride = ref('{}'); async function init() { const meta = await misskeyApi('admin/meta'); iconUrl.value = meta.iconUrl; - sidebarLogoUrl.value = meta.sidebarLogoUrl; app192IconUrl.value = meta.app192IconUrl; app512IconUrl.value = meta.app512IconUrl; bannerUrl.value = meta.bannerUrl; @@ -176,7 +165,6 @@ async function init() { function save() { os.apiWithDialog('admin/update-meta', { iconUrl: iconUrl.value, - sidebarLogoUrl: sidebarLogoUrl.value, app192IconUrl: app192IconUrl.value, app512IconUrl: app512IconUrl.value, bannerUrl: bannerUrl.value, diff --git a/packages/frontend/src/pages/admin/email-settings.vue b/packages/frontend/src/pages/admin/email-settings.vue index ddfe5ae81f..4a858887f3 100644 --- a/packages/frontend/src/pages/admin/email-settings.vue +++ b/packages/frontend/src/pages/admin/email-settings.vue @@ -100,7 +100,7 @@ async function init() { async function testEmail() { const { canceled, result: destination } = await os.inputText({ - title: i18n.ts.emailDestination, + title: i18n.ts.destination, type: 'email', default: instance.maintainerEmail ?? '', placeholder: 'test@example.com', diff --git a/packages/frontend/src/pages/admin/external-services.vue b/packages/frontend/src/pages/admin/external-services.vue index 50e2c2dd51..e4308e6030 100644 --- a/packages/frontend/src/pages/admin/external-services.vue +++ b/packages/frontend/src/pages/admin/external-services.vue @@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only - +
@@ -19,7 +19,6 @@ SPDX-License-Identifier: AGPL-3.0-only - @@ -28,12 +27,17 @@ SPDX-License-Identifier: AGPL-3.0-only - - Save
-
+
+ @@ -44,12 +48,12 @@ import MkInput from '@/components/MkInput.vue'; import MkButton from '@/components/MkButton.vue'; import MkSwitch from '@/components/MkSwitch.vue'; import FormSuspense from '@/components/form/suspense.vue'; +import FormSection from '@/components/form/section.vue'; import * as os from '@/os.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; import { fetchInstance } from '@/instance.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; -import MkFolder from '@/components/MkFolder.vue'; const deeplAuthKey = ref(''); const deeplIsPro = ref(false); @@ -64,7 +68,7 @@ async function init() { deeplFreeInstance.value = meta.deeplFreeInstance; } -function save_deepl() { +function save() { os.apiWithDialog('admin/update-meta', { deeplAuthKey: deeplAuthKey.value, deeplIsPro: deeplIsPro.value, @@ -84,3 +88,10 @@ definePageMetadata(() => ({ icon: 'ph-arrow-square-out ph-bold ph-lg', })); + + diff --git a/packages/frontend/src/pages/admin/federation.vue b/packages/frontend/src/pages/admin/federation.vue index 1902c97724..141de0be34 100644 --- a/packages/frontend/src/pages/admin/federation.vue +++ b/packages/frontend/src/pages/admin/federation.vue @@ -81,9 +81,9 @@ const pagination = { sort: sort.value, host: host.value !== '' ? host.value : null, ...( - state.value === 'federating' ? { federating: true, suspended: false, blocked: false } : - state.value === 'subscribing' ? { subscribing: true, suspended: false, blocked: false } : - state.value === 'publishing' ? { publishing: true, suspended: false, blocked: false } : + state.value === 'federating' ? { federating: true } : + state.value === 'subscribing' ? { subscribing: true } : + state.value === 'publishing' ? { publishing: true } : state.value === 'suspended' ? { suspended: true } : state.value === 'blocked' ? { blocked: true } : state.value === 'silenced' ? { silenced: true } : diff --git a/packages/frontend/src/pages/admin/index.vue b/packages/frontend/src/pages/admin/index.vue index e8d123060a..c5bb2766dc 100644 --- a/packages/frontend/src/pages/admin/index.vue +++ b/packages/frontend/src/pages/admin/index.vue @@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
@@ -62,13 +61,13 @@ SPDX-License-Identifier: AGPL-3.0-only diff --git a/packages/frontend/src/pages/following-feed.vue b/packages/frontend/src/pages/following-feed.vue deleted file mode 100644 index 91f74b2cf9..0000000000 --- a/packages/frontend/src/pages/following-feed.vue +++ /dev/null @@ -1,185 +0,0 @@ - - - - - - - diff --git a/packages/frontend/src/pages/gallery/post.vue b/packages/frontend/src/pages/gallery/post.vue index 6ed119c0c4..ab0305ca8d 100644 --- a/packages/frontend/src/pages/gallery/post.vue +++ b/packages/frontend/src/pages/gallery/post.vue @@ -31,7 +31,6 @@ SPDX-License-Identifier: AGPL-3.0-only -
@@ -63,7 +62,7 @@ SPDX-License-Identifier: AGPL-3.0-only - - diff --git a/packages/frontend/src/pages/instance-info.vue b/packages/frontend/src/pages/instance-info.vue index a5e6e5ac33..a6bc3e7138 100644 --- a/packages/frontend/src/pages/instance-info.vue +++ b/packages/frontend/src/pages/instance-info.vue @@ -43,19 +43,11 @@ SPDX-License-Identifier: AGPL-3.0-only {{ i18n.ts._delivery._type[suspensionState] }} -
- {{ i18n.ts.deleteAllFiles }} - {{ i18n.ts.severAllFollowRelations }} -
- {{ i18n.ts._delivery.stop }} - {{ i18n.ts.blockedByBase }} - {{ i18n.ts.blockThisInstance }} - {{ i18n.ts.silencedByBase }} - {{ i18n.ts.silenceThisInstance }} - {{ i18n.ts.markInstanceAsNSFW }} - {{ i18n.ts.rejectReports }} - {{ i18n.ts.mediaSilencedByBase }} - {{ i18n.ts.mediaSilenceThisInstance }} + {{ i18n.ts._delivery.stop }} + {{ i18n.ts._delivery.resume }} + {{ i18n.ts.blockThisInstance }} + {{ i18n.ts.silenceThisInstance }} + Mark as NSFW Refresh metadata @@ -130,36 +122,6 @@ SPDX-License-Identifier: AGPL-3.0-only
-
- - - -
-
- - - -
@@ -172,7 +134,7 @@ SPDX-License-Identifier: AGPL-3.0-only diff --git a/packages/frontend/src/pages/my-antennas/create.vue b/packages/frontend/src/pages/my-antennas/create.vue index 2b8518747f..2d026d2fa9 100644 --- a/packages/frontend/src/pages/my-antennas/create.vue +++ b/packages/frontend/src/pages/my-antennas/create.vue @@ -4,33 +4,43 @@ SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/pages/my-antennas/edit.vue b/packages/frontend/src/pages/my-antennas/edit.vue index 9f927cd1a0..9471be8575 100644 --- a/packages/frontend/src/pages/my-antennas/edit.vue +++ b/packages/frontend/src/pages/my-antennas/edit.vue @@ -4,17 +4,15 @@ SPDX-License-Identifier: AGPL-3.0-only --> diff --git a/packages/frontend/src/components/MkAntennaEditor.vue b/packages/frontend/src/pages/my-antennas/editor.vue similarity index 64% rename from packages/frontend/src/components/MkAntennaEditor.vue rename to packages/frontend/src/pages/my-antennas/editor.vue index cb7ee3d6ca..2949bfc02c 100644 --- a/packages/frontend/src/components/MkAntennaEditor.vue +++ b/packages/frontend/src/pages/my-antennas/editor.vue @@ -41,10 +41,8 @@ SPDX-License-Identifier: AGPL-3.0-only {{ i18n.ts.withFileAntenna }}
-
- {{ i18n.ts.save }} - {{ i18n.ts.delete }} -
+ {{ i18n.ts.save }} + {{ i18n.ts.delete }}
@@ -61,53 +59,28 @@ import MkSwitch from '@/components/MkSwitch.vue'; import * as os from '@/os.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; -import { deepMerge } from '@/scripts/merge.js'; -import type { DeepPartial } from '@/scripts/merge.js'; - -type PartialAllowedAntenna = Omit & { - id?: string; - createdAt?: string; - updatedAt?: string; -}; const props = defineProps<{ - antenna?: DeepPartial; + antenna: Misskey.entities.Antenna }>(); -const initialAntenna = deepMerge(props.antenna ?? {}, { - name: '', - src: 'all', - userListId: null, - users: [], - keywords: [], - excludeKeywords: [], - excludeBots: false, - withReplies: false, - caseSensitive: false, - localOnly: false, - withFile: false, - isActive: true, - hasUnreadNote: false, - notify: false, -}); - const emit = defineEmits<{ - (ev: 'created', newAntenna: Misskey.entities.Antenna): void, - (ev: 'updated', editedAntenna: Misskey.entities.Antenna): void, + (ev: 'created'): void, + (ev: 'updated'): void, (ev: 'deleted'): void, }>(); -const name = ref(initialAntenna.name); -const src = ref(initialAntenna.src); -const userListId = ref(initialAntenna.userListId); -const users = ref(initialAntenna.users.join('\n')); -const keywords = ref(initialAntenna.keywords.map(x => x.join(' ')).join('\n')); -const excludeKeywords = ref(initialAntenna.excludeKeywords.map(x => x.join(' ')).join('\n')); -const caseSensitive = ref(initialAntenna.caseSensitive); -const localOnly = ref(initialAntenna.localOnly); -const excludeBots = ref(initialAntenna.excludeBots); -const withReplies = ref(initialAntenna.withReplies); -const withFile = ref(initialAntenna.withFile); +const name = ref(props.antenna.name); +const src = ref(props.antenna.src); +const userListId = ref(props.antenna.userListId); +const users = ref(props.antenna.users.join('\n')); +const keywords = ref(props.antenna.keywords.map(x => x.join(' ')).join('\n')); +const excludeKeywords = ref(props.antenna.excludeKeywords.map(x => x.join(' ')).join('\n')); +const caseSensitive = ref(props.antenna.caseSensitive); +const localOnly = ref(props.antenna.localOnly); +const excludeBots = ref(props.antenna.excludeBots); +const withReplies = ref(props.antenna.withReplies); +const withFile = ref(props.antenna.withFile); const userLists = ref(null); watch(() => src.value, async () => { @@ -131,26 +104,24 @@ async function saveAntenna() { excludeKeywords: excludeKeywords.value.trim().split('\n').map(x => x.trim().split(' ')), }; - if (initialAntenna.id == null) { - const res = await os.apiWithDialog('antennas/create', antennaData); - emit('created', res); + if (props.antenna.id == null) { + await os.apiWithDialog('antennas/create', antennaData); + emit('created'); } else { - const res = await os.apiWithDialog('antennas/update', { ...antennaData, antennaId: initialAntenna.id }); - emit('updated', res); + await os.apiWithDialog('antennas/update', { ...antennaData, antennaId: props.antenna.id }); + emit('updated'); } } async function deleteAntenna() { - if (initialAntenna.id == null) return; - const { canceled } = await os.confirm({ type: 'warning', - text: i18n.tsx.removeAreYouSure({ x: initialAntenna.name }), + text: i18n.tsx.removeAreYouSure({ x: props.antenna.name }), }); if (canceled) return; await misskeyApi('antennas/delete', { - antennaId: initialAntenna.id, + antennaId: props.antenna.id, }); os.success(); diff --git a/packages/frontend/src/pages/my-clips/index.vue b/packages/frontend/src/pages/my-clips/index.vue index ece998a7a5..1a0d7177fc 100644 --- a/packages/frontend/src/pages/my-clips/index.vue +++ b/packages/frontend/src/pages/my-clips/index.vue @@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only {{ i18n.ts.add }} - +
diff --git a/packages/frontend/src/pages/my-lists/list.vue b/packages/frontend/src/pages/my-lists/list.vue index 5f195693cc..7492b099ea 100644 --- a/packages/frontend/src/pages/my-lists/list.vue +++ b/packages/frontend/src/pages/my-lists/list.vue @@ -133,27 +133,22 @@ async function removeUser(item, ev) { } async function showMembershipMenu(item, ev) { - const withRepliesRef = ref(item.withReplies); - os.popupMenu([{ - type: 'switch', - text: i18n.ts.showRepliesToOthersInTimeline, - icon: 'ti ti-messages', - ref: withRepliesRef, + text: item.withReplies ? i18n.ts.hideRepliesToOthersInTimeline : i18n.ts.showRepliesToOthersInTimeline, + icon: item.withReplies ? 'ti ti-messages-off' : 'ti ti-messages', + action: async () => { + misskeyApi('users/lists/update-membership', { + listId: list.value.id, + userId: item.userId, + withReplies: !item.withReplies, + }).then(() => { + paginationEl.value.updateItem(item.id, (old) => ({ + ...old, + withReplies: !item.withReplies, + })); + }); + }, }], ev.currentTarget ?? ev.target); - - watch(withRepliesRef, withReplies => { - misskeyApi('users/lists/update-membership', { - listId: list.value!.id, - userId: item.userId, - withReplies, - }).then(() => { - paginationEl.value!.updateItem(item.id, (old) => ({ - ...old, - withReplies, - })); - }); - }); } async function deleteList() { diff --git a/packages/frontend/src/pages/notifications.vue b/packages/frontend/src/pages/notifications.vue index bd93fc8369..28f5838296 100644 --- a/packages/frontend/src/pages/notifications.vue +++ b/packages/frontend/src/pages/notifications.vue @@ -30,7 +30,7 @@ import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue'; import * as os from '@/os.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; -import { notificationTypes } from '@@/js/const.js'; +import { notificationTypes } from '@/const.js'; const tab = ref('all'); const includeTypes = ref(null); diff --git a/packages/frontend/src/pages/page-editor/common.ts b/packages/frontend/src/pages/page-editor/common.ts deleted file mode 100644 index 420c8fc967..0000000000 --- a/packages/frontend/src/pages/page-editor/common.ts +++ /dev/null @@ -1,15 +0,0 @@ -/* - * SPDX-FileCopyrightText: syuilo and misskey-project - * SPDX-License-Identifier: AGPL-3.0-only - */ - -import { i18n } from '@/i18n.js'; - -export function getPageBlockList() { - return [ - { value: 'section', text: i18n.ts._pages.blocks.section }, - { value: 'text', text: i18n.ts._pages.blocks.text }, - { value: 'image', text: i18n.ts._pages.blocks.image }, - { value: 'note', text: i18n.ts._pages.blocks.note }, - ]; -} diff --git a/packages/frontend/src/pages/page-editor/els/page-editor.el.section.vue b/packages/frontend/src/pages/page-editor/els/page-editor.el.section.vue index 0f8dc33143..47e9c08c2c 100644 --- a/packages/frontend/src/pages/page-editor/els/page-editor.el.section.vue +++ b/packages/frontend/src/pages/page-editor/els/page-editor.el.section.vue @@ -29,7 +29,6 @@ import * as os from '@/os.js'; import { i18n } from '@/i18n.js'; import { deepClone } from '@/scripts/clone.js'; import MkButton from '@/components/MkButton.vue'; -import { getPageBlockList } from '@/pages/page-editor/common.js'; const XBlocks = defineAsyncComponent(() => import('../page-editor.blocks.vue')); @@ -54,9 +53,11 @@ watch(children, () => { deep: true, }); +const getPageBlockList = inject<(any) => any>('getPageBlockList'); + async function rename() { const { canceled, result: title } = await os.inputText({ - title: i18n.ts._pages.enterSectionTitle, + title: 'Enter title', default: props.modelValue.title, }); if (canceled) return; diff --git a/packages/frontend/src/pages/page-editor/page-editor.vue b/packages/frontend/src/pages/page-editor/page-editor.vue index ac9f3e7401..80c822a204 100644 --- a/packages/frontend/src/pages/page-editor/page-editor.vue +++ b/packages/frontend/src/pages/page-editor/page-editor.vue @@ -69,7 +69,6 @@ import MkButton from '@/components/MkButton.vue'; import MkSelect from '@/components/MkSelect.vue'; import MkSwitch from '@/components/MkSwitch.vue'; import MkInput from '@/components/MkInput.vue'; -import { url } from '@@/js/config.js'; import * as os from '@/os.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; import { selectFile } from '@/scripts/select-file.js'; @@ -77,7 +76,6 @@ import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import { $i } from '@/account.js'; import { mainRouter } from '@/router/main.js'; -import { getPageBlockList } from '@/pages/page-editor/common.js'; const props = defineProps<{ initPageId?: string; @@ -102,6 +100,7 @@ const alignCenter = ref(false); const hideTitleWhenPinned = ref(false); provide('readonly', readonly.value); +provide('getPageBlockList', getPageBlockList); watch(eyeCatchingImageId, async () => { if (eyeCatchingImageId.value == null) { @@ -216,6 +215,15 @@ async function add() { content.value.push({ id, type }); } +function getPageBlockList() { + return [ + { value: 'section', text: i18n.ts._pages.blocks.section }, + { value: 'text', text: i18n.ts._pages.blocks.text }, + { value: 'image', text: i18n.ts._pages.blocks.image }, + { value: 'note', text: i18n.ts._pages.blocks.note }, + ]; +} + function setEyeCatchingImage(img) { selectFile(img.currentTarget ?? img.target, null).then(file => { eyeCatchingImageId.value = file.id; diff --git a/packages/frontend/src/pages/page.vue b/packages/frontend/src/pages/page.vue index 12b70fa64f..1e38f42890 100644 --- a/packages/frontend/src/pages/page.vue +++ b/packages/frontend/src/pages/page.vue @@ -62,10 +62,8 @@ SPDX-License-Identifier: AGPL-3.0-only {{ page.likedCount }}
- -
@@ -80,6 +78,14 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ i18n.ts.createdAt }}:
{{ i18n.ts.updatedAt }}:
+
+ {{ i18n.ts._pages.viewSource }} + +
@@ -98,13 +104,13 @@ SPDX-License-Identifier: AGPL-3.0-only diff --git a/packages/frontend/src/pages/registry.keys.vue b/packages/frontend/src/pages/registry.keys.vue index 8dcb8fa477..bac1d2bb70 100644 --- a/packages/frontend/src/pages/registry.keys.vue +++ b/packages/frontend/src/pages/registry.keys.vue @@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only {{ i18n.ts._registry.createKey }} - +
{{ key[0] }}
diff --git a/packages/frontend/src/pages/reset-password.vue b/packages/frontend/src/pages/reset-password.vue index 6d24029535..6b67a9cc87 100644 --- a/packages/frontend/src/pages/reset-password.vue +++ b/packages/frontend/src/pages/reset-password.vue @@ -44,9 +44,7 @@ async function save() { onMounted(() => { if (props.token == null) { - const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkForgotPassword.vue')), {}, { - closed: () => dispose(), - }); + os.popup(defineAsyncComponent(() => import('@/components/MkForgotPassword.vue')), {}, {}, 'closed'); mainRouter.push('/'); } }); diff --git a/packages/frontend/src/pages/reversi/game.board.vue b/packages/frontend/src/pages/reversi/game.board.vue index 54e66f6e16..175ea62411 100644 --- a/packages/frontend/src/pages/reversi/game.board.vue +++ b/packages/frontend/src/pages/reversi/game.board.vue @@ -149,9 +149,9 @@ import MkButton from '@/components/MkButton.vue'; import MkFolder from '@/components/MkFolder.vue'; import MkSwitch from '@/components/MkSwitch.vue'; import { deepClone } from '@/scripts/clone.js'; -import { useInterval } from '@@/js/use-interval.js'; +import { useInterval } from '@/scripts/use-interval.js'; import { signinRequired } from '@/account.js'; -import { url } from '@@/js/config.js'; +import { url } from '@/config.js'; import { i18n } from '@/i18n.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; import { userPage } from '@/filters/user.js'; @@ -169,7 +169,7 @@ const props = defineProps<{ const showBoardLabels = ref(false); const useAvatarAsStone = ref(true); const autoplaying = ref(false); -// eslint-disable-next-line vue/no-setup-props-reactivity-loss +// eslint-disable-next-line vue/no-setup-props-destructure const game = ref(deepClone(props.game)); const logPos = ref(game.value.logs.length); const engine = shallowRef(Reversi.Serializer.restoreGame({ diff --git a/packages/frontend/src/pages/reversi/game.setting.vue b/packages/frontend/src/pages/reversi/game.setting.vue index 08bb3cb76c..31c0003130 100644 --- a/packages/frontend/src/pages/reversi/game.setting.vue +++ b/packages/frontend/src/pages/reversi/game.setting.vue @@ -121,7 +121,7 @@ import MkRadios from '@/components/MkRadios.vue'; import MkSwitch from '@/components/MkSwitch.vue'; import MkFolder from '@/components/MkFolder.vue'; import * as os from '@/os.js'; -import type { MenuItem } from '@/types/menu.js'; +import { MenuItem } from '@/types/menu.js'; import { useRouter } from '@/router/supplier.js'; const $i = signinRequired(); diff --git a/packages/frontend/src/pages/reversi/game.vue b/packages/frontend/src/pages/reversi/game.vue index 10ea3717ab..eadc51881c 100644 --- a/packages/frontend/src/pages/reversi/game.vue +++ b/packages/frontend/src/pages/reversi/game.vue @@ -20,9 +20,8 @@ import { useStream } from '@/stream.js'; import { signinRequired } from '@/account.js'; import { useRouter } from '@/router/supplier.js'; import * as os from '@/os.js'; -import { url } from '@@/js/config.js'; import { i18n } from '@/i18n.js'; -import { useInterval } from '@@/js/use-interval.js'; +import { useInterval } from '@/scripts/use-interval.js'; const $i = signinRequired(); @@ -45,7 +44,7 @@ function start(_game: Misskey.entities.ReversiGameDetailed) { if (shareWhenStart.value) { misskeyApi('notes/create', { - text: `${i18n.ts._reversi.iStartedAGame}\n${url}/reversi/g/${props.gameId}`, + text: i18n.ts._reversi.iStartedAGame + '\n' + location.href, visibility: 'home', }); } diff --git a/packages/frontend/src/pages/reversi/index.vue b/packages/frontend/src/pages/reversi/index.vue index d823861b4a..51a03e4418 100644 --- a/packages/frontend/src/pages/reversi/index.vue +++ b/packages/frontend/src/pages/reversi/index.vue @@ -117,7 +117,7 @@ import { $i } from '@/account.js'; import MkPagination from '@/components/MkPagination.vue'; import { useRouter } from '@/router/supplier.js'; import * as os from '@/os.js'; -import { useInterval } from '@@/js/use-interval.js'; +import { useInterval } from '@/scripts/use-interval.js'; import { pleaseLogin } from '@/scripts/please-login.js'; import * as sound from '@/scripts/sound.js'; diff --git a/packages/frontend/src/pages/role.vue b/packages/frontend/src/pages/role.vue index 9ee3fd7f01..45edbc5da2 100644 --- a/packages/frontend/src/pages/role.vue +++ b/packages/frontend/src/pages/role.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only @@ -161,7 +153,6 @@ const setMaxBirthDate = () => { const profile = reactive({ name: $i.name, description: $i.description, - followedMessage: $i.followedMessage, location: $i.location, birthday: $i.birthday, listenbrainz: $i.listenbrainz, @@ -218,8 +209,6 @@ function save() { // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing description: profile.description || null, // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - followedMessage: profile.followedMessage || null, - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing location: profile.location || null, // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing birthday: profile.birthday || null, @@ -492,7 +481,6 @@ definePageMetadata(() => ({ &:hover, &:focus { opacity: .7; } - &:active { cursor: pointer; } diff --git a/packages/frontend/src/pages/settings/sounds.sound.vue b/packages/frontend/src/pages/settings/sounds.sound.vue index 81478fede5..113abd708b 100644 --- a/packages/frontend/src/pages/settings/sounds.sound.vue +++ b/packages/frontend/src/pages/settings/sounds.sound.vue @@ -9,13 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only -
- {{ i18n.ts.selectFile }} -
- {{ i18n.ts._soundSettings.driveFileError }} -
-
-
+
{{ i18n.ts.selectFile }}
{{ friendlyFileName }}
@@ -25,13 +19,13 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ i18n.ts.listen }} - {{ i18n.ts.save }} + {{ i18n.ts.save }}
- - diff --git a/packages/frontend/src/pages/settings/webhook.new.vue b/packages/frontend/src/pages/settings/webhook.new.vue index d62357caaf..5bf85e48f4 100644 --- a/packages/frontend/src/pages/settings/webhook.new.vue +++ b/packages/frontend/src/pages/settings/webhook.new.vue @@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
- +
{{ i18n.ts._webhookSettings._events.follow }} diff --git a/packages/frontend/src/pages/tag.vue b/packages/frontend/src/pages/tag.vue index 0d261b1af3..9b77392872 100644 --- a/packages/frontend/src/pages/tag.vue +++ b/packages/frontend/src/pages/tag.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only